Skip to content

Commit

Permalink
feat: Confirm build frontend (#101)
Browse files Browse the repository at this point in the history
* feat: add handler for /bot/build/is_changed endpoint

* feat: create RebuildModal component

* feat: integrate RebuildModal component into the app

* refactor: adjust buildStart function behavior

* fix: backend bug

* fix: corrected modal text
  • Loading branch information
artem-mar authored Dec 31, 2024
1 parent bdcb3ee commit ebfd4e5
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 75 deletions.
1 change: 0 additions & 1 deletion backend/chatsky_ui/services/process_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ async def check_status(self, id_: int, *args, **kwargs) -> None:
self.bot_repo_manager.commit_with_tag(process.id)
self.graph_repo_manager.commit_with_tag(process.id)
break
await asyncio.sleep(2) # TODO: ?sleep time shouldn't be constant

async def get_build_info(self, id_: int, run_manager: RunManager) -> Optional[Dict[str, Any]]:
"""Returns metadata of a specific build process identified by its unique ID.
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/api/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,16 @@ export const send_message = async (user_id: number, user_message: string) => {
throw error
}
}

export const checkBuildIsChanged = async () => {
try {
const {
data: { data },
} = await $v1.get("/bot/build/is_changed")

return data
} catch (error) {
console.log(error)
throw error
}
}
64 changes: 46 additions & 18 deletions frontend/src/components/header/BuildMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,51 @@ import ChatIcon from "../../icons/buildmenu/ChatIcon"
import PlayIcon from "../../icons/buildmenu/PlayIcon"
import StopIcon from "../../icons/buildmenu/StopIcon"
import { parseSearchParams } from "../../utils"
import { PopUpContext } from "@/contexts/popUpContext"
import { checkBuildIsChanged } from "@/api/bot"
import RebuildModal from "@/modals/RebuildModal/RebuildModal"

const BuildMenu = () => {
const { saveFlows, flows } = useContext(flowContext)
const { buildStart, buildPending, buildStatus, setBuildStatus, buildStop } =
useContext(buildContext)
const { buildStart, buildPending, buildStatus, buildStop } = useContext(buildContext)
const { chat, setChat } = useContext(chatContext)
const { runStart, runPending, runStatus, runStop, run, setRunStatus } = useContext(runContext)
const { runStart, runPending, runStatus, runStop, run } = useContext(runContext)
const [searchParams, setSearchParams] = useSearchParams()
const { openPopUp } = useContext(PopUpContext)

const handleConfirmRebuild = () => {
openPopUp(
<RebuildModal
id='rebuild'
onRebuild={async () => {
const status = await buildStart({ wait_time: 1, end_status: "success" })
if (status === "completed") {
await runStart({ end_status: "success", wait_time: 0 })
}
}}
onNewRun={async () => {
await runStart({ end_status: "success", wait_time: 0 })
}}
/>,
"rebuild"
)
}

const buttonClickHandler = async () => {
if (runStatus !== "alive" && runStatus !== "running") {
saveFlows(flows, { interface: "ui" })
setRunStatus(() => "running")
await buildStart({ wait_time: 1, end_status: "success" })
await runStart({ end_status: "success", wait_time: 0 })

const flowUpdated = await checkBuildIsChanged()

if (!flowUpdated) {
handleConfirmRebuild()
return
}

const status = await buildStart({ wait_time: 1, end_status: "success" })
if (status === "completed") {
await runStart({ end_status: "success", wait_time: 0 })
}
} else if ((runStatus === "alive" || runStatus === "running") && run) {
runStop(run?.id)
} else if (buildStatus === "running") {
Expand All @@ -35,9 +65,7 @@ const BuildMenu = () => {

return (
<div className='flex items-center justify-start gap-1.5'>
<Tooltip
content='Start build and run script process'
radius='sm'>
<Tooltip content='Start build and run script process' radius='sm'>
<button
data-testid='run-btn'
onClick={buttonClickHandler}
Expand All @@ -46,11 +74,12 @@ const BuildMenu = () => {
runStatus === "alive"
? "border-emerald-500"
: runStatus === "stopped"
? "border-border"
: runStatus === "running"
? "border-amber-600"
: "border-red-500"
)}>
? "border-border"
: runStatus === "running"
? "border-amber-600"
: "border-red-500"
)}
>
{(runPending || buildPending) && (
<div className='absolute bg-background rounded-full -bottom-1.5 -right-1.5 w-5 h-5 transition animate-fade-in'>
<Loader className='!border-danger border-2 !w-4 !h-4' />
Expand Down Expand Up @@ -100,9 +129,7 @@ const BuildMenu = () => {
)}
/>
</Button> */}
<Tooltip
content='Open the chat window'
radius='sm'>
<Tooltip content='Open the chat window' radius='sm'>
<Button
data-testid='chat-btn'
onClick={() => {
Expand All @@ -117,7 +144,8 @@ const BuildMenu = () => {
className={classNames(
"bg-background hover:bg-overlay border border-border rounded-small",
chat ? "bg-overlay border-border-darker" : ""
)}>
)}
>
<ChatIcon className='w-5 h-5' />
</Button>
</Tooltip>
Expand Down
108 changes: 59 additions & 49 deletions frontend/src/contexts/buildContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type BuildContextType = {
setBuilds: React.Dispatch<React.SetStateAction<localBuildType[]>>
buildPending: boolean
setBuildPending: React.Dispatch<React.SetStateAction<boolean>>
buildStart: (options: buildPresetType) => void
buildStart: (options: buildPresetType) => Promise<buildApiStatusType>
buildStop: () => void
buildStatus: string
setBuildStatus: React.Dispatch<React.SetStateAction<buildApiStatusType>>
Expand All @@ -36,7 +36,7 @@ export const buildContext = createContext({
setBuilds: () => {},
buildPending: false,
setBuildPending: () => {},
buildStart: () => {},
buildStart: async () => "failed",
buildStop: () => {},
buildStatus: "",
setBuildStatus: () => {},
Expand Down Expand Up @@ -79,69 +79,78 @@ export const BuildProvider = ({ children }: { children: React.ReactNode }) => {
getBuildInitial()
}, [])

const buildStart = async ({ end_status = "completed", wait_time = 0 }: buildPresetType) => {
setBuildPending(() => true)
const buildStart = async ({
end_status = "completed",
wait_time = 0,
}: buildPresetType): Promise<buildApiStatusType> => {
setBuildPending(true)
setBuildStatus("running")

try {
const start_res = await build_start({ end_status, wait_time })
const started_builds = await get_builds()
// const started_build = started_builds.find((build) => build.id === start_res.build_id)
setBuildsHandler(started_builds)

const timerId = setTimeout(async () => {
setBuild(false)
setBuildStatus("failed")
n.add({
title: "Build timeout error!",
message: "",
type: "error",
})
await build_stop(start_res.build_id)
setBuildPending(false)
return "failed"
}, 15000)

let flag = true
let timer = 0
const timerId = setInterval(() => timer++, 1000)
while (flag) {
if (timer > 15) {
setBuild(() => false)
setBuildStatus("failed")
n.add({
title: "Build timeout error!",
message: "",
type: "error",
})
await build_stop(start_res.build_id)
return (flag = false)
}
const status_res = await build_status(start_res.build_id)
const status = status_res.status.toLowerCase()
const status = status_res.status

if (status !== "running" && status !== "alive") {
flag = false
setTimeout(async () => {
const build = await get_builds()
setBuilds(() =>
build.map((build) => ({
...build,
type: "build",
}))
)
}, 1000)
if (status === "completed") {
setBuildStatus("completed")
setBuild(() => true)
n.add({
title: "Build successfully!",
message: "",
type: "success",
})
} else if (status === "failed") {
setBuildStatus("failed")
setBuild(() => false)
n.add({
title: "Build failed!",
message: "Unknown build error. Please check your script.",
type: "error",
})
}
clearTimeout(timerId)

await handleBuildCompletion(status)
return status
}
await new Promise((resolve) => setTimeout(resolve, 1000))
}
clearInterval(timerId)
} catch (error) {
console.log(error)
console.error("Build start error:", error)
return "failed"
} finally {
setBuildPending(() => false)
setBuildPending(false)
}
return "failed"
}

const handleBuildCompletion = async (status: string) => {
const builds = await get_builds()

setBuilds(builds.map((build) => ({ ...build, type: "build" })))

if (status === "completed") {
setBuildStatus("completed")
setBuild(true)
n.add({
title: "Build successfully!",
message: "",
type: "success",
})
} else if (status === "failed") {
setBuildStatus("failed")
setBuild(false)
n.add({
title: "Build failed!",
message: "Unknown build error. Please check your script.",
type: "error",
})
}
}

const buildStop = async () => {
try {
await build_stop(builds[0].id + 1)
Expand Down Expand Up @@ -173,7 +182,8 @@ export const BuildProvider = ({ children }: { children: React.ReactNode }) => {
logsPage,
setLogsPage,
setBuildsHandler,
}}>
}}
>
{children}
</buildContext.Provider>
)
Expand Down
39 changes: 32 additions & 7 deletions frontend/src/modals/LaunchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Button } from "@nextui-org/react"
import React, { useContext, useState } from "react"
import { CustomModalProps, Modal, ModalBody, ModalFooter, ModalHeader } from "./ModalComponents"
import { set_tg_token } from "@/api/flows"
import RebuildModal from "./RebuildModal/RebuildModal"
import { checkBuildIsChanged } from "@/api/bot"

type LaunchModalProps = CustomModalProps & {
title?: React.ReactNode
Expand All @@ -22,12 +24,30 @@ const LaunchModal = ({
interface_description = "Please follow the instructions below to set up your bot interface.",
actionText = "Launch",
}: LaunchModalProps) => {
const { closePopUp } = useContext(PopUpContext)
const { closePopUp, openPopUp } = useContext(PopUpContext)
const { flows, saveFlows } = useContext(flowContext)
const { buildStart, buildStatus, setBuildStatus } = useContext(buildContext)
const { runStart } = useContext(runContext)
const [token, setToken] = useState("")

const handleConfirmRebuild = () => {
openPopUp(
<RebuildModal
id='rebuild'
onRebuild={async () => {
const status = await buildStart({ wait_time: 1, end_status: "success" })
if (status === "completed") {
await runStart({ end_status: "success", wait_time: 0 })
}
}}
onNewRun={async () => {
await runStart({ end_status: "success", wait_time: 0 })
}}
/>,
"rebuild"
)
}

const onCloseHandler = () => {
closePopUp(id)
}
Expand All @@ -37,18 +57,23 @@ const LaunchModal = ({
onCloseHandler()
await saveFlows(flows, { interface: "tg" })
await set_tg_token(token)
await buildStart({ wait_time: 0, end_status: "success" })
await runStart({ end_status: "success", wait_time: 0 })
const flowUpdated = await checkBuildIsChanged()
if (!flowUpdated) {
handleConfirmRebuild()
return
}

const status = await buildStart({ wait_time: 0, end_status: "success" })
if (status === "completed") {
await runStart({ end_status: "success", wait_time: 0 })
}
} catch (error) {
console.error(error)
}
}

return (
<Modal
isOpen={true}
onClose={onCloseHandler}
size='2xl'>
<Modal isOpen={true} onClose={onCloseHandler} size='2xl'>
<ModalHeader>
<div className='text-xl font-bold'>{title}</div>
</ModalHeader>
Expand Down
Loading

0 comments on commit ebfd4e5

Please sign in to comment.