From d7b0d6cddaa5f6398b006a9fd4ca4e1f369ae6ea Mon Sep 17 00:00:00 2001 From: gokhangunduz Date: Tue, 9 Jan 2024 10:38:14 +0300 Subject: [PATCH] refactor(function-context) :tada: update context logic and robotData --- public/css/style.css | 17 +- .../CFBridgeToggle/CFBridgeToggle.tsx | 4 +- .../CFEnvCategories/CFEnvCategories.tsx | 6 +- src/components/CFGPUToggle/CFGPUToggle.tsx | 4 +- src/components/CFRobotName/CFRobotName.tsx | 6 +- src/components/CFRosDistros/CFRosDistros.tsx | 49 +- .../CreateRobotTypes/CreateRobotTypes.tsx | 95 ++-- .../EnvironmentHeader/EnvironmentHeader.tsx | 8 +- .../EnvironmentHeaderTabs.tsx} | 29 +- .../EnvironmentResource.tsx} | 2 +- src/components/Robot3D/Robot3D.tsx | 2 +- src/contexts/CreateRobotContext.tsx | 14 +- src/contexts/FunctionsContext.tsx | 448 +++++++++++------- src/contexts/RobotContext.tsx | 42 +- src/interfaces/robotInterfaces.ts | 16 + .../EnvironmentPage/CodeEditor/CodeEditor.tsx | 8 +- .../TaskManagement/BarcodeManagement.tsx | 2 +- src/validations/RobotsValidations.ts | 122 ++--- 18 files changed, 513 insertions(+), 361 deletions(-) rename src/components/{RobotHeaderTabs/RobotHeaderTabs.tsx => EnvironmentHeaderTabs/EnvironmentHeaderTabs.tsx} (79%) rename src/components/{RobotResource/RobotResource.tsx => EnvironmentResource/EnvironmentResource.tsx} (97%) diff --git a/public/css/style.css b/public/css/style.css index 222db5d4..aa9b4e4c 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1488,6 +1488,13 @@ video { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); } +.scale-90 { + --tw-scale-x: .9; + --tw-scale-y: .9; + -webkit-transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .scale-\[0\.25\] { --tw-scale-x: 0.25; --tw-scale-y: 0.25; @@ -2495,16 +2502,16 @@ video { line-height: 1.25rem; } -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - .text-xl { font-size: 1.25rem; line-height: 1.75rem; } +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + .\!font-semibold { font-weight: 600 !important; } diff --git a/src/components/CFBridgeToggle/CFBridgeToggle.tsx b/src/components/CFBridgeToggle/CFBridgeToggle.tsx index 71938b95..fec85c8b 100644 --- a/src/components/CFBridgeToggle/CFBridgeToggle.tsx +++ b/src/components/CFBridgeToggle/CFBridgeToggle.tsx @@ -18,9 +18,9 @@ export default function CFBridgeToggle({ labelInfoTip="The ROS2 Bridge allows you to connect your robot to the ecosystem. This allows you to use ROS2 tools to interact with your robot." dataTut="create-robot-step1-ros2-bridge" disabled={isImportRobot} - checked={formik?.values?.isEnabledROS2Bridge} + checked={formik?.values?.services.ros.isEnabled} onChange={(e: any) => { - formik.setFieldValue("isEnabledROS2Bridge", e); + formik.setFieldValue("services.ros.isEnabled", e); }} /> ); diff --git a/src/components/CFEnvCategories/CFEnvCategories.tsx b/src/components/CFEnvCategories/CFEnvCategories.tsx index 19c68c5a..dc99048c 100644 --- a/src/components/CFEnvCategories/CFEnvCategories.tsx +++ b/src/components/CFEnvCategories/CFEnvCategories.tsx @@ -143,9 +143,9 @@ export default function CFEnvCategories({ }); }} text={ -
+
-
+

{environment?.application?.alias || environment?.application?.name} diff --git a/src/components/CFGPUToggle/CFGPUToggle.tsx b/src/components/CFGPUToggle/CFGPUToggle.tsx index 4f9689d8..88210948 100644 --- a/src/components/CFGPUToggle/CFGPUToggle.tsx +++ b/src/components/CFGPUToggle/CFGPUToggle.tsx @@ -18,9 +18,9 @@ export default function CFGPUToggle({ labelInfoTip="If you want or need to GPU resource on cloud instance set active" dataTut="create-robot-step1-gpu-resource" disabled={formik.isSubmitting || isImportRobot} - checked={formik?.values?.gpuEnabledForCloudInstance} + checked={formik?.values?.resources.gpu.enabledForCloudInstance} onChange={(e: any) => - formik.setFieldValue("gpuEnabledForCloudInstance", e) + formik.setFieldValue("resources.gpu.enabledForCloudInstance", e) } /> ); diff --git a/src/components/CFRobotName/CFRobotName.tsx b/src/components/CFRobotName/CFRobotName.tsx index c82e2a5e..7a298c65 100644 --- a/src/components/CFRobotName/CFRobotName.tsx +++ b/src/components/CFRobotName/CFRobotName.tsx @@ -17,11 +17,11 @@ export default function CFRobotName({ dataTut="create-robot-step1-name" labelName="Robot Name:" labelInfoTip="Type a new robot name." - inputProps={formik.getFieldProps("robotName")} + inputProps={formik.getFieldProps("details.name")} disabled={formik.isSubmitting || isImportRobot} inputHoverText="You can't change robot name because this robot is created before." - inputError={formik.errors.robotName} - inputTouched={formik.touched.robotName} + inputError={formik.errors.details?.name} + inputTouched={formik.touched.details?.name} /> ); } diff --git a/src/components/CFRosDistros/CFRosDistros.tsx b/src/components/CFRosDistros/CFRosDistros.tsx index feb9073f..79769fbe 100644 --- a/src/components/CFRosDistros/CFRosDistros.tsx +++ b/src/components/CFRosDistros/CFRosDistros.tsx @@ -1,11 +1,11 @@ -import React, { ReactElement } from "react"; -import InfoTip from "../InfoTip/InfoTip"; -import { toast } from "sonner"; import { stringSlugify } from "../../functions/GeneralFunctions"; -import { MdVerified } from "react-icons/md"; -import InputError from "../InputError/InputError"; import { IDetails } from "../../interfaces/robotInterfaces"; +import InputError from "../InputError/InputError"; +import { MdVerified } from "react-icons/md"; +import InfoTip from "../InfoTip/InfoTip"; +import { ReactElement } from "react"; import { FormikProps } from "formik"; +import { toast } from "sonner"; interface ICFRosDistros { formik: FormikProps; @@ -17,20 +17,16 @@ export default function CFRosDistros({ isImportRobot, }: ICFRosDistros): ReactElement { function handleRosDistroFilter(item: string) { + const { rosDistros } = formik.values.services.ros; + if (item === "HUMBLE" || item === "IRON") { - if ( - formik.values.rosDistros?.includes("GALACTIC") || - formik.values.rosDistros?.includes("FOXY") - ) { + if (rosDistros?.includes("GALACTIC") || rosDistros?.includes("FOXY")) { return 1; } return 0; } else { - if ( - formik.values.rosDistros?.includes("IRON") || - formik.values.rosDistros?.includes("HUMBLE") - ) { + if (rosDistros?.includes("IRON") || rosDistros?.includes("HUMBLE")) { return 1; } @@ -43,7 +39,7 @@ export default function CFRosDistros({ data-tut="create-robot-step1-ros-distrobutions" className="flex flex-col gap-2" > -

+
Ros Distrobutions: ros !== item), ); } else if ( @@ -96,7 +92,10 @@ export default function CFRosDistros({ "You can't select Galactic or Foxy with Humble or Iron", ); } else { - formik.setFieldValue("rosDistros", [...rosDistros, item]); + formik.setFieldValue("services.ros.rosDistros", [ + ...rosDistros, + item, + ]); } }} > @@ -109,18 +108,18 @@ export default function CFRosDistros({ filter: `grayscale(${handleRosDistroFilter(item)})`, }} /> - + ROS2{" "} {item === "FOXY" ? "Foxy" : item === "HUMBLE" - ? "Humble" - : item === "GALACTIC" - ? "Galactic" - : item === "IRON" && "Iron"} + ? "Humble" + : item === "GALACTIC" + ? "Galactic" + : item === "IRON" && "Iron"}
- {formik.values.rosDistros?.includes(item) && ( + {formik.values.services.ros.rosDistros?.includes(item) && (
diff --git a/src/components/CreateRobotTypes/CreateRobotTypes.tsx b/src/components/CreateRobotTypes/CreateRobotTypes.tsx index 112f98ca..0680e6cf 100644 --- a/src/components/CreateRobotTypes/CreateRobotTypes.tsx +++ b/src/components/CreateRobotTypes/CreateRobotTypes.tsx @@ -1,13 +1,12 @@ -import React, { Fragment, ReactElement, useEffect, useState } from "react"; -import { getPhysicalInstances } from "../../toolkit/InstanceSlice"; +import { Fragment, ReactElement, useEffect, useState } from "react"; +import { IDetails } from "../../interfaces/robotInterfaces"; import SidebarInfo from "../SidebarInfo/SidebarInfo"; -import { useAppDispatch } from "../../hooks/redux"; +import useFunctions from "../../hooks/useFunctions"; import InputError from "../InputError/InputError"; import useMain from "../../hooks/useMain"; import InfoTip from "../InfoTip/InfoTip"; -import { toast } from "sonner"; -import { IDetails } from "../../interfaces/robotInterfaces"; import { FormikProps } from "formik"; +import { toast } from "sonner"; interface ICreateRobotTypes { formik: FormikProps; @@ -20,38 +19,44 @@ export default function CreateRobotTypes({ }: ICreateRobotTypes): ReactElement { const [responsePhysicalInstances, setResponsePhysicalInstances] = useState(undefined); - const dispatch = useAppDispatch(); const { selectedState } = useMain(); + const { getPhysicalInstances } = useFunctions(); + useEffect(() => { - !formik.values?.isVirtualRobot && - !Array.isArray(responsePhysicalInstances) && - dispatch( - getPhysicalInstances({ - organizationId: selectedState?.organization?.organizationId, - roboticsCloudName: selectedState?.roboticsCloud?.name, - instanceId: selectedState?.instance?.instanceId, - region: selectedState?.instance?.region, - }), - ).then((response: any) => { - setResponsePhysicalInstances( - response?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchPhysicalInstances?.filter( - (instance: any) => - instance?.federationPhase === "Connected" && - instance?.multicastPhase === "Connected" && - instance?.phase === "Connected", - ) || [], - ); - }); + if (!responsePhysicalInstances && !formik.values.details.isVirtualRobot) { + handleGetPhysicalInstances(); + } + + responsePhysicalInstances?.filter( + (instance: any) => + instance?.federationPhase !== "Connected" || + instance?.multicastPhase !== "Connected" || + instance?.phase !== "Connected", + )?.length && + setResponsePhysicalInstances( + responsePhysicalInstances?.filter( + (instance: any) => + instance?.federationPhase === "Connected" && + instance?.multicastPhase === "Connected" && + instance?.phase === "Connected", + ) || [], + ); + // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - dispatch, - formik.values.isVirtualRobot, - selectedState?.instance?.instanceId, - selectedState?.instance?.region, - selectedState?.organization?.organizationId, - selectedState?.roboticsCloud?.name, - ]); + }, [responsePhysicalInstances, formik.values.details.isVirtualRobot]); + + function handleGetPhysicalInstances() { + getPhysicalInstances( + { + organizationId: selectedState?.organization?.organizationId!, + roboticsCloudName: selectedState?.roboticsCloud?.name!, + instanceId: selectedState?.instance?.instanceId!, + region: selectedState?.instance?.region!, + }, + { setResponse: setResponsePhysicalInstances }, + ); + } return ( @@ -59,7 +64,7 @@ export default function CreateRobotTypes({ data-tut="create-robot-step1-type" className="flex flex-col gap-2 pb-2" > -
+
Robot Type:
@@ -83,7 +88,8 @@ export default function CreateRobotTypes({ } key={index} className={`relative flex w-full items-center justify-center gap-1 rounded border-2 p-3 transition-all duration-300 ${ - formik.values?.isVirtualRobot === robotType?.isVirtualRobot + formik.values.details.isVirtualRobot === + robotType?.isVirtualRobot ? robotType?.disabled ? "border-primary-300" : "border-primary-400 shadow" @@ -95,13 +101,13 @@ export default function CreateRobotTypes({ robotType?.disabled ? toast.error("You can't change robot type in update mode") : formik.setFieldValue( - "isVirtualRobot", + "details.isVirtualRobot", robotType?.isVirtualRobot, ); }} >
- + {robotType?.name}
@@ -111,11 +117,11 @@ export default function CreateRobotTypes({
- {!formik.values?.isVirtualRobot && + {!formik.values?.details.isVirtualRobot && Array.isArray(responsePhysicalInstances) ? ( responsePhysicalInstances?.length > 0 ? (
-
+
Physical Instances:
@@ -125,7 +131,8 @@ export default function CreateRobotTypes({
- + {instance?.name}
@@ -152,7 +159,7 @@ export default function CreateRobotTypes({ )}
@@ -162,7 +169,7 @@ export default function CreateRobotTypes({
) ) : ( - !formik.values?.isVirtualRobot && ( + !formik.values.details.isVirtualRobot && (
- - + +
diff --git a/src/components/RobotHeaderTabs/RobotHeaderTabs.tsx b/src/components/EnvironmentHeaderTabs/EnvironmentHeaderTabs.tsx similarity index 79% rename from src/components/RobotHeaderTabs/RobotHeaderTabs.tsx rename to src/components/EnvironmentHeaderTabs/EnvironmentHeaderTabs.tsx index 3886ac20..06353fd6 100644 --- a/src/components/RobotHeaderTabs/RobotHeaderTabs.tsx +++ b/src/components/EnvironmentHeaderTabs/EnvironmentHeaderTabs.tsx @@ -10,7 +10,7 @@ import { FaPython } from "react-icons/fa"; import useCreateRobot from "../../hooks/useCreateRobot"; import { useAppSelector } from "../../hooks/redux"; -export default function RobotHeaderTabs(): ReactElement { +export default function EnvironmentHeaderTabs(): ReactElement { const { responseRobot, isSettedCookie, isRobotReady } = useRobot(); const { applicationMode } = useAppSelector((state) => state.user); @@ -29,13 +29,12 @@ export default function RobotHeaderTabs(): ReactElement { isLoading: !responseRobot || !( - responseRobot?.bridgeEnabled && - responseRobot?.bridgeIngressEndpoint && + robotData.step1.services.ros.isEnabled && + robotData.step1.services.ros.socketEndpoint && isSettedCookie ), isHidden: - // applicationMode || (responseRobot && !responseRobot?.bridgeEnabled), - true, + applicationMode || (responseRobot && !responseRobot?.bridgeEnabled), }, { name: "Teleoperation", @@ -43,8 +42,8 @@ export default function RobotHeaderTabs(): ReactElement { isLoading: !responseRobot || !( - responseRobot?.bridgeEnabled && - responseRobot?.bridgeIngressEndpoint && + robotData.step1.services.ros.isEnabled && + robotData.step1.services.ros.socketEndpoint && isSettedCookie ), isHidden: @@ -56,8 +55,8 @@ export default function RobotHeaderTabs(): ReactElement { isLoading: !responseRobot || !( - responseRobot?.bridgeEnabled && - responseRobot?.bridgeIngressEndpoint && + robotData.step1.services.ros.isEnabled && + robotData.step1.services.ros.socketEndpoint && isSettedCookie ), isHidden: @@ -69,10 +68,10 @@ export default function RobotHeaderTabs(): ReactElement { isLoading: !responseRobot || !( - responseRobot?.ideEnabled && - responseRobot?.ideIngressEndpoint && - responseRobot?.vdiEnabled && - responseRobot?.vdiIngressEndpoint && + robotData.step1.services.ide.isEnabled && + robotData.step1.services.ide.httpsEndpoint && + robotData.step1.services.vdi.isEnabled && + robotData.step1.services.vdi.socketEndpoint && isSettedCookie ) || !isRobotReady, @@ -85,8 +84,8 @@ export default function RobotHeaderTabs(): ReactElement { isLoading: !responseRobot || !( - responseRobot?.ideEnabled && - responseRobot?.ideIngressEndpoint && + robotData.step1.services.ide.isEnabled && + robotData.step1.services.ide.httpsEndpoint && isSettedCookie ), isHidden: false, diff --git a/src/components/RobotResource/RobotResource.tsx b/src/components/EnvironmentResource/EnvironmentResource.tsx similarity index 97% rename from src/components/RobotResource/RobotResource.tsx rename to src/components/EnvironmentResource/EnvironmentResource.tsx index 7e4bcc15..63789002 100644 --- a/src/components/RobotResource/RobotResource.tsx +++ b/src/components/EnvironmentResource/EnvironmentResource.tsx @@ -6,7 +6,7 @@ import Skeleton from "../Skeleton/Skeleton"; import { FaMemory } from "react-icons/fa"; import { useAppSelector } from "../../hooks/redux"; -export default function RobotResource(): ReactElement { +export default function EnvironmentResource(): ReactElement { const { robotData } = useCreateRobot(); const { applicationMode } = useAppSelector((state) => state.user); diff --git a/src/components/Robot3D/Robot3D.tsx b/src/components/Robot3D/Robot3D.tsx index 796f09b9..1dc36f31 100644 --- a/src/components/Robot3D/Robot3D.tsx +++ b/src/components/Robot3D/Robot3D.tsx @@ -12,7 +12,7 @@ export default function Robot3D() { useMemo(() => { const loader = new GLTFLoader(); loader.load( - "/models/robot.glb", + "https://github.com/robolaunch/ui-models/raw/main/robot.glb", (gltf: any) => { console.log("Robot model loaded:", gltf); setRobotModel(gltf.scene); diff --git a/src/contexts/CreateRobotContext.tsx b/src/contexts/CreateRobotContext.tsx index 8bdbfb39..8c35dee6 100644 --- a/src/contexts/CreateRobotContext.tsx +++ b/src/contexts/CreateRobotContext.tsx @@ -75,6 +75,8 @@ export default ({ children }: any) => { isEnabled: true, socketEndpoint: "", rosDistros: [], + podName: "", + log: "", }, vdi: { isEnabled: true, @@ -83,7 +85,7 @@ export default ({ children }: any) => { customPorts: [], gpuAllocation: 0, podName: "", - sessionCount: 0, + sessionCount: 2, log: "", }, ide: { @@ -97,6 +99,10 @@ export default ({ children }: any) => { podName: "", log: "", }, + physicalIde: { + isEnabled: true, + httpsEndpoint: "", + }, jupyterNotebook: { isEnabled: false, httpsEndpoint: "", @@ -131,6 +137,12 @@ export default ({ children }: any) => { configureWorkspace: false, isDevelopmentMode: applicationMode, }, + + clusters: { + environment: [], + build: [], + launch: [], + }, }, step2: { configureWorkspace: false, diff --git a/src/contexts/FunctionsContext.tsx b/src/contexts/FunctionsContext.tsx index 1490400b..ae84ed45 100644 --- a/src/contexts/FunctionsContext.tsx +++ b/src/contexts/FunctionsContext.tsx @@ -752,9 +752,9 @@ export default ({ children }: any) => { fleetName: values?.fleetName, robotName: values?.robotName, }), - ).then((responseFederatedRobot: any) => { + ).then((responseRobot: any) => { if ( - responseFederatedRobot?.payload?.data?.[0].roboticsClouds?.[0] + responseRobot?.payload?.data?.[0].roboticsClouds?.[0] ?.cloudInstances?.[0]?.robolaunchFederatedRobots ) { parameters?.setRobotData && @@ -763,45 +763,64 @@ export default ({ children }: any) => { ...prevState, step1: { ...prevState.step1, - organization: { - id: selectedState?.organization?.organizationId, - name: orgNameViewer( - selectedState?.organization?.organizationName!, - ), - }, - region: { - name: selectedState?.instance?.region, + + details: { + name: responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0]?.name, + isVirtualRobot: responseRobot?.payload?.data[0] + ?.roboticsClouds[0]?.cloudInstances[0] + ?.robolaunchFederatedRobots[0]?.physicalInstance + ? false + : true, + isDevelopmentMode: false, + configureWorkspace: false, }, - cloudInstance: { - id: selectedState?.instance?.instanceId, - name: selectedState?.instance?.name, - resources: { - cpu: { - coreTotal: - selectedState?.instance?.cloudInstanceResource - ?.cpuTotal, - }, - gpu: { - coreTotal: - selectedState?.instance?.cloudInstanceResource?.gpuUsage - ?.map( - (gpu: any) => - Number(gpu?.allocated) + Number(gpu?.capacity), - ) - .reduce((a: any, b: any) => a + b, 0), - }, - memory: { - capacityTotal: - selectedState?.instance?.cloudInstanceResource - ?.memoryTotal, - }, - storage: { - capacityTotal: 0, + tree: { + organization: { + id: selectedState?.organization?.organizationId, + name: orgNameViewer( + selectedState?.organization?.organizationName!, + ), + }, + region: { + name: selectedState?.instance?.region, + }, + cloudInstance: { + id: selectedState?.instance?.instanceId, + name: selectedState?.instance?.name, + resources: { + cpu: { + coreTotal: + selectedState?.instance?.cloudInstanceResource + ?.cpuTotal, + }, + gpu: { + coreTotal: + selectedState?.instance?.cloudInstanceResource?.gpuUsage + ?.map( + (gpu: any) => + Number(gpu?.allocated) + Number(gpu?.capacity), + ) + .reduce((a: any, b: any) => a + b, 0), + }, + memory: { + capacityTotal: + selectedState?.instance?.cloudInstanceResource + ?.memoryTotal, + }, + storage: { + capacityTotal: 0, + }, }, }, - }, - namespace: { - name: selectedState?.fleet?.name, + physicalInstance: { + name: responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.physicalInstance, + }, + namespace: { + name: selectedState?.fleet?.name, + }, }, resources: { @@ -809,22 +828,27 @@ export default ({ children }: any) => { allocatedCore: 0, }, gpu: { + enabledForCloudInstance: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.vdiGpuResource > 0 + ? true + : false, allocatedCore: Number( - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots[0].ideGpuResource || 0, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + .ideGpuResource || 0, ) + Number( - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots[0].notebookGpuResource || - 0, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + .notebookGpuResource || 0, ) + Number( - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots[0].vdiGpuResource || 0, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + .vdiGpuResource || 0, ), }, memory: { @@ -832,169 +856,210 @@ export default ({ children }: any) => { }, storage: { allocatedCapacity: Number( - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots[0].storageAmount || 0, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + .storageAmount || 0, ), }, }, services: { + ros: { + isEnabled: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.bridgeEnabled, + rosDistros: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.distributions, + socketEndpoint: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.bridgeIngressEndpoint, + podName: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.bridgePodName, + log: responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.bridgeApplicationLog, + }, vdi: { isEnabled: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.vdiEnabled, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.vdiEnabled, socketEndpoint: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.vdiIngressEndpoint, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.vdiIngressEndpoint, fileManagerEndpoint: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0] + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] ?.vdiFileBrowserIngressEndpoint, + customPorts: + responseRobot?.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], + }; + }), gpuAllocation: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.vdiGpuResource, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.vdiGpuResource, podName: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.vdiPodName, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.vdiPodName, sessionCount: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.vdiSessionCount, - log: responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.vdiApplicationLog, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.vdiSessionCount, + log: responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.vdiApplicationLog, }, ide: { isEnabled: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.ideEnabled, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.ideEnabled, httpsEndpoint: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.ideIngressEndpoint, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.ideIngressEndpoint, fileManagerEndpoint: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0] + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] ?.ideFileBrowserIngressEndpoint, + customPorts: + responseRobot?.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], + }; + }), gpuAllocation: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.ideGpuResource, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.ideGpuResource, + maxGpuAllocation: 0, gpuModelName: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.ideGpuModelName, + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.ideGpuModelName, podName: - responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.idePodName, - log: responseFederatedRobot?.payload?.data[0] + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.idePodName, + log: responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots?.[0] + ?.ideApplicationLog, + }, + physicalIde: { + isEnabled: responseRobot?.payload?.data[0] ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots?.[0]?.ideApplicationLog, + ?.robolaunchFederatedRobots[0]?.physicalIdeIngressEndpoint + ? true + : false, + httpsEndpoint: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.physicalIdeIngressEndpoint, + }, + jupyterNotebook: { + isEnabled: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.notebookEnabled, + httpsEndpoint: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.notebookIngressEndpoint, + fileManagerEndpoint: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.notebookFileBrowserIngressEndpoint, + gpuAllocation: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.notebookGpuResource, + customPorts: + responseRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchFederatedRobots[0]?.notebookCustomPorts + ?.split("/") + ?.map((item: string) => { + return { + name: item?.split("-")[0], + port: item?.split("-")[1].split(":")[1], + backendPort: item?.split("-")[1].split(":")[0], + }; + }), + podName: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.notebookPodName, + log: responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.notebookApplicationLog, }, }, - name: responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots[0]?.name, - isVirtualRobot: responseFederatedRobot?.payload?.data[0] - ?.roboticsClouds[0]?.cloudInstances[0] - ?.robolaunchFederatedRobots[0]?.physicalInstance - ? false - : true, - physicalInstanceName: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.physicalInstance, - robotStorage: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.storageAmount, - isEnabledIde: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.ideEnabled, - isEnabledROS2Bridge: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.bridgeEnabled, - remoteDesktop: { - isEnabled: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] + directories: { + permittedDirectories: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.permittedDirectories, + persistentDirectories: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.persistentDirectories, + hostDirectories: + responseRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchFederatedRobots[0]?.hostDirectories + ?.split(",") + ?.map((item: string) => { + return { + hostDirectory: item?.split(":")[0], + mountPath: item?.split(":")[1], + }; + }), + }, + applicationConfig: { + domainName: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.domainName, + application: + responseRobot?.payload?.data[0]?.roboticsClouds[0] + ?.cloudInstances[0]?.robolaunchFederatedRobots[0] + ?.application, + devspace: + responseRobot?.payload?.data[0]?.roboticsClouds[0] ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.vdiEnabled, - sessionCount: - responseFederatedRobot?.payload?.data?.[0] - ?.roboticsClouds?.[0]?.cloudInstances?.[0] - ?.robolaunchFederatedRobots?.[0]?.vdiSessionCount, + ?.devspace, + }, + + clusters: { + environment: + responseRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchFederatedRobots[0].robotClusters?.map( + (cluster: { name: string; robotStatus: string }) => { + return { + name: cluster.name, + status: cluster.robotStatus, + }; + }, + ), }, - rosDistros: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.distributions, - gpuEnabledForCloudInstance: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.vdiGpuResource > 0 - ? true - : false, - isDevelopmentMode: false, - vdiPodName: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] - ?.cloudInstances[0]?.robolaunchFederatedRobots[0] - ?.vdiPodName, - 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]?.robolaunchFederatedRobots?.[0]?.ideCustomPorts - ?.split("/") - ?.map((item: string) => { - return { - 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]?.robolaunchFederatedRobots?.[0]?.vdiCustomPorts - ?.split("/") - ?.map((item: string) => { - return { - name: item?.split("-")?.[0], - port: item?.split("-")?.[1].split(":")?.[1], - backendPort: item?.split("-")?.[1].split(":")?.[0], - }; - }), }, step2: { workspaces: - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0] + responseRobot?.payload?.data[0]?.roboticsClouds[0] ?.cloudInstances[0]?.robolaunchFederatedRobots[0] ?.robotWorkspaces, }, @@ -1003,7 +1068,7 @@ export default ({ children }: any) => { parameters?.setResponse && parameters?.setResponse( - responseFederatedRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchFederatedRobots?.find( + responseRobot?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchFederatedRobots?.find( (robot: any) => robot?.name === values?.robotName, ) || {}, ); @@ -1036,6 +1101,13 @@ export default ({ children }: any) => { setRobotData((prevState: any) => { return { ...prevState, + step1: { + ...prevState.step1, + clusters: { + ...prevState.step1.clusters, + build: [], + }, + }, step3: { buildManagerName: responseRobotBuildManager?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.robolaunchFederatedRobots[0]?.buildManagerName?.split( @@ -1435,6 +1507,18 @@ export default ({ children }: any) => { responseEnvironment?.payload?.data[0]?.roboticsClouds[0] ?.cloudInstances[0]?.environments[0]?.devspace, }, + + clusters: { + environment: + responseEnvironment?.payload?.data[0]?.roboticsClouds[0]?.cloudInstances[0]?.environments[0].robotClusters?.map( + (cluster: { name: string; robotStatus: string }) => { + return { + name: cluster.name, + status: cluster.robotStatus, + }; + }, + ), + }, }, step2: { workspaces: diff --git a/src/contexts/RobotContext.tsx b/src/contexts/RobotContext.tsx index ad9582d9..532a8d37 100644 --- a/src/contexts/RobotContext.tsx +++ b/src/contexts/RobotContext.tsx @@ -1,10 +1,11 @@ import { useEffect, createContext, useState, useReducer } from "react"; -import { IrobotTab } from "../interfaces/robotInterfaces"; +import { IEnvironmentCluster, IrobotTab } from "../interfaces/robotInterfaces"; import useFunctions from "../hooks/useFunctions"; import { useParams } from "react-router-dom"; import useMain from "../hooks/useMain"; import ROSLIB from "roslib"; import { useAppSelector } from "../hooks/redux"; +import useCreateRobot from "../hooks/useCreateRobot"; export const RobotContext: any = createContext(null); @@ -39,6 +40,8 @@ export default ({ children }: any) => { const [responseLaunchManagers, setResponseLaunchManagers] = useState(undefined); + const { robotData } = useCreateRobot(); + const [iFrameId, setIFrameId] = useState(0); const [isRobotReady, setIsRobotReady] = useState(false); const [isSettedCookie, setIsSettedCookie] = useState( @@ -107,12 +110,13 @@ export default ({ children }: any) => { handleGetPhysicalInstance(); } - const timerResponseRobot = setInterval(() => { + const timerEnvironment = setInterval(() => { if ( !sidebarState?.isOpen && - Array.isArray(responseRobot?.robotClusters) && - responseRobot?.robotClusters?.filter( - (robot: any) => robot?.robotStatus !== "EnvironmentReady", + Array.isArray(robotData.step1.clusters.environment) && + robotData.step1.clusters.environment?.filter( + (cluster: IEnvironmentCluster) => + cluster?.status !== "EnvironmentReady", )?.length ) { applicationMode ? handleGetEnvironment() : handleGetRobot(); @@ -144,26 +148,31 @@ export default ({ children }: any) => { }, 10000); if (sidebarState?.isOpen) { - clearInterval(timerResponseRobot); + clearInterval(timerEnvironment); clearInterval(timerResponseBuildManager); clearInterval(timerResponseLaunchManagers); } return () => { - clearInterval(timerResponseRobot); + clearInterval(timerEnvironment); clearInterval(timerResponseBuildManager); clearInterval(timerResponseLaunchManagers); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pagesState, responseRobot, responseBuildManager, responseLaunchManagers]); + }, [ + pagesState, + responseRobot, + responseBuildManager, + responseLaunchManagers, + robotData, + ]); // Main Functions // isRobotReady useEffect(() => { - const isWorkspaceReady = - responseRobot?.robotClusters?.filter( - (robot: any) => robot?.robotStatus !== "EnvironmentReady", + const isEnvironmentReady = + robotData.step1.clusters.environment?.filter( + (robot: any) => robot?.status !== "EnvironmentReady", )?.length === 0 ? true : false; @@ -188,13 +197,18 @@ export default ({ children }: any) => { ? true : false; - if (isWorkspaceReady || isBuildManagerReady || isLaunchManagerReady) { + if (isEnvironmentReady || isBuildManagerReady || isLaunchManagerReady) { setIsRobotReady(true); } else { setIFrameId((prevState) => prevState + 1); setIsRobotReady(false); } - }, [responseRobot, responseBuildManager, responseLaunchManagers]); + }, [ + responseRobot, + responseBuildManager, + responseLaunchManagers, + robotData.step1.clusters.environment, + ]); // isRobotReady // Cookies Reloader diff --git a/src/interfaces/robotInterfaces.ts b/src/interfaces/robotInterfaces.ts index 8da23594..ad0184eb 100644 --- a/src/interfaces/robotInterfaces.ts +++ b/src/interfaces/robotInterfaces.ts @@ -204,6 +204,8 @@ export interface IDetails { isEnabled: boolean; rosDistros: any[]; socketEndpoint: string; + podName: string; + log: string; }; vdi: { isEnabled: boolean; @@ -226,6 +228,10 @@ export interface IDetails { podName: string; log: string; }; + physicalIde: { + isEnabled: boolean; + httpsEndpoint: string; + }; jupyterNotebook: { isEnabled: boolean; httpsEndpoint: string; @@ -253,6 +259,16 @@ export interface IDetails { version: string; }; }; + clusters: { + environment: IEnvironmentCluster[]; + build: any[]; + launch: any[]; + }; +} + +export interface IEnvironmentCluster { + name: string; + status: string; } export interface IhostDirectories { diff --git a/src/pages/EnvironmentPage/CodeEditor/CodeEditor.tsx b/src/pages/EnvironmentPage/CodeEditor/CodeEditor.tsx index cedd20cc..6da443d6 100644 --- a/src/pages/EnvironmentPage/CodeEditor/CodeEditor.tsx +++ b/src/pages/EnvironmentPage/CodeEditor/CodeEditor.tsx @@ -1,18 +1,18 @@ import CodeEditorSwitcher from "../../../components/CodeEditorSwitcher/CodeEditorSwitcher"; import { FullScreen, useFullScreenHandle } from "react-full-screen"; import ControlBar from "../../../components/ControlBar/ControlBar"; +import useCreateRobot from "../../../hooks/useCreateRobot"; import { Fragment, ReactElement, useState } from "react"; import { useAppSelector } from "../../../hooks/redux"; import Card from "../../../components/Card/Card"; import useRobot from "../../../hooks/useRobot"; -import useCreateRobot from "../../../hooks/useCreateRobot"; export default function CodeEditor(): ReactElement { const [activeTabCodeEditor, setActiveTabCodeEditor] = useState<1 | 2>(1); const handleFullScreen = useFullScreenHandle(); - const { activeTab, responseRobot, isRobotReady, isSettedCookie } = useRobot(); + const { activeTab, isRobotReady, isSettedCookie } = useRobot(); const { urls } = useAppSelector((state) => state.robot); @@ -26,7 +26,7 @@ export default function CodeEditor(): ReactElement { : "absolute -top-[9999px]" } > - {responseRobot?.physicalIdeIngressEndpoint && ( + {robotData.step1.services.physicalIde?.isEnabled && ( diff --git a/src/pages/EnvironmentPage/TaskManagement/BarcodeManagement.tsx b/src/pages/EnvironmentPage/TaskManagement/BarcodeManagement.tsx index f74d361f..7faa9029 100644 --- a/src/pages/EnvironmentPage/TaskManagement/BarcodeManagement.tsx +++ b/src/pages/EnvironmentPage/TaskManagement/BarcodeManagement.tsx @@ -1,4 +1,4 @@ -import React, { Fragment, ReactElement, useState } from "react"; +import { Fragment, ReactElement, useState } from "react"; import CardLayout from "../../../layouts/CardLayout"; import BarcodeManagement3D from "./BarcodeManagement3D"; import { FullScreen, useFullScreenHandle } from "react-full-screen"; diff --git a/src/validations/RobotsValidations.ts b/src/validations/RobotsValidations.ts index 6a4a956b..0087cf67 100644 --- a/src/validations/RobotsValidations.ts +++ b/src/validations/RobotsValidations.ts @@ -7,64 +7,78 @@ export const ImportRobotSetSidebarState = Yup.object().shape({ }); export const CFRobotStep1Validations = Yup.object().shape({ - robotName: Yup.string() - .required("Robot name is required.") - .min(3, "Minimum 3 characters.") - .max(16, "Maximum 16 characters.") - .lowercase("Must be lowercase.") - .matches( - /^[a-z0-9]+(-[a-z0-9]+)*$/, - "Must be lowercase with hyphen (-) only in the middle.", - ), - physicalInstanceName: Yup.string().when("isVirtualRobot", { - is: false, - then: Yup.string().required("Physical Instance is required"), - otherwise: Yup.string().notRequired(), + details: Yup.object().shape({ + name: Yup.string() + .required("Robot name is required.") + .min(3, "Minimum 3 characters.") + .max(16, "Maximum 16 characters.") + .lowercase("Must be lowercase.") + .matches( + /^[a-z0-9]+(-[a-z0-9]+)*$/, + "Must be lowercase with hyphen (-) only in the middle.", + ), }), - rosDistros: Yup.array().min(1, "At least one ROS Distro is required"), - remoteDesktop: Yup.object().shape({ - isEnabled: Yup.boolean().notRequired(), - sessionCount: Yup.number().when("remoteDesktop.isEnabled", { - is: true, - then: Yup.number().required("Session Count is required"), - otherwise: Yup.number().notRequired(), + + tree: Yup.object().when("details.isVirtualRobot", { + is: false, + then: Yup.object().shape({ + physicalInstance: Yup.object().shape({ + name: Yup.string().required("Physical Instance is required"), + }), }), }), - 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."), + + services: Yup.object().shape({ + ros: Yup.object().shape({ + rosDistros: Yup.array().min(1, "At least one ROS Distro is required"), + + vdi: Yup.object().shape({ + sessionCount: Yup.number().min(2, "At least one session is required"), + customPorts: 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."), + }), + ), + }), + ide: Yup.object().shape({ + customPorts: 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."), + }), + ), + }), }), - ), + }), + directories: Yup.object().shape({ + 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 '/'"), + }), + ), + }), }); export const CFRobotStep2Validations = Yup.object().shape({