diff --git a/public/css/style.css b/public/css/style.css index b5319011..14421e6c 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -2405,6 +2405,10 @@ video { padding-top: 2rem; } +.pl-4 { + padding-left: 1rem; +} + .text-center { text-align: center; } diff --git a/src/components/CFAddWorkspaceButton/CFAddWorkspaceButton.tsx b/src/components/CFAddWorkspaceButton/CFAddWorkspaceButton.tsx index 0155c73f..70186c3d 100644 --- a/src/components/CFAddWorkspaceButton/CFAddWorkspaceButton.tsx +++ b/src/components/CFAddWorkspaceButton/CFAddWorkspaceButton.tsx @@ -1,15 +1,17 @@ import React, { ReactElement } from "react"; import CreateRobotFormAddButton from "../CreateRobotFormAddButton/CreateRobotFormAddButton"; import useCreateRobot from "../../hooks/useCreateRobot"; -import { IRobotWorkspaces } from "../../interfaces/robotInterfaces"; +import { IWorkspaces } from "../../interfaces/robotInterfaces"; import { FormikProps } from "formik/dist/types"; interface ICFAddWorkspaceButton { - formik: FormikProps<IRobotWorkspaces>; + formik: FormikProps<IWorkspaces>; + disabled?: boolean; } export default function CFAddWorkspaceButton({ formik, + disabled, }: ICFAddWorkspaceButton): ReactElement { const { handleAddWorkspaceStep } = useCreateRobot(); @@ -17,6 +19,7 @@ export default function CFAddWorkspaceButton({ <div data-tut="create-robot-step2-workspace-add-button"> <CreateRobotFormAddButton onClick={() => handleAddWorkspaceStep(formik)} + disabled={disabled} /> </div> ); diff --git a/src/components/CFGrantDirTags/CFGrantDirTags.tsx b/src/components/CFGrantDirTags/CFGrantDirTags.tsx index 9c72aa1a..bbb30b3b 100644 --- a/src/components/CFGrantDirTags/CFGrantDirTags.tsx +++ b/src/components/CFGrantDirTags/CFGrantDirTags.tsx @@ -1,9 +1,9 @@ -import React, { ReactElement, useEffect, useState } from "react"; import { IDetails } from "../../interfaces/robotInterfaces"; +import { ReactElement, useEffect, useState } from "react"; import useCreateRobot from "../../hooks/useCreateRobot"; import { TagsInput } from "react-tag-input-component"; import { FormikProps } from "formik/dist/types"; -import InfoTip from "../InfoTip/InfoTip"; +import CFInfoBar from "../CFInfoBar/CFInfoBar"; import { toast } from "sonner"; interface ICFGrantDirTag { @@ -37,38 +37,35 @@ export default function CFGrantDirTag({ }, [selected]); return ( - <div> - <div className="flex min-w-fit gap-1 pb-3 text-xs font-medium text-layer-light-700"> - Granted Directories: - <InfoTip content="Here you can specify the directories that the user needs to access." /> - </div> - - <div> - <TagsInput - value={selected} - onChange={setSelected} - name="Granted directories" - classNames={{ - input: "!text-xs disabled:cursor-not-allowed", - tag: "!text-xs !bg-layer-light-50 border border-layer-light-200", - }} - placeHolder="enter a path" - beforeAddValidate={(tag) => { - if (tag.charAt(0) !== "/") { - toast.error("Path must start with '/'"); - return false; - } - return true; - }} - onRemoved={(tag) => { - if (tag === "/home/robolaunch") { - toast.error("Cannot remove this path"); - return false; - } - }} - disabled={disabled} - /> - </div> - </div> + <CFInfoBar + label="Granted Directories:" + tip="Here you can specify the directories that the user needs to access." + vertical + > + <TagsInput + value={selected} + onChange={setSelected} + name="Granted directories" + classNames={{ + input: "!text-xs disabled:cursor-not-allowed", + tag: "!text-xs !bg-layer-light-50 border border-layer-light-200", + }} + placeHolder="enter a path" + beforeAddValidate={(tag) => { + if (tag.charAt(0) !== "/") { + toast.error("Path must start with '/'"); + return false; + } + return true; + }} + onRemoved={(tag) => { + if (tag === "/home/robolaunch") { + toast.error("Cannot remove this path"); + return false; + } + }} + disabled={disabled} + /> + </CFInfoBar> ); } diff --git a/src/components/CFHostDirectories/CFHostDirectories.tsx b/src/components/CFHostDirectories/CFHostDirectories.tsx index 38638ee7..b61684bd 100644 --- a/src/components/CFHostDirectories/CFHostDirectories.tsx +++ b/src/components/CFHostDirectories/CFHostDirectories.tsx @@ -1,9 +1,9 @@ -import { ReactElement } from "react"; -import CFInfoBar from "../CFInfoBar/CFInfoBar"; import CreateRobotFormAddButton from "../CreateRobotFormAddButton/CreateRobotFormAddButton"; -import { FormikProps } from "formik/dist/types"; -import { IDetails } from "../../interfaces/robotInterfaces"; import CFHostDirectoriesInput from "../CFHostDirectoriesInput/CFHostDirectoriesInput"; +import { IDetails } from "../../interfaces/robotInterfaces"; +import { FormikProps } from "formik/dist/types"; +import CFInfoBar from "../CFInfoBar/CFInfoBar"; +import { ReactElement } from "react"; interface ICFHostDirectories { formik: FormikProps<IDetails>; @@ -46,6 +46,7 @@ export default function CFHostDirectories({ ]); }} className="!mt-1" + disabled={disabled} /> </div> ); diff --git a/src/components/CFInfoBar/CFInfoBar.tsx b/src/components/CFInfoBar/CFInfoBar.tsx index 2895b2d6..e5547aa6 100644 --- a/src/components/CFInfoBar/CFInfoBar.tsx +++ b/src/components/CFInfoBar/CFInfoBar.tsx @@ -1,6 +1,6 @@ import InputError from "../InputError/InputError"; -import React, { ReactElement } from "react"; import InfoTip from "../InfoTip/InfoTip"; +import { ReactElement } from "react"; interface ICFInfoBar { label: string; @@ -27,14 +27,14 @@ export default function CFInfoBar({ }: ICFInfoBar): ReactElement { return ( <div className={`${classNameContainer} transition-300`} data-tut={dataTut}> - <div className={`flex ${vertical && "flex-col"} `}> - <div className="flex min-w-fit gap-1 pb-3 text-xs font-medium text-layer-light-700"> + <div className={`flex ${vertical && "flex-col gap-2"}`}> + <div className="flex min-w-fit gap-1 text-xs font-medium text-layer-light-700"> {label} <InfoTip content={tip} rightTip={rightTip} /> </div> {children} </div> - <InputError error={error} touched={touched} /> + {error && touched && <InputError error={error} />} </div> ); } diff --git a/src/components/CFInputToggle/CFInputToggle.tsx b/src/components/CFInputToggle/CFInputToggle.tsx index 88946240..d9fceab8 100644 --- a/src/components/CFInputToggle/CFInputToggle.tsx +++ b/src/components/CFInputToggle/CFInputToggle.tsx @@ -20,7 +20,7 @@ export default function CFInputToggle({ checked, }: ICFInputToggle): ReactElement { return ( - <div data-tut={dataTut} className="flex items-center gap-1 pb-3"> + <div data-tut={dataTut} className="flex items-center gap-1"> <div className="flex min-w-fit gap-1 text-xs font-medium text-layer-light-700"> {labelName} <InfoTip content={labelInfoTip} /> diff --git a/src/components/CFPersistDirTags/CFPersistDirTags.tsx b/src/components/CFPersistDirTags/CFPersistDirTags.tsx index ec51852f..57f8b71c 100644 --- a/src/components/CFPersistDirTags/CFPersistDirTags.tsx +++ b/src/components/CFPersistDirTags/CFPersistDirTags.tsx @@ -1,5 +1,5 @@ -import React, { ReactElement, useEffect, useState } from "react"; import { IDetails } from "../../interfaces/robotInterfaces"; +import { ReactElement, useEffect, useState } from "react"; import useCreateRobot from "../../hooks/useCreateRobot"; import { TagsInput } from "react-tag-input-component"; import { FormikProps } from "formik/dist/types"; diff --git a/src/components/CFRobotButtons/CFRobotButtons.tsx b/src/components/CFRobotButtons/CFRobotButtons.tsx new file mode 100644 index 00000000..30eb015e --- /dev/null +++ b/src/components/CFRobotButtons/CFRobotButtons.tsx @@ -0,0 +1,86 @@ +import CFCancelButton from "../CFCancelButton/CFCancelButton"; +import useCreateRobot from "../../hooks/useCreateRobot"; +import { envApplication } from "../../helpers/envProvider"; +import { useParams } from "react-router-dom"; +import Button from "../Button/Button"; +import { ReactElement } from "react"; + +interface ICFRobotButtons { + step: 1 | 2 | 3 | 4; + formik: any; + isImportRobot?: boolean; +} + +export default function CFRobotButtons({ + step, + formik, + isImportRobot, +}: ICFRobotButtons): ReactElement { + const url = useParams(); + + const { robotData } = useCreateRobot(); + + function handleDisabled(): boolean { + switch (step) { + case 1: + return ( + !formik.isValid || + formik.isSubmitting || + JSON.stringify(formik.initialValues) === JSON.stringify(formik.values) + ); + + case 2: + return !!( + !formik?.isValid || + formik.isSubmitting || + JSON.stringify(formik.initialValues) === + JSON.stringify(formik.values) || + (envApplication && url?.robotName) + ); + + case 3: + return ( + !formik?.isValid || + formik.isSubmitting || + JSON.stringify(formik.initialValues) === JSON.stringify(formik.values) + ); + case 4: + return !formik?.isValid || formik.isSubmitting; + } + } + + function handleText(): string { + switch (step) { + case 1: + return isImportRobot ? "Update Robot" : `Next Step`; + + case 2: + return isImportRobot + ? "Update Robot" + : robotData?.step1?.isDevelopmentMode + ? envApplication + ? "Create Application with Workspaces" + : "Create Robot" + : "Next Step"; + + case 3: + return isImportRobot ? `Update Build Configration` : `Next Step`; + + case 4: + return url?.robotName ? `Add Launch Step` : `Create Robot`; + } + } + + return ( + <div className="mt-10 flex gap-2"> + {!isImportRobot && <CFCancelButton disabled={formik?.isSubmitting} />} + <Button + type="submit" + className="!h-11 text-xs" + disabled={handleDisabled()} + loading={formik?.isSubmitting} + text={handleText()} + /> + </div> + ); +} diff --git a/src/components/CFSection/CFSection.tsx b/src/components/CFSection/CFSection.tsx index 1ad41d1d..40cb9bc9 100644 --- a/src/components/CFSection/CFSection.tsx +++ b/src/components/CFSection/CFSection.tsx @@ -7,7 +7,7 @@ interface ICFSection { export default function CFSection({ children, - gap = 1, + gap = 4, }: ICFSection): ReactElement { return <div className={`flex w-full flex-col gap-${gap}`}>{children}</div>; } diff --git a/src/components/CFStorageRange/CFStorageRange.tsx b/src/components/CFStorageRange/CFStorageRange.tsx index d7da9d88..a6589cef 100644 --- a/src/components/CFStorageRange/CFStorageRange.tsx +++ b/src/components/CFStorageRange/CFStorageRange.tsx @@ -1,7 +1,7 @@ -import { ReactElement } from "react"; -import { FormikProps } from "formik/dist/types"; -import { IDetails } from "../../interfaces/robotInterfaces"; import FormInputRange from "../FormInputRange/FormInputRange"; +import { IDetails } from "../../interfaces/robotInterfaces"; +import { FormikProps } from "formik/dist/types"; +import { ReactElement } from "react"; interface ICFStorageRange { formik: FormikProps<IDetails>; @@ -21,6 +21,8 @@ export default function CFStorageRange({ min={20} max={100} disabled={formik.isSubmitting || disabled} + error={formik.errors.robotStorage} + touched={formik.touched.robotStorage} /> ); } diff --git a/src/components/CFVDICount/CFVDICount.tsx b/src/components/CFVDICount/CFVDICount.tsx index ee28d42a..80756f05 100644 --- a/src/components/CFVDICount/CFVDICount.tsx +++ b/src/components/CFVDICount/CFVDICount.tsx @@ -1,7 +1,7 @@ -import { IDetails } from "../../interfaces/robotInterfaces"; import FormInputRange from "../FormInputRange/FormInputRange"; +import { IDetails } from "../../interfaces/robotInterfaces"; import { FormikProps } from "formik/dist/types"; -import React, { ReactElement, useEffect } from "react"; +import { ReactElement, useEffect } from "react"; interface ICFVDICount { formik: FormikProps<IDetails>; diff --git a/src/components/CreateForms/CFStep1.tsx b/src/components/CreateForms/CFStep1.tsx index 43cdb3e0..66240fa4 100644 --- a/src/components/CreateForms/CFStep1.tsx +++ b/src/components/CreateForms/CFStep1.tsx @@ -1,6 +1,4 @@ import { CFRobotStep1Validations } from "../../validations/RobotsValidations"; -import CreateRobotFormCancelButton from "../CFCancelButton/CFCancelButton"; -import { addPhysicalInstanceToFleet } from "../../toolkit/InstanceSlice"; import { Fragment, ReactElement, useEffect, useState } from "react"; import CreateRobotTypes from "../CreateRobotTypes/CreateRobotTypes"; import CreateRobotStorage from "../CFStorageRange/CFStorageRange"; @@ -10,20 +8,19 @@ import { envApplication } from "../../helpers/envProvider"; import CreateRobotFormLoader from "../CFLoader/CFLoader"; import useCreateRobot from "../../hooks/useCreateRobot"; import CFRosDistros from "../CFRosDistros/CFRosDistros"; -import { createRobot } from "../../toolkit/RobotSlice"; import CFGPUToggle from "../CFGPUToggle/CFGPUToggle"; import CFRobotName from "../CFRobotName/CFRobotName"; import useFunctions from "../../hooks/useFunctions"; -import { useAppDispatch } from "../../hooks/redux"; import CFVDICount from "../CFVDICount/CFVDICount"; import CFSection from "../CFSection/CFSection"; import Seperator from "../Seperator/Seperator"; import CFDevMode from "../CFDevMode/CFDevMode"; import { useParams } from "react-router-dom"; import useMain from "../../hooks/useMain"; -import Button from "../Button/Button"; import { useFormik } from "formik"; import { toast } from "sonner"; +import CFAdvancedSettings from "../CFAdvancedSettings/CFAdvancedSettings"; +import CFRobotButtons from "../CFRobotButtons/CFRobotButtons"; interface ICFStep1 { isImportRobot?: boolean; @@ -33,9 +30,13 @@ export default function CFStep1({ isImportRobot }: ICFStep1): ReactElement { const { robotData, setRobotData } = useCreateRobot(); const { selectedState, handleCreateRobotNextStep } = useMain(); const [responseRobot, setResponseRobot] = useState<any>(undefined); - const { getRobot, getEnvironment } = useFunctions(); + const { + getRobot, + getEnvironment, + addPhysicalInstanceToFleet, + createRobot: updateRobot, + } = useFunctions(); const url = useParams(); - const dispatch = useAppDispatch(); useEffect(() => { if (!responseRobot && isImportRobot) { @@ -87,28 +88,7 @@ export default function CFStep1({ isImportRobot }: ICFStep1): ReactElement { formik.setSubmitting(true); if (isImportRobot) { - await dispatch( - createRobot({ - organizationId: selectedState?.organization?.organizationId!, - roboticsCloudName: selectedState?.roboticsCloud?.name!, - instanceId: selectedState?.instance?.instanceId!, - region: selectedState?.roboticsCloud?.region!, - robotName: robotData?.step1?.robotName, - fleetName: selectedState?.fleet?.name, - physicalInstanceName: robotData?.step1?.isVirtualRobot - ? undefined - : robotData?.step1?.physicalInstanceName, - distributions: robotData?.step1?.rosDistros, - bridgeEnabled: robotData?.step1?.isEnabledROS2Bridge, - vdiEnabled: robotData?.step1?.remoteDesktop?.isEnabled, - vdiSessionCount: robotData?.step1?.remoteDesktop?.sessionCount, - ideEnabled: robotData?.step1?.isEnabledIde, - storageAmount: robotData?.step1?.robotStorage, - gpuEnabledForCloudInstance: - robotData?.step1?.gpuEnabledForCloudInstance, - workspaces: robotData.step2.workspaces, - }), - ); + await updateRobot(); toast.success( "Robot updated successfully. Redirecting to fleet page...", @@ -121,17 +101,7 @@ export default function CFStep1({ isImportRobot }: ICFStep1): ReactElement { ?.robotName}}`; }, 2000); } else if (!formik.values?.isVirtualRobot) { - await dispatch( - addPhysicalInstanceToFleet({ - organizationId: selectedState?.organization?.organizationId, - roboticsCloudName: selectedState?.roboticsCloud?.name, - instanceId: selectedState?.instance?.instanceId, - region: selectedState?.roboticsCloud?.region, - robolaunchFederatedFleetsName: selectedState?.fleet?.name, - robolaunchPhysicalInstancesName: - robotData.step1.physicalInstanceName, - }), - ); + addPhysicalInstanceToFleet(); } formik.setSubmitting(false); @@ -167,65 +137,57 @@ export default function CFStep1({ isImportRobot }: ICFStep1): ReactElement { > <CFSection> <CFRobotName formik={formik} isImportRobot={isImportRobot} /> + <Seperator /> </CFSection> - <Seperator /> - <CFSection> <CreateRobotTypes formik={formik} isImportRobot={isImportRobot} /> + <Seperator /> </CFSection> - <Seperator /> - <CFSection> <CFRosDistros formik={formik} isImportRobot={isImportRobot} /> + <Seperator /> </CFSection> - <Seperator /> - <CFSection> <CreateRobotStorage formik={formik} disabled={isImportRobot} /> + <Seperator /> </CFSection> - <Seperator /> - - <div className="flex items-center gap-4"> - <CFBridgeToggle formik={formik} isImportRobot={isImportRobot} /> - + <CFSection> <CFVDICount formik={formik} disabled={isImportRobot} /> - </div> - - <Seperator /> + <Seperator /> + </CFSection> <CFSection> - <CFGPUToggle formik={formik} isImportRobot={isImportRobot} /> + <div className="flex items-center gap-4"> + <CFBridgeToggle formik={formik} isImportRobot={isImportRobot} /> + <CFGPUToggle formik={formik} isImportRobot={isImportRobot} /> + </div> + <Seperator /> </CFSection> - <Seperator /> - <Fragment> {!isImportRobot && ( - <CFDevMode formik={formik} isImportRobot={isImportRobot} /> + <CFSection> + <CFDevMode formik={formik} isImportRobot={isImportRobot} /> + <Seperator /> + </CFSection> )} </Fragment> - <div className="mt-10 flex gap-2 "> - {!isImportRobot && ( - <CreateRobotFormCancelButton disabled={formik.isSubmitting} /> - )} - <Button - disabled={ - !formik.isValid || - formik.isSubmitting || - JSON.stringify(formik.initialValues) === - JSON.stringify(formik.values) - } - type="submit" - className="!h-11 text-xs" - loading={formik.isSubmitting} - text={isImportRobot ? "Update Robot" : `Next Step`} + <CFSection> + <CFAdvancedSettings formik={formik} disabled={isImportRobot} /> + </CFSection> + + <CFSection> + <CFRobotButtons + formik={formik} + step={1} + isImportRobot={isImportRobot} /> - </div> + </CFSection> </CreateRobotFormLoader> } </Fragment> diff --git a/src/components/CreateForms/CFStep2.tsx b/src/components/CreateForms/CFStep2.tsx index e922f612..5f4de927 100644 --- a/src/components/CreateForms/CFStep2.tsx +++ b/src/components/CreateForms/CFStep2.tsx @@ -4,19 +4,16 @@ import CFWorkspacesMapper from "../CFWorkspacesMapper/CFWorkspacesMapper"; import { CFAppStep2Validations } from "../../validations/AppsValidations"; import { Fragment, ReactElement, useEffect, useState } from "react"; import { IWorkspaces } from "../../interfaces/robotInterfaces"; -import CFCancelButton from "../CFCancelButton/CFCancelButton"; import { envApplication } from "../../helpers/envProvider"; import useCreateRobot from "../../hooks/useCreateRobot"; -import { createRobot } from "../../toolkit/RobotSlice"; import SidebarInfo from "../SidebarInfo/SidebarInfo"; import useFunctions from "../../hooks/useFunctions"; -import { useAppDispatch } from "../../hooks/redux"; import { useParams } from "react-router-dom"; import CFLoader from "../CFLoader/CFLoader"; import useMain from "../../hooks/useMain"; -import Button from "../Button/Button"; import { useFormik } from "formik"; import { toast } from "sonner"; +import CFRobotButtons from "../CFRobotButtons/CFRobotButtons"; interface ICFStep2 { isImportRobot?: boolean; @@ -36,16 +33,16 @@ export default function CFStep2({ isImportRobot }: ICFStep2): ReactElement { getEnvironment, getNamespace, createEnvironment, + createRobot, } = useFunctions(); const url = useParams(); - const dispatch = useAppDispatch(); const formik = useFormik<IWorkspaces>({ validationSchema: envApplication ? CFAppStep2Validations : CFRobotStep2Validations, initialValues: robotData?.step2, - onSubmit: () => { + onSubmit: async () => { formik.setSubmitting(true); if (envApplication) { @@ -53,28 +50,7 @@ export default function CFStep2({ isImportRobot }: ICFStep2): ReactElement { await handleSubmit(); }); } else { - dispatch( - createRobot({ - organizationId: selectedState?.organization?.organizationId!, - roboticsCloudName: selectedState?.roboticsCloud?.name!, - instanceId: selectedState?.instance?.instanceId!, - region: selectedState?.roboticsCloud?.region!, - robotName: robotData?.step1?.robotName, - fleetName: selectedState?.fleet?.name, - physicalInstanceName: robotData?.step1?.isVirtualRobot - ? undefined - : robotData?.step1?.physicalInstanceName, - distributions: robotData?.step1?.rosDistros, - bridgeEnabled: robotData?.step1?.isEnabledROS2Bridge, - vdiEnabled: robotData?.step1?.remoteDesktop?.isEnabled, - vdiSessionCount: robotData?.step1?.remoteDesktop?.sessionCount, - ideEnabled: robotData?.step1?.isEnabledIde, - storageAmount: robotData?.step1?.robotStorage, - gpuEnabledForCloudInstance: - robotData?.step1?.gpuEnabledForCloudInstance, - workspaces: robotData.step2.workspaces, - }), - ).then(async () => { + await createRobot().then(async () => { await handleSubmit(); }); } @@ -244,52 +220,17 @@ export default function CFStep2({ isImportRobot }: ICFStep2): ReactElement { isImportRobot={isImportRobot} /> - <CFAddWorkspaceButton formik={formik} /> + <CFAddWorkspaceButton formik={formik} disabled={isImportRobot} /> </Fragment> )} <Fragment> {!(envApplication && url?.robotName) && ( - <div className="mt-10 flex w-full flex-col gap-6"> - <div className="flex gap-2"> - {!isImportRobot && ( - <CFCancelButton disabled={formik.isSubmitting} /> - )} - <Button - type="submit" - disabled={ - !!( - !formik?.isValid || - formik.isSubmitting || - JSON.stringify(formik.initialValues) === - JSON.stringify(formik.values) || - (envApplication && url?.robotName) - ) - } - loading={formik.isSubmitting} - className="!h-11 w-full text-xs" - text={ - formik.isSubmitting ? ( - <img - className="h-10 w-10" - src="/svg/general/loading.svg" - alt="loading" - /> - ) : isImportRobot ? ( - "Update Robot" - ) : robotData?.step1?.isDevelopmentMode ? ( - envApplication ? ( - "Create Application with Workspaces" - ) : ( - "Create Robot" - ) - ) : ( - "Next Step" - ) - } - /> - </div> - </div> + <CFRobotButtons + formik={formik} + step={2} + isImportRobot={isImportRobot} + /> )} </Fragment> </CFLoader> diff --git a/src/components/CreateForms/CFStep3.tsx b/src/components/CreateForms/CFStep3.tsx index c96404a7..b0bfd0a1 100644 --- a/src/components/CreateForms/CFStep3.tsx +++ b/src/components/CreateForms/CFStep3.tsx @@ -1,10 +1,8 @@ -import RobotDeleteBuildManagerButton from "../RobotDeleteBuildManagerButton/RobotDeleteBuildManagerButton"; import CreateRobotFormAddButton from "../CreateRobotFormAddButton/CreateRobotFormAddButton"; -import React, { Fragment, ReactElement, useEffect, useState } from "react"; import CFAddBuildButton from "../CFAddBuildButton/CFAddBuildButton"; import { IBuildSteps } from "../../interfaces/robotInterfaces"; +import { Fragment, ReactElement, useEffect, useState } from "react"; import { createBuildManager } from "../../toolkit/RobotSlice"; -import CFCancelButton from "../CFCancelButton/CFCancelButton"; import CFBuildMapper from "../CFBuildMapper/CFBuildMapper"; import CreateRobotFormLoader from "../CFLoader/CFLoader"; import useCreateRobot from "../../hooks/useCreateRobot"; @@ -14,9 +12,9 @@ import useFunctions from "../../hooks/useFunctions"; import { useAppDispatch } from "../../hooks/redux"; import { FormikProps, useFormik } from "formik"; import useMain from "../../hooks/useMain"; -import Button from "../Button/Button"; import { toast } from "sonner"; import * as Yup from "yup"; +import CFRobotButtons from "../CFRobotButtons/CFRobotButtons"; interface ICFStep3 { isImportRobot?: boolean; @@ -372,39 +370,11 @@ export default function CFStep3({ isImportRobot }: ICFStep3): ReactElement { <CFAddBuildButton formik={formik} /> - <div className="mt-10 flex w-full gap-2"> - {isImportRobot ? ( - <RobotDeleteBuildManagerButton - disabled={formik?.isSubmitting} - submitting={formik?.isSubmitting} - /> - ) : ( - <CFCancelButton disabled={formik?.isSubmitting} /> - )} - <Button - type="submit" - disabled={ - !formik?.isValid || - formik.isSubmitting || - JSON.stringify(formik.initialValues) === - JSON.stringify(formik.values) - } - className="!h-11 w-full text-xs" - text={ - formik.isSubmitting ? ( - <img - className="h-10 w-10" - src="/svg/general/loading.svg" - alt="loading" - /> - ) : isImportRobot ? ( - `Update Build Configration` - ) : ( - `Next Step` - ) - } - /> - </div> + <CFRobotButtons + formik={formik} + step={3} + isImportRobot={isImportRobot} + /> </Fragment> )} </CreateRobotFormLoader> diff --git a/src/components/CreateForms/CFStep4.tsx b/src/components/CreateForms/CFStep4.tsx index 9b55874b..fe8eeebe 100644 --- a/src/components/CreateForms/CFStep4.tsx +++ b/src/components/CreateForms/CFStep4.tsx @@ -1,5 +1,4 @@ import RobotDeleteLaunchManagerButton from "../RobotDeleteLaunchManagerButton/RobotDeleteLaunchManagerButton"; -import CreateRobotFormCancelButton from "../CFCancelButton/CFCancelButton"; import { organizationNameViewer } from "../../functions/GeneralFunctions"; import CFLaunchWorkspace from "../CFLaunchWorkspace/CFLaunchWorkspace"; import { Fragment, ReactElement, useEffect, useState } from "react"; @@ -13,12 +12,11 @@ import CFLaunchCode from "../CFLaunchCode/CFLaunchCode"; import CFEnvMapper from "../CFEnvMapper/CFEnvMapper"; import useFunctions from "../../hooks/useFunctions"; import { useAppDispatch } from "../../hooks/redux"; -import { useParams } from "react-router-dom"; import useMain from "../../hooks/useMain"; -import Button from "../Button/Button"; import { useFormik } from "formik"; import { toast } from "sonner"; import * as Yup from "yup"; +import CFRobotButtons from "../CFRobotButtons/CFRobotButtons"; interface ICFStep4 { isImportRobot?: boolean; @@ -37,7 +35,6 @@ export default function CFStep4({ const [responseBuildManager, setResponseBuildManager] = useState<any>(undefined); const { getBuildManager } = useFunctions(); - const url = useParams(); const formik = useFormik<ILaunchStep>({ initialValues: @@ -198,17 +195,11 @@ export default function CFStep4({ <div className="mt-10 flex gap-2"> {!isImportRobot ? ( - <Fragment> - {!url?.robotName && ( - <CreateRobotFormCancelButton disabled={formik.isSubmitting} /> - )} - <Button - type="submit" - disabled={!formik?.isValid || formik.isSubmitting} - className="!h-11 w-full text-xs" - text={url?.robotName ? `Add Launch Step` : `Create Robot`} - /> - </Fragment> + <CFRobotButtons + formik={formik} + step={4} + isImportRobot={isImportRobot} + /> ) : ( <RobotDeleteLaunchManagerButton launchManagerName={formik.values?.name} diff --git a/src/components/FormInputRange/FormInputRange.tsx b/src/components/FormInputRange/FormInputRange.tsx index c6192645..8077cb2f 100644 --- a/src/components/FormInputRange/FormInputRange.tsx +++ b/src/components/FormInputRange/FormInputRange.tsx @@ -1,5 +1,5 @@ -import React, { ReactElement } from "react"; import CFInfoBar from "../CFInfoBar/CFInfoBar"; +import { ReactElement } from "react"; interface IFormInputRange { label: string; @@ -38,7 +38,7 @@ export default function FormInputRange({ type="range" autoComplete="off" {...InputProps} - className={`mb-3 ml-2 w-full ${disabled && "cursor-not-allowed"}`} + className={`ml-2 w-full ${disabled && "cursor-not-allowed"}`} style={{ appearance: "auto", color: "#AC2DFE", diff --git a/src/components/WorkspacesTable/WorkspacesTable.tsx b/src/components/WorkspacesTable/WorkspacesTable.tsx index 3ed894a2..5e07f3e3 100644 --- a/src/components/WorkspacesTable/WorkspacesTable.tsx +++ b/src/components/WorkspacesTable/WorkspacesTable.tsx @@ -15,8 +15,6 @@ export default function WorkspacesTable({ }: IWorkspacesTable): ReactElement { const url = useParams(); - console.log("gg", responseRobot); - const data: any = useMemo( () => responseRobot?.robotWorkspaces?.map((workspace: any) => { diff --git a/src/contexts/FunctionsContext.tsx b/src/contexts/FunctionsContext.tsx index cd1ee2f7..7b1e6c35 100644 --- a/src/contexts/FunctionsContext.tsx +++ b/src/contexts/FunctionsContext.tsx @@ -814,9 +814,9 @@ export default ({ children }: any) => { ?.cloudInstances[0]?.robolaunchFederatedRobots[0] ?.vdiEnabled, sessionCount: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.vdiSessionCount, + responseFederatedRobot?.payload?.data?.[0] + ?.roboticsClouds?.[0]?.cloudInstances?.[0] + ?.robolaunchFederatedRobots?.[0]?.vdiSessionCount, }, rosDistros: responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] @@ -834,27 +834,44 @@ export default ({ children }: any) => { ?.cloudInstances[0]?.robolaunchFederatedRobots[0] ?.vdiPodName, idePodName: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.idePodName, + responseFederatedRobot?.payload?.data?.[0] + ?.roboticsClouds?.[0]?.cloudInstances?.[0] + ?.robolaunchFederatedRobots?.[0]?.idePodName, + permittedDirectories: + responseFederatedRobot?.payload?.data?.[0] + ?.roboticsClouds?.[0]?.cloudInstances?.[0] + ?.robolaunchFederatedRobots?.[0]?.permittedDirectories, + persistentDirectories: + responseFederatedRobot?.payload?.data?.[0] + ?.roboticsClouds?.[0]?.cloudInstances?.[0] + ?.robolaunchFederatedRobots?.[0]?.persistentDirectories, + hostDirectories: + responseFederatedRobot?.payload?.data?.[0]?.roboticsClouds?.[0]?.cloudInstances?.[0]?.robolaunchFederatedRobots?.[0]?.hostDirectories + ?.split(",") + ?.map((item: string) => { + return { + hostDirectory: item?.split(":")[0], + mountPath: item?.split(":")[1], + }; + }), ideCustomPorts: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.environments[0]?.ideCustomPorts + responseFederatedRobot?.payload?.data?.[0]?.roboticsClouds?.[0]?.cloudInstances?.[0]?.robolaunchFederatedRobots?.[0]?.ideCustomPorts ?.split("/") ?.map((item: string) => { return { - name: item?.split("-")[0], - port: item?.split("-")[1].split(":")[1], - backendPort: item?.split("-")[1].split(":")[0], + name: item?.split("-")?.[0], + port: item?.split("-")?.[1].split(":")?.[1], + backendPort: item?.split("-")?.[1].split(":")?.[0], }; }), vdiCustomPorts: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.environments[0]?.vdiCustomPorts + responseFederatedRobot?.payload?.data?.[0]?.roboticsClouds?.[0]?.cloudInstances?.[0]?.robolaunchFederatedRobots?.[0]?.vdiCustomPorts ?.split("/") ?.map((item: string) => { return { - name: item?.split("-")[0], - port: item?.split("-")[1].split(":")[1], - backendPort: item?.split("-")[1].split(":")[0], + name: item?.split("-")?.[0], + port: item?.split("-")?.[1].split(":")?.[1], + backendPort: item?.split("-")?.[1].split(":")?.[0], }; }), }, @@ -1195,67 +1212,77 @@ export default ({ children }: any) => { }); } - async function addPhysicalInstanceToFleet() { - await dispatch( - addPhysicalInstanceToFleetDispatch({ - organizationId: selectedState?.organization?.organizationId, - roboticsCloudName: selectedState?.roboticsCloud?.name, - instanceId: selectedState?.instance?.instanceId, - region: selectedState?.roboticsCloud?.region, - robolaunchFederatedFleetsName: selectedState?.fleet?.name, - robolaunchPhysicalInstancesName: robotData.step1.physicalInstanceName, - }), - ); - } - - async function createRobot() { - await dispatch( - createRobotDispatch({ - organizationId: selectedState?.organization?.organizationId!, - roboticsCloudName: selectedState?.roboticsCloud?.name!, - instanceId: selectedState?.instance?.instanceId!, - region: selectedState?.roboticsCloud?.region!, - robotName: robotData?.step1?.robotName, - fleetName: selectedState?.fleet?.name, - physicalInstanceName: robotData?.step1?.isVirtualRobot - ? undefined - : robotData?.step1?.physicalInstanceName, - distributions: robotData?.step1?.rosDistros, - bridgeEnabled: robotData?.step1?.isEnabledROS2Bridge, - vdiEnabled: robotData?.step1?.remoteDesktop?.isEnabled, - vdiSessionCount: robotData?.step1?.remoteDesktop?.sessionCount, - ideEnabled: robotData?.step1?.isEnabledIde, - storageAmount: robotData?.step1?.robotStorage, - gpuEnabledForCloudInstance: - robotData?.step1?.gpuEnabledForCloudInstance, - workspaces: robotData.step2.workspaces, - }), - ); + function addPhysicalInstanceToFleet() { + return new Promise<void>(async (resolve, reject) => { + try { + await dispatch( + addPhysicalInstanceToFleetDispatch({ + organizationId: selectedState?.organization?.organizationId, + roboticsCloudName: selectedState?.roboticsCloud?.name, + instanceId: selectedState?.instance?.instanceId, + region: selectedState?.roboticsCloud?.region, + robolaunchFederatedFleetsName: selectedState?.fleet?.name, + robolaunchPhysicalInstancesName: + robotData.step1.physicalInstanceName, + }), + ); + resolve(); + } catch (error) { + reject(error); + } + }); } - async function updateRobot() { - await dispatch( - createRobotDispatch({ - organizationId: selectedState?.organization?.organizationId!, - roboticsCloudName: selectedState?.roboticsCloud?.name!, - instanceId: selectedState?.instance?.instanceId!, - region: selectedState?.roboticsCloud?.region!, - fleetName: selectedState?.fleet?.name, - robotName: robotData.step1.robotName, - workspaceUpdated: true, - physicalInstanceName: robotData?.step1?.isVirtualRobot - ? undefined - : robotData?.step1?.physicalInstanceName, - distributions: robotData.step1.rosDistros, - bridgeEnabled: robotData.step1.isEnabledROS2Bridge, - vdiEnabled: robotData.step1.remoteDesktop.isEnabled, - vdiSessionCount: robotData.step1.remoteDesktop.sessionCount, - ideEnabled: robotData.step1.isEnabledIde, - storageAmount: robotData.step1.robotStorage, - gpuEnabledForCloudInstance: robotData.step1.gpuEnabledForCloudInstance, - workspaces: robotData.step2.workspaces, - }), - ); + function createRobot() { + return new Promise<void>(async (resolve, reject) => { + try { + await dispatch( + createRobotDispatch({ + organizationId: selectedState?.organization?.organizationId!, + region: selectedState?.roboticsCloud?.region!, + roboticsCloudName: selectedState?.roboticsCloud?.name!, + instanceId: selectedState?.instance?.instanceId!, + fleetName: selectedState?.fleet?.name, + robotName: robotData?.step1?.robotName, + physicalInstanceName: robotData?.step1?.isVirtualRobot + ? undefined + : robotData?.step1?.physicalInstanceName, + distributions: robotData?.step1?.rosDistros, + bridgeEnabled: robotData?.step1?.isEnabledROS2Bridge, + vdiEnabled: robotData?.step1?.remoteDesktop?.isEnabled, + vdiSessionCount: robotData?.step1?.remoteDesktop?.sessionCount, + ideEnabled: robotData?.step1?.isEnabledIde, + storageAmount: robotData?.step1?.robotStorage, + gpuEnabledForCloudInstance: + robotData?.step1?.gpuEnabledForCloudInstance, + workspaces: robotData.step2.workspaces, + permittedDirectories: robotData?.step1?.permittedDirectories, + persistentDirectories: robotData?.step1?.persistentDirectories, + hostDirectories: + robotData?.step1?.hostDirectories + ?.map((directory) => { + return `${directory.hostDirectory}:${directory.mountPath}`; + }) + ?.join(",") || "", + ideCustomPorts: + robotData.step1.ideCustomPorts + ?.map((port) => { + return `${port.name}-${port.backendPort}:${port.port}`; + }) + ?.join("/") || "", + vdiCustomPorts: + robotData.step1.vdiCustomPorts + ?.map((port) => { + return `${port.name}-${port.backendPort}:${port.port}`; + }) + ?.join("/") || "", + }), + ); + resolve(); + } catch (error) { + reject(error); + } + }); } function createEnvironment(withoutWorkspaces: boolean) { @@ -1264,16 +1291,18 @@ export default ({ children }: any) => { await dispatch( createEnvironmentDispatch({ organizationId: selectedState?.organization?.organizationId!, - roboticsCloudName: selectedState?.roboticsCloud?.name!, region: selectedState?.roboticsCloud?.region!, + roboticsCloudName: selectedState?.roboticsCloud?.name!, instanceId: selectedState?.instance?.instanceId!, fleetName: selectedState?.fleet?.name, environmentName: robotData?.step1?.robotName, - domainName: robotData?.step1?.domainName, + storageAmount: robotData?.step1?.robotStorage, + vdiSessionCount: robotData?.step1?.remoteDesktop?.sessionCount, + + domainName: robotData?.step1?.domainName, ideGpuResource: robotData?.step1?.ideGpuResource, ideGpuResourceType: robotData?.step1?.ideGpuResourceType, - vdiSessionCount: robotData?.step1?.remoteDesktop?.sessionCount, applicationName: robotData?.step1?.application?.name, applicationVersion: robotData?.step1?.application?.version, devspaceUbuntuDistro: robotData?.step1?.devspace?.ubuntuDistro, @@ -1365,7 +1394,6 @@ export default ({ children }: any) => { getEnvironment, addPhysicalInstanceToFleet, createRobot, - updateRobot, createEnvironment, createBuildManager, getIP, diff --git a/src/controllers/NamespaceTableData.tsx b/src/controllers/NamespaceTableData.tsx index feef7db1..6e095e46 100644 --- a/src/controllers/NamespaceTableData.tsx +++ b/src/controllers/NamespaceTableData.tsx @@ -1,24 +1,183 @@ -import { useMemo } from "react"; -import useMain from "../hooks/useMain"; -import InfoCell from "../components/TableInformationCells/InfoCell"; -import BasicCell from "../components/TableInformationCells/BasicCell"; -import { envApplication } from "../helpers/envProvider"; -import RobotServicesCell from "../components/TableInformationCells/RobotServicesCell"; -import StateCell from "../components/TableInformationCells/StateCell"; import EnvironmentActionCells from "../components/TableActionCells/EnvironmentActionCells"; +import RobotServicesCell from "../components/TableInformationCells/RobotServicesCell"; import RobotActionCells from "../components/TableActionCells/RobotActionCells"; import { handleSplitOrganizationName } from "../functions/GeneralFunctions"; +import StateCell from "../components/TableInformationCells/StateCell"; +import BasicCell from "../components/TableInformationCells/BasicCell"; +import InfoCell from "../components/TableInformationCells/InfoCell"; +import { envApplication } from "../helpers/envProvider"; +import { useEffect, useMemo, useState } from "react"; +import useFunctions from "../hooks/useFunctions"; +import { useParams } from "react-router-dom"; +import useMain from "../hooks/useMain"; -interface INamespaceTableData { - responseRobots: any; - setReload: any; -} +export function NamespaceTableData() { + const { pagesState, selectedState } = useMain(); + const url = useParams(); + const [reload, setReload] = useState<boolean>(false); + const [responseRobots, setResponseRobots] = useState<any>(undefined); + + const { + getOrganization, + getRoboticsCloud, + getInstance, + getFleet, + getNamespace, + getRobots, + getEnvironments, + } = useFunctions(); + + useEffect(() => { + if ( + pagesState?.organization?.organizationName !== + `org_${url?.organizationName}` + ) { + handleGetOrganization(); + } else if (pagesState?.roboticsCloud?.name !== url?.roboticsCloudName) { + handleGetRoboticsCloud(); + } else if (pagesState?.instance?.name !== url?.instanceName) { + handleGetInstance(); + } else if (pagesState?.fleet?.name !== url?.fleetName) { + envApplication ? handleGetNamespace() : handleGetFleet(); + } else { + envApplication ? handleGetEnvironments() : handleGetRobots(); + } + + const timer = + selectedState?.organization && + selectedState?.roboticsCloud && + selectedState?.instance && + selectedState?.fleet && + setInterval(() => { + pagesState?.fleet && envApplication + ? handleGetEnvironments() + : handleGetRobots(); + }, 10000); + + return () => { + clearInterval(timer); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pagesState, url, reload]); + + useEffect(() => { + setResponseRobots(undefined); + }, [url]); + + function handleReload() { + setResponseRobots(undefined); + setReload((prevState: boolean) => !prevState); + } + + function handleGetOrganization() { + getOrganization( + { + organizationName: url?.organizationName!, + }, + { + isSetState: true, + ifErrorNavigateTo404: !responseRobots, + setPages: true, + }, + ); + } + + function handleGetRoboticsCloud() { + getRoboticsCloud( + { + organizationId: pagesState?.organization?.organizationId!, + roboticsCloudName: url?.roboticsCloudName!, + }, + { + isSetState: true, + ifErrorNavigateTo404: !responseRobots, + setPages: true, + }, + ); + } + + function handleGetInstance() { + getInstance( + { + organizationId: pagesState?.organization?.organizationId!, + roboticsCloudName: pagesState?.roboticsCloud?.name!, + instanceName: url?.instanceName!, + region: pagesState?.roboticsCloud?.region!, + details: true, + }, + { + isSetState: true, + ifErrorNavigateTo404: !responseRobots, + setPages: true, + }, + ); + } + + function handleGetFleet() { + getFleet( + { + organizationId: pagesState?.organization?.organizationId!, + roboticsCloudName: pagesState?.roboticsCloud?.name!, + instanceId: pagesState?.instance?.instanceId!, + region: pagesState?.roboticsCloud?.region!, + fleetName: url?.fleetName!, + }, + { + isSetState: true, + ifErrorNavigateTo404: !responseRobots, + setPages: true, + }, + ); + } + + function handleGetNamespace() { + getNamespace( + { + organizationId: selectedState?.organization?.organizationId!, + roboticsCloudName: selectedState?.roboticsCloud?.name!, + instanceId: selectedState?.instance?.instanceId!, + region: selectedState?.instance?.region!, + namespaceName: url?.fleetName!, + }, + { + isSetState: true, + ifErrorNavigateTo404: !responseRobots, + setPages: true, + }, + ); + } + + function handleGetRobots() { + getRobots( + { + organizationId: pagesState?.organization?.organizationId!, + roboticsCloudName: pagesState?.roboticsCloud?.name!, + instanceId: pagesState?.instance?.instanceId!, + region: pagesState?.roboticsCloud?.region!, + fleetName: pagesState?.fleet?.name, + }, + { + ifErrorNavigateTo404: !responseRobots, + setResponse: setResponseRobots, + }, + ); + } -export function NamespaceTableData({ - responseRobots, - setReload, -}: INamespaceTableData) { - const { pagesState } = useMain(); + function handleGetEnvironments() { + getEnvironments( + { + organizationId: pagesState?.organization?.organizationId!, + roboticsCloudName: pagesState?.roboticsCloud?.name!, + instanceId: pagesState?.instance?.instanceId!, + region: pagesState?.roboticsCloud?.region!, + fleetName: pagesState?.fleet?.name, + }, + { + ifErrorNavigateTo404: !responseRobots, + setResponse: setResponseRobots, + }, + ); + } const data: any = useMemo( () => @@ -119,7 +278,7 @@ export function NamespaceTableData({ key: "robotServices", header: `${envApplication ? "Application" : "Robot"} Services`, sortable: true, - filter: true, + filter: false, align: "left", body: (rowData: any) => { return ( @@ -144,7 +303,7 @@ export function NamespaceTableData({ key: "physicalState", header: `Physical ${envApplication ? "Application" : "Robot"} State`, sortable: true, - filter: true, + filter: false, align: "left", body: (rowData: any) => { if (!rowData?.physicalState) { @@ -179,5 +338,7 @@ export function NamespaceTableData({ return { data, columns, + responseRobots, + handleReload, }; } diff --git a/src/interfaces/robotInterfaces.ts b/src/interfaces/robotInterfaces.ts index 1e3c6382..10d02857 100644 --- a/src/interfaces/robotInterfaces.ts +++ b/src/interfaces/robotInterfaces.ts @@ -21,6 +21,11 @@ export interface IcreateRobotRequest { imageRepository?: string; imageTag?: string; workspaces: IWorkspace[]; + permittedDirectories: string; + persistentDirectories: string; + hostDirectories: string; + ideCustomPorts: string; + vdiCustomPorts: string; } export interface IgetRobotsRequest { diff --git a/src/interfaces/useFunctionsInterfaces.ts b/src/interfaces/useFunctionsInterfaces.ts index 23438f70..cdf0b4ee 100644 --- a/src/interfaces/useFunctionsInterfaces.ts +++ b/src/interfaces/useFunctionsInterfaces.ts @@ -191,7 +191,6 @@ export interface IuseFunctions { ) => void; getRobots: (values: IgetRobots, parameters?: ImultipleGetParameters) => void; getRobot: (values: IgetRobot, parameters?: IsingleGetRobotParameters) => void; - getBuildManager: ( values: IgetBuildManager, parameters?: IsingleGetBuildParameters, @@ -208,12 +207,13 @@ export interface IuseFunctions { values: IgetEnvironmentRequest, parameters?: IsingleGetEnviromentParameters, ) => void; - addPhysicalInstanceToFleet: () => void; - createRobot: () => void; - updateRobot: () => void; + + addPhysicalInstanceToFleet: () => Promise<void>; + createRobot: () => Promise<void>; createEnvironment: (withoutWorkspaces?: boolean) => Promise<void>; createBuildManager: () => void; getIP: () => void; + handleSetterCurrentOrganization: ( urlOrganizationName: string | undefined, ) => void; @@ -225,7 +225,6 @@ export interface IuseFunctions { handleSetterResponseOrganizations: (setResponseOrganizations: any) => void; handleSetterResponseRoboticsClouds: (setResponseRoboticsClouds: any) => void; handleSetterResponseInstances: (setResponseInstances: any) => void; - handleSetterResponseFleets: (setResponseFleets: any) => void; handleSetterResponseFleet: (setResponseFleet: any) => void; handleSetterResponseRobots: (setResponseRobots: any) => void; diff --git a/src/pages/DashboardsPage/NSDashboard/NSDashboard.tsx b/src/pages/DashboardsPage/NSDashboard/NSDashboard.tsx index 475933bf..6879f453 100644 --- a/src/pages/DashboardsPage/NSDashboard/NSDashboard.tsx +++ b/src/pages/DashboardsPage/NSDashboard/NSDashboard.tsx @@ -1,185 +1,21 @@ -import { ReactElement, useEffect, useState } from "react"; import InformationWidget from "../../../components/InformationWidget/InformationWidget"; +import { NamespaceTableData } from "../../../controllers/NamespaceTableData"; +import DashboardLayout from "../../../layouts/DashboardLayout"; import RegionsWidget from "../../../components/RegionsWidget/RegionsWidget"; import GeneralTable from "../../../components/Table/GeneralTable"; -import DashboardLayout from "../../../layouts/DashboardLayout"; -import useFunctions from "../../../hooks/useFunctions"; -import { useParams } from "react-router-dom"; -import useMain from "../../../hooks/useMain"; -import { envApplication } from "../../../helpers/envProvider"; import CountWidget from "../../../components/CountWidget/CountWidget"; -import { getGuideItem } from "../../../functions/handleGuide"; import TourGuide from "../../../components/TourGuide/TourGuide"; -import { NamespaceTableData } from "../../../controllers/NamespaceTableData"; +import { getGuideItem } from "../../../functions/handleGuide"; +import { envApplication } from "../../../helpers/envProvider"; +import useMain from "../../../hooks/useMain"; +import { useParams } from "react-router-dom"; +import { ReactElement } from "react"; export default function NSDashboard(): ReactElement { - const [responseRobots, setResponseRobots] = useState<any>(undefined); - const { pagesState, selectedState } = useMain(); - const [reload, setReload] = useState<boolean>(false); + const { data, columns, responseRobots, handleReload } = NamespaceTableData(); + const { selectedState } = useMain(); const url = useParams(); - const { - getOrganization, - getRoboticsCloud, - getInstance, - getFleet, - getNamespace, - getRobots, - getEnvironments, - } = useFunctions(); - - useEffect(() => { - if ( - pagesState?.organization?.organizationName !== - `org_${url?.organizationName}` - ) { - handleGetOrganization(); - } else if (pagesState?.roboticsCloud?.name !== url?.roboticsCloudName) { - handleGetRoboticsCloud(); - } else if (pagesState?.instance?.name !== url?.instanceName) { - handleGetInstance(); - } else if (pagesState?.fleet?.name !== url?.fleetName) { - envApplication ? handleGetNamespace() : handleGetFleet(); - } else { - envApplication ? handleGetEnvironments() : handleGetRobots(); - } - - const timer = - selectedState?.organization && - selectedState?.roboticsCloud && - selectedState?.instance && - selectedState?.fleet && - setInterval(() => { - pagesState?.fleet && envApplication - ? handleGetEnvironments() - : handleGetRobots(); - }, 10000); - - return () => { - clearInterval(timer); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pagesState, url, reload]); - - useEffect(() => { - setResponseRobots(undefined); - }, [url]); - - const { data, columns } = NamespaceTableData({ - responseRobots, - setReload, - }); - - function handleGetOrganization() { - getOrganization( - { - organizationName: url?.organizationName!, - }, - { - isSetState: true, - ifErrorNavigateTo404: !responseRobots, - setPages: true, - }, - ); - } - - function handleGetRoboticsCloud() { - getRoboticsCloud( - { - organizationId: pagesState?.organization?.organizationId!, - roboticsCloudName: url?.roboticsCloudName!, - }, - { - isSetState: true, - ifErrorNavigateTo404: !responseRobots, - setPages: true, - }, - ); - } - - function handleGetInstance() { - getInstance( - { - organizationId: pagesState?.organization?.organizationId!, - roboticsCloudName: pagesState?.roboticsCloud?.name!, - instanceName: url?.instanceName!, - region: pagesState?.roboticsCloud?.region!, - details: true, - }, - { - isSetState: true, - ifErrorNavigateTo404: !responseRobots, - setPages: true, - }, - ); - } - - function handleGetFleet() { - getFleet( - { - organizationId: pagesState?.organization?.organizationId!, - roboticsCloudName: pagesState?.roboticsCloud?.name!, - instanceId: pagesState?.instance?.instanceId!, - region: pagesState?.roboticsCloud?.region!, - fleetName: url?.fleetName!, - }, - { - isSetState: true, - ifErrorNavigateTo404: !responseRobots, - setPages: true, - }, - ); - } - - function handleGetNamespace() { - getNamespace( - { - organizationId: selectedState?.organization?.organizationId!, - roboticsCloudName: selectedState?.roboticsCloud?.name!, - instanceId: selectedState?.instance?.instanceId!, - region: selectedState?.instance?.region!, - namespaceName: url?.fleetName!, - }, - { - isSetState: true, - ifErrorNavigateTo404: !responseRobots, - setPages: true, - }, - ); - } - - function handleGetRobots() { - getRobots( - { - organizationId: pagesState?.organization?.organizationId!, - roboticsCloudName: pagesState?.roboticsCloud?.name!, - instanceId: pagesState?.instance?.instanceId!, - region: pagesState?.roboticsCloud?.region!, - fleetName: pagesState?.fleet?.name, - }, - { - ifErrorNavigateTo404: !responseRobots, - setResponse: setResponseRobots, - }, - ); - } - - function handleGetEnvironments() { - getEnvironments( - { - organizationId: pagesState?.organization?.organizationId!, - roboticsCloudName: pagesState?.roboticsCloud?.name!, - instanceId: pagesState?.instance?.instanceId!, - region: pagesState?.roboticsCloud?.region!, - fleetName: pagesState?.fleet?.name, - }, - { - ifErrorNavigateTo404: !responseRobots, - setResponse: setResponseRobots, - }, - ); - } - return ( <DashboardLayout widget1={ @@ -246,11 +82,8 @@ export default function NSDashboard(): ReactElement { title={envApplication ? "Applications" : "Robots"} data={data} columns={columns} - loading={Array.isArray(responseRobots) ? false : true} - handleReload={() => { - setResponseRobots(undefined); - setReload(!reload); - }} + loading={!Array.isArray(responseRobots)} + handleReload={handleReload} /> } /> diff --git a/src/toolkit/RobotSlice.ts b/src/toolkit/RobotSlice.ts index 6e355042..cc951902 100644 --- a/src/toolkit/RobotSlice.ts +++ b/src/toolkit/RobotSlice.ts @@ -55,6 +55,12 @@ export const createRobot = createAsyncThunk( imageTag: values?.imageTag, }, robotWorkspaces: values?.workspaces, + + permittedDirectories: values?.permittedDirectories, + persistentDirectories: values?.persistentDirectories, + hostDirectories: values?.hostDirectories, + ideCustomPorts: values?.ideCustomPorts, + vdiCustomPorts: values?.vdiCustomPorts, }, ], }, @@ -63,7 +69,7 @@ export const createRobot = createAsyncThunk( ], }); return response.data; - } + }, ); export const getRobots = createAsyncThunk( @@ -86,7 +92,7 @@ export const getRobots = createAsyncThunk( ], }); return response.data; - } + }, ); export const getRobot = createAsyncThunk( @@ -111,7 +117,7 @@ export const getRobot = createAsyncThunk( ], }); return response.data; - } + }, ); export const deleteRobot = createAsyncThunk( @@ -136,7 +142,7 @@ export const deleteRobot = createAsyncThunk( ], }); return response.data; - } + }, ); export const createBuildManager = createAsyncThunk( @@ -167,7 +173,7 @@ export const createBuildManager = createAsyncThunk( ], }); return response.data; - } + }, ); export const getBuildManagers = createAsyncThunk( @@ -192,7 +198,7 @@ export const getBuildManagers = createAsyncThunk( ], }); return response.data; - } + }, ); export const getBuildManager = createAsyncThunk( @@ -221,7 +227,7 @@ export const getBuildManager = createAsyncThunk( ], }); return response.data; - } + }, ); export const deleteBuildManager = createAsyncThunk( @@ -250,7 +256,7 @@ export const deleteBuildManager = createAsyncThunk( ], }); return response.data; - } + }, ); export const createLaunchManager = createAsyncThunk( @@ -281,7 +287,7 @@ export const createLaunchManager = createAsyncThunk( ], }); return response.data; - } + }, ); export const getLaunchManagers = createAsyncThunk( @@ -306,7 +312,7 @@ export const getLaunchManagers = createAsyncThunk( ], }); return response.data; - } + }, ); export const getLaunchManager = createAsyncThunk( @@ -335,7 +341,7 @@ export const getLaunchManager = createAsyncThunk( ], }); return response.data; - } + }, ); export const deleteLaunchManager = createAsyncThunk( @@ -365,7 +371,7 @@ export const deleteLaunchManager = createAsyncThunk( ], }); return response.data; - } + }, ); export const RobotSlice = createSlice({ diff --git a/src/validations/RobotsValidations.ts b/src/validations/RobotsValidations.ts index c7472194..6a4a956b 100644 --- a/src/validations/RobotsValidations.ts +++ b/src/validations/RobotsValidations.ts @@ -30,6 +30,41 @@ export const CFRobotStep1Validations = Yup.object().shape({ otherwise: Yup.number().notRequired(), }), }), + hostDirectories: Yup.array().of( + Yup.object().shape({ + hostDirectory: Yup.string() + .required("Directory is required.") + .matches(/^\//, "Path must start with a '/'"), + mountPath: Yup.string() + .required("Path is required.") + .matches(/^\//, "Path must start with a '/'"), + }), + ), + ideCustomPorts: Yup.array().of( + Yup.object().shape({ + name: Yup.string() + .required("Port name is required.") + .min(4, "Minimum 4 characters.") + .max(4, "Maximum 4 characters.") + .matches(/^[a-z]+$/, "Must be lowercase and english letters only."), + port: Yup.number() + .required("Port is required.") + .min(0, "Minimum 0.") + .max(65535, "Maximum 65535."), + }), + ), + vdiCustomPorts: Yup.array().of( + Yup.object().shape({ + name: Yup.string() + .required("Port name is required.") + .min(4, "Minimum 4 characters.") + .max(4, "Maximum 4 characters."), + port: Yup.number() + .required("Port is required.") + .min(0, "Minimum 0.") + .max(65535, "Maximum 65535."), + }), + ), }); export const CFRobotStep2Validations = Yup.object().shape({