From dddb0cb870597e86c3fd44fb2eeef0daa32ec43d Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Fri, 23 Feb 2024 16:19:34 +0530 Subject: [PATCH 1/3] adds tag and repo in create app --- Taskfile.yaml | 4 + .../console/page-components/app-states.tsx | 15 + .../app+/$app+/settings+/compute/route.tsx | 110 ++- .../$environment+/new-app/app-compute.tsx | 728 +++++++++++------- 4 files changed, 555 insertions(+), 302 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index cd33c7618..0b8d8576c 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -41,6 +41,10 @@ tasks: "vision") URL_SUFFIX="-vision" ;; + + "piyush") + URL_SUFFIX="-piyush" + ;; *) URL_SUFFIX="" diff --git a/src/apps/console/page-components/app-states.tsx b/src/apps/console/page-components/app-states.tsx index ddb844e49..e78443f2f 100644 --- a/src/apps/console/page-components/app-states.tsx +++ b/src/apps/console/page-components/app-states.tsx @@ -6,6 +6,8 @@ import { AppIn, Github__Com___Kloudlite___Operator___Apis___Crds___V1__AppContainerIn as AppSpecContainersIn, } from '~/root/src/generated/gql/server'; +import {useMapper} from "~/components/utils"; +import {parseNodes} from "~/console/server/r-utils/common"; const defaultApp: AppIn = { metadata: { @@ -185,6 +187,18 @@ export const useAppState = () => { }); }; + type IparseNodes = { + edges: Array<{ node: any }>; + }; + + const getRepoMapper = (resources: IparseNodes | undefined) => { + return useMapper(parseNodes(resources), (val) => ({ + label: val.name, + value: val.name, + accName: val.accountName + })) + } + return { resetState, completePages, @@ -203,6 +217,7 @@ export const useAppState = () => { activeContIndex: activeContIndex || 0, services: app.spec.services || [], setServices, + getRepoMapper, }; }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx index 4811a8f4a..221b13789 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { NumberInput, TextInput } from '~/components/atoms/input'; import Slider from '~/components/atoms/slider'; import { useAppState } from '~/console/page-components/app-states'; @@ -14,6 +14,10 @@ import Wrapper from '~/console/components/wrapper'; import { useUnsavedChanges } from '~/root/lib/client/hooks/use-unsaved-changes'; import { Button } from '~/components/atoms/button'; import { plans } from '../../../../new-app/datas'; +import useCustomSwr from "~/lib/client/hooks/use-custom-swr"; +import {useConsoleApi} from "~/console/server/gql/api-provider"; +import {useMapper} from "~/components/utils"; +import {parseNodes} from "~/console/server/r-utils/common"; const valueRender = ({ label, @@ -37,9 +41,24 @@ const valueRender = ({ }; const SettingCompute = () => { - const { app, setApp, getContainer, activeContIndex } = useAppState(); + const { app, setApp, getContainer, activeContIndex, getRepoMapper } = useAppState(); const { setPerformAction, hasChanges, loading } = useUnsavedChanges(); + const api = useConsoleApi() + + const [selectedRepo, setSelectedRepo] = useState(""); + const [imageDigests, setImageDigests] = useState<{ tags: string[], digest: string }[]>([]) + const [accountName, setAccountName] = useState("") + + + const { + data, + isLoading: repoLoading, + error: repoLoadingError, + } = useCustomSwr('/repos', async () => { + return api.listRepo({}); + }); + const { values, errors, handleChange, submit, resetValues } = useForm({ initialValues: { imageUrl: getContainer(0)?.image || '', @@ -47,6 +66,10 @@ const SettingCompute = () => { cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || 1, + repoName: '', + repoImageTag: '', + repoImageUrl: '', + cpu: parseValue( app.spec.containers[activeContIndex]?.resourceCpu?.max, 250 @@ -132,6 +155,29 @@ const SettingCompute = () => { // ); // }, [values.cpuMode, values.selectedPlan]); + // const repository = useMapper(parseNodes(data), (val) => ({ + // label: val.name, + // value: val.name, + // accName: val.accountName + // })); + + const repos = getRepoMapper(data) + + + useEffect(() => { + (async () => { + const {data} = await api.listDigest({repoName: selectedRepo}) + + const digests = data.edges.map(item => ({ + digest: item.node.digest, + tags: item.node.tags, + })) + + setImageDigests(digests); + })() + + }, [selectedRepo]) + useEffect(() => { submit(); }, [values]); @@ -167,14 +213,14 @@ const SettingCompute = () => { >
- } - size="lg" - value={values.imageUrl} - onChange={handleChange('imageUrl')} - error={!!errors.imageUrl} - message={errors.imageUrl} + label={ + + } + size="lg" + value={values.imageUrl} + onChange={handleChange('imageUrl')} + error={!!errors.imageUrl} + message={errors.imageUrl} /> {/* { // message={errors.pullSecret} // onChange={handleChange('pullSecret')} /> */} + +
+ OR +
+ + { + handleChange('repoImageTag')(dummyEvent(val.value)); + handleChange('repoImageUrl')(dummyEvent(`registry.kloudlite.io/${accountName}/${values.repoName}:${val.value}`)) + }} + options={async () => [...new Set(imageDigests.map(item => item.tags).flat())].map(item => ({ + label: item, + value: item, + }))} + error={!!errors.repoImageTag} + message={errors.repoImageTag} + /> +
{/*
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx index cf71f78d3..30de2d47e 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx @@ -1,302 +1,429 @@ -import { ArrowLeft, ArrowRight } from '@jengaicons/react'; -import { Button } from '~/components/atoms/button'; -import { NumberInput, TextInput } from '~/components/atoms/input'; +import {ArrowLeft, ArrowRight} from '@jengaicons/react'; +import {Button} from '~/components/atoms/button'; +import {NumberInput, TextInput} from '~/components/atoms/input'; import Slider from '~/components/atoms/slider'; -import { useAppState } from '~/console/page-components/app-states'; -import { keyconstants } from '~/console/server/r-utils/key-constants'; -import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; +import {useAppState} from '~/console/page-components/app-states'; +import {keyconstants} from '~/console/server/r-utils/key-constants'; +import useForm, {dummyEvent} from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; -import { InfoLabel } from '~/console/components/commons'; -import { FadeIn, parseValue } from '~/console/page-components/util'; +import {InfoLabel} from '~/console/components/commons'; +import {FadeIn, parseValue} from '~/console/page-components/util'; import Select from '~/components/atoms/select'; import ExtendedFilledTab from '~/console/components/extended-filled-tab'; -import { plans } from './datas'; +import {plans} from './datas'; +import {ExtractNodeType, parseName, parseNodes} from "~/console/server/r-utils/common"; +import {IRepos} from "~/console/server/gql/queries/repo-queries"; +import useCustomSwr from "~/lib/client/hooks/use-custom-swr"; +import {useConsoleApi} from "~/console/server/gql/api-provider"; +import {IDigests} from "~/console/server/gql/queries/tags-queries"; +import {useAppend, useMapper} from "~/components/utils"; +import {IDialogBase} from "~/console/components/types.d"; +import {useEffect, useRef, useState} from "react"; + const valueRender = ({ - label, - isShared, - memoryPerCpu, -}: { - label: string; - isShared: boolean; - memoryPerCpu: number; + label, + isShared, + memoryPerCpu, + }: { + label: string; + isShared: boolean; + memoryPerCpu: number; }) => { - return ( -
+ return ( +
{label}-{isShared ? 'Shared' : 'Dedicated'} - + {memoryPerCpu}GB/vCPU -
- ); +
+ ); +}; + +type ISelectedRepo = { + label: string; + value: string; + service: ExtractNodeType; }; +type ISelectedImage = { + label: string; + value: string; + service: ExtractNodeType; +}; + +type IDialog = IDialogBase>; + + const AppCompute = () => { - const { app, setApp, setPage, markPageAsCompleted, activeContIndex } = - useAppState(); - - const { values, errors, handleChange, isLoading, submit } = useForm({ - initialValues: { - imageUrl: app.spec.containers[activeContIndex]?.image || '', - pullSecret: 'TODO', - cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', - memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || '1', - - cpu: parseValue( - app.spec.containers[activeContIndex]?.resourceCpu?.max, - 250 - ), - - selectedPlan: - app.metadata?.annotations[keyconstants.selectedPlan] || 'shared-1', - selectionMode: - app.metadata?.annotations[keyconstants.selectionModeKey] || 'quick', - manualCpuMin: parseValue( - app.spec.containers[activeContIndex].resourceCpu?.min, - 0 - ), - manualCpuMax: parseValue( - app.spec.containers[activeContIndex].resourceCpu?.max, - 0 - ), - manualMemMin: parseValue( - app.spec.containers[activeContIndex].resourceMemory?.min, - 0 - ), - manualMemMax: parseValue( - app.spec.containers[activeContIndex].resourceMemory?.max, - 0 - ), - }, - validationSchema: Yup.object({ - imageUrl: Yup.string().required(), - pullSecret: Yup.string(), - cpuMode: Yup.string().required(), - selectedPlan: Yup.string().required(), - // cpu: Yup.number().required().min(100).max(8000), - }), - onSubmit: (val) => { - setApp((s) => ({ - ...s, - metadata: { - ...s.metadata!, - annotations: { - ...(s.metadata?.annotations || {}), - [keyconstants.cpuMode]: val.cpuMode, - [keyconstants.memPerCpu]: val.memPerCpu, - [keyconstants.selectionModeKey]: val.selectionMode, - [keyconstants.selectedPlan]: val.selectedPlan, - }, + const {app, setApp, setPage, markPageAsCompleted, activeContIndex} = + useAppState(); + const api = useConsoleApi() + + const [selectedRepo, setSelectedRepo] = useState(""); + const [imageDigests, setImageDigests] = useState<{ tags: string[], digest: string }[]>([]) + const [accountName, setAccountName] = useState("") + const [showImageUrl, setShowImageUrl] = useState(true) + + const { + data, + isLoading: repoLoading, + error: repoLoadingError, + } = useCustomSwr('/repos', async () => { + return api.listRepo({}); + }); + + + const {values, errors, handleChange, isLoading, submit} = useForm({ + initialValues: { + imageUrl: app.spec.containers[activeContIndex]?.image || '', + pullSecret: 'TODO', + cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', + memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || '1', + + cpu: parseValue( + app.spec.containers[activeContIndex]?.resourceCpu?.max, + 250 + ), + + repoName: '', + repoImageTag: '', + repoImageUrl: '', + + selectedPlan: + app.metadata?.annotations[keyconstants.selectedPlan] || 'shared-1', + selectionMode: + app.metadata?.annotations[keyconstants.selectionModeKey] || 'quick', + manualCpuMin: parseValue( + app.spec.containers[activeContIndex].resourceCpu?.min, + 0 + ), + manualCpuMax: parseValue( + app.spec.containers[activeContIndex].resourceCpu?.max, + 0 + ), + manualMemMin: parseValue( + app.spec.containers[activeContIndex].resourceMemory?.min, + 0 + ), + manualMemMax: parseValue( + app.spec.containers[activeContIndex].resourceMemory?.max, + 0 + ), }, - spec: { - ...s.spec, - containers: [ - { - ...(s.spec.containers?.[0] || {}), - image: val.imageUrl, - name: 'container-0', - resourceCpu: - val.selectionMode === 'quick' - ? { - max: `${val.cpu}m`, - min: `${val.cpu}m`, - } - : { - max: `${val.manualCpuMax}m`, - min: `${val.manualCpuMin}m`, - }, - resourceMemory: - val.selectionMode === 'quick' - ? { - max: `${( - (values.cpu || 1) * parseValue(values.memPerCpu, 4) - ).toFixed(2)}Mi`, - min: `${val.cpu}Mi`, - } - : { - max: `${val.manualMemMax}Mi`, - min: `${val.manualMemMin}Mi`, + validationSchema: Yup.object({ + // imageUrl: Yup.string().required(), + pullSecret: Yup.string(), + cpuMode: Yup.string().required(), + selectedPlan: Yup.string().required(), + // cpu: Yup.number().required().min(100).max(8000), + }), + onSubmit: (val) => { + setApp((s) => ({ + ...s, + metadata: { + ...s.metadata!, + annotations: { + ...(s.metadata?.annotations || {}), + [keyconstants.cpuMode]: val.cpuMode, + [keyconstants.memPerCpu]: val.memPerCpu, + [keyconstants.selectionModeKey]: val.selectionMode, + [keyconstants.selectedPlan]: val.selectedPlan, }, - }, - ], + }, + spec: { + ...s.spec, + containers: [ + { + ...(s.spec.containers?.[0] || {}), + image: val.imageUrl == '' ? val.repoImageUrl : val.imageUrl, + name: 'container-0', + repoName: val.repoName, + // repoImage: val.repoImage, + resourceCpu: + val.selectionMode === 'quick' + ? { + max: `${val.cpu}m`, + min: `${val.cpu}m`, + } + : { + max: `${val.manualCpuMax}m`, + min: `${val.manualCpuMin}m`, + }, + resourceMemory: + val.selectionMode === 'quick' + ? { + max: `${( + (values.cpu || 1) * parseValue(values.memPerCpu, 4) + ).toFixed(2)}Mi`, + min: `${val.cpu}Mi`, + } + : { + max: `${val.manualMemMax}Mi`, + min: `${val.manualMemMin}Mi`, + }, + }, + ], + }, + })); }, - })); - }, - }); + }); + + const repos = useMapper(parseNodes(data), (val) => ({ + label: val.name, + value: val.name, + accName: val.accountName + })); - return ( - { - e.preventDefault(); + useEffect(() => { (async () => { - const res = await submit(); - if (res) { - setPage(3); - markPageAsCompleted(2); - } - })(); - }} - > -
- Compute refers to the processing power and resources used for data - manipulation and calculations in a system. -
-
- - } - size="lg" - value={values.imageUrl} - onChange={handleChange('imageUrl')} - error={!!errors.imageUrl} - message={errors.imageUrl} - /> - {/* ({ + digest: item.node.digest, + tags: item.node.tags, + })) + + setImageDigests(digests); + })() + + }, [selectedRepo]) + + return ( + { + e.preventDefault(); + + (async () => { + const res = await submit(); + if (res) { + setPage(3); + markPageAsCompleted(2); + } + })(); + }} + > +
+ Compute refers to the processing power and resources used for data + manipulation and calculations in a system. +
+
+ { showImageUrl && ( + + } + size="lg" + value={values.imageUrl} + onChange={(e) => { + handleChange('imageUrl')(dummyEvent(e.target.value.toLowerCase())) + handleChange('repoName')(dummyEvent('')); + handleChange('repoImageTag')(dummyEvent('')); + }} + error={!!errors.imageUrl} + message={errors.imageUrl} + /> + )} + +
{ + setShowImageUrl(!showImageUrl) + // handleChange('imageUrl')(dummyEvent('')); + // handleChange('repoName')(dummyEvent('')); + // handleChange('repoImageTag')(dummyEvent('')); + }} + className={"bodyMd text-border-focus underline underline-offset-2"}> + { showImageUrl ? "Advanced options" : "Normal options"} +
+ + {!showImageUrl && ( + { + handleChange('repoImageTag')(dummyEvent(val.value)); + handleChange('repoImageUrl')(dummyEvent(`registry.kloudlite.io/${accountName}/${values.repoName}:${val.value}`)) + }} + options={async () => [...new Set(imageDigests.map(item => item.tags).flat())].map(item => ({ + label: item, + value: item, + }))} + error={!!errors.repoImageTag} + message={errors.repoImageTag} + /> + )} + + {/* } size="lg" value={values.pullSecret} /> */} -
- -
-
-
- { - handleChange('selectionMode')(dummyEvent(e)); - }} - items={[ - { label: 'Quick', value: 'quick' }, - { - label: 'Manual', - value: 'manual', - }, - ]} - /> -
-
- {values.selectionMode === 'quick' ? ( -
- [ + ...Object.entries(plans).map(([_, vs]) => ({ + label: vs.label, + options: vs.options.map((op) => ({ + ...op, + render: () => ( +
+
{op.label}
+
{op.memoryPerCpu}GB/vCPU
+
+ ), + })), + })), + ]} + valueRender={valueRender} + onChange={(v) => { + handleChange('selectedPlan')(dummyEvent(v.value)); + handleChange('memPerCpu')(dummyEvent(v.memoryPerCpu)); + handleChange('cpuMode')( + dummyEvent(v.isShared ? 'shared' : 'dedicated') + ); + }} + /> +
+
+
+ Select CPU +
+ + {((values.cpu || 1) / 1000).toFixed(2)}vCPU &{' '} + {( + ((values.cpu || 1) * parseValue(values.memPerCpu, 4)) / + 1000 + ).toFixed(2)} + GB Memory + +
+ { + handleChange('cpu')(dummyEvent(value)); + }} + /> +
+
+ ) : ( +
+
+
+
+ 1000m = 1VCPU - } - /> -
-
- + } + /> +
+
+ 1000m = 1VCPU - } - size="lg" - suffix="m" - /> -
-
-
-
-
-
- -
-
- -
-
+ } + size="lg" + suffix="m" + /> +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ )}
-
- )} - - {/*
+ {/*
Select plan @@ -395,33 +522,50 @@ const AppCompute = () => {
*/} -
-
- - ); +
+
+ + ); }; +// const ContainerRepoLayout = () => { +// const { promise } = useLoaderData(); +// return ( +// +// {({ repository }) => { +// const repoList = parseNodes(repository); +// return ; +// }} +// +// ); +// }; +// +// const NewContainerRepo = () => { +// return ; +// }; + export default AppCompute; +// export default NewContainerRepo \ No newline at end of file From c54d4cd9ac3f76a67f6d1d2b8a95952499e489cd Mon Sep 17 00:00:00 2001 From: Bikash Date: Fri, 23 Feb 2024 17:24:24 +0530 Subject: [PATCH 2/3] Fixed default select for select component --- .../app+/$app+/settings+/compute/route.tsx | 148 ++-- .../$environment+/new-app/app-compute.tsx | 829 +++++++++--------- .../$environment+/new-app/app-review.tsx | 28 +- .../console/server/r-utils/key-constants.js | 2 + 4 files changed, 506 insertions(+), 501 deletions(-) diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx index 221b13789..6cf949c8e 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx @@ -13,11 +13,10 @@ import ExtendedFilledTab from '~/console/components/extended-filled-tab'; import Wrapper from '~/console/components/wrapper'; import { useUnsavedChanges } from '~/root/lib/client/hooks/use-unsaved-changes'; import { Button } from '~/components/atoms/button'; +import useCustomSwr from '~/lib/client/hooks/use-custom-swr'; +import { useConsoleApi } from '~/console/server/gql/api-provider'; +import { parseNodes } from '~/console/server/r-utils/common'; import { plans } from '../../../../new-app/datas'; -import useCustomSwr from "~/lib/client/hooks/use-custom-swr"; -import {useConsoleApi} from "~/console/server/gql/api-provider"; -import {useMapper} from "~/components/utils"; -import {parseNodes} from "~/console/server/r-utils/common"; const valueRender = ({ label, @@ -41,15 +40,13 @@ const valueRender = ({ }; const SettingCompute = () => { - const { app, setApp, getContainer, activeContIndex, getRepoMapper } = useAppState(); + const { app, setApp, getContainer, activeContIndex, getRepoMapper } = + useAppState(); const { setPerformAction, hasChanges, loading } = useUnsavedChanges(); - const api = useConsoleApi() - - const [selectedRepo, setSelectedRepo] = useState(""); - const [imageDigests, setImageDigests] = useState<{ tags: string[], digest: string }[]>([]) - const [accountName, setAccountName] = useState("") + const api = useConsoleApi(); + const [accountName, setAccountName] = useState(''); const { data, @@ -66,8 +63,8 @@ const SettingCompute = () => { cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || 1, - repoName: '', - repoImageTag: '', + repoName: app.metadata?.annotations?.[keyconstants.repoName] || '', + repoImageTag: app.metadata?.annotations?.[keyconstants.imageTag] || '', repoImageUrl: '', cpu: parseValue( @@ -111,6 +108,8 @@ const SettingCompute = () => { ...(s.metadata?.annotations || {}), [keyconstants.cpuMode]: val.cpuMode, [keyconstants.selectedPlan]: val.selectedPlan, + [keyconstants.repoName]: val.repoName, + [keyconstants.imageTag]: val.repoImageTag, }, }, spec: { @@ -161,22 +160,18 @@ const SettingCompute = () => { // accName: val.accountName // })); - const repos = getRepoMapper(data) - - - useEffect(() => { - (async () => { - const {data} = await api.listDigest({repoName: selectedRepo}) - - const digests = data.edges.map(item => ({ - digest: item.node.digest, - tags: item.node.tags, - })) - - setImageDigests(digests); - })() + const repos = getRepoMapper(data); - }, [selectedRepo]) + const { + data: digestData, + isLoading: digestLoading, + error: digestError, + } = useCustomSwr( + () => `/digests_${values.repoName}`, + async () => { + return api.listDigest({ repoName: values.repoName }); + } + ); useEffect(() => { submit(); @@ -213,14 +208,14 @@ const SettingCompute = () => { >
- } - size="lg" - value={values.imageUrl} - onChange={handleChange('imageUrl')} - error={!!errors.imageUrl} - message={errors.imageUrl} + label={ + + } + size="lg" + value={values.imageUrl} + onChange={handleChange('imageUrl')} + error={!!errors.imageUrl} + message={errors.imageUrl} /> {/* { // onChange={handleChange('pullSecret')} /> */} -
- OR -
+
OR
{ - handleChange('repoImageTag')(dummyEvent(val.value)); - handleChange('repoImageUrl')(dummyEvent(`registry.kloudlite.io/${accountName}/${values.repoName}:${val.value}`)) - }} - options={async () => [...new Set(imageDigests.map(item => item.tags).flat())].map(item => ({ + label="Image Tag" + size="lg" + placeholder="Select Image Tag" + value={{ label: '', value: values.repoImageTag }} + searchable + onChange={(val) => { + handleChange('repoImageTag')(dummyEvent(val.value)); + handleChange('repoImageUrl')( + dummyEvent( + `registry.kloudlite.io/${accountName}/${values.repoName}:${val.value}` + ) + ); + }} + options={async () => + [ + ...new Set( + parseNodes(digestData) + .map((item) => item.tags) + .flat() + ), + ].map((item) => ({ label: item, value: item, - }))} - error={!!errors.repoImageTag} - message={errors.repoImageTag} + })) + } + error={!!errors.repoImageTag || !!digestError} + message={ + errors.repoImageTag + ? errors.repoImageTag + : digestError + ? 'Failed to load Image tags.' + : '' + } + loading={digestLoading} /> -
{/*
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx index 30de2d47e..029bcabe2 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx @@ -1,429 +1,420 @@ -import {ArrowLeft, ArrowRight} from '@jengaicons/react'; -import {Button} from '~/components/atoms/button'; -import {NumberInput, TextInput} from '~/components/atoms/input'; +import { ArrowLeft, ArrowRight } from '@jengaicons/react'; +import { Button } from '~/components/atoms/button'; +import { NumberInput, TextInput } from '~/components/atoms/input'; import Slider from '~/components/atoms/slider'; -import {useAppState} from '~/console/page-components/app-states'; -import {keyconstants} from '~/console/server/r-utils/key-constants'; -import useForm, {dummyEvent} from '~/root/lib/client/hooks/use-form'; +import { useAppState } from '~/console/page-components/app-states'; +import { keyconstants } from '~/console/server/r-utils/key-constants'; +import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; -import {InfoLabel} from '~/console/components/commons'; -import {FadeIn, parseValue} from '~/console/page-components/util'; +import { InfoLabel } from '~/console/components/commons'; +import { FadeIn, parseValue } from '~/console/page-components/util'; import Select from '~/components/atoms/select'; import ExtendedFilledTab from '~/console/components/extended-filled-tab'; -import {plans} from './datas'; -import {ExtractNodeType, parseName, parseNodes} from "~/console/server/r-utils/common"; -import {IRepos} from "~/console/server/gql/queries/repo-queries"; -import useCustomSwr from "~/lib/client/hooks/use-custom-swr"; -import {useConsoleApi} from "~/console/server/gql/api-provider"; -import {IDigests} from "~/console/server/gql/queries/tags-queries"; -import {useAppend, useMapper} from "~/components/utils"; -import {IDialogBase} from "~/console/components/types.d"; -import {useEffect, useRef, useState} from "react"; - +import { parseNodes } from '~/console/server/r-utils/common'; +import useCustomSwr from '~/lib/client/hooks/use-custom-swr'; +import { useConsoleApi } from '~/console/server/gql/api-provider'; +import { useMapper } from '~/components/utils'; +import { useState } from 'react'; +import { plans } from './datas'; const valueRender = ({ - label, - isShared, - memoryPerCpu, - }: { - label: string; - isShared: boolean; - memoryPerCpu: number; + label, + isShared, + memoryPerCpu, +}: { + label: string; + isShared: boolean; + memoryPerCpu: number; }) => { - return ( -
+ return ( +
{label}-{isShared ? 'Shared' : 'Dedicated'} - + {memoryPerCpu}GB/vCPU -
- ); -}; - -type ISelectedRepo = { - label: string; - value: string; - service: ExtractNodeType; +
+ ); }; -type ISelectedImage = { - label: string; - value: string; - service: ExtractNodeType; -}; - -type IDialog = IDialogBase>; - - const AppCompute = () => { - const {app, setApp, setPage, markPageAsCompleted, activeContIndex} = - useAppState(); - const api = useConsoleApi() - - const [selectedRepo, setSelectedRepo] = useState(""); - const [imageDigests, setImageDigests] = useState<{ tags: string[], digest: string }[]>([]) - const [accountName, setAccountName] = useState("") - const [showImageUrl, setShowImageUrl] = useState(true) - - const { - data, - isLoading: repoLoading, - error: repoLoadingError, - } = useCustomSwr('/repos', async () => { - return api.listRepo({}); - }); - - - const {values, errors, handleChange, isLoading, submit} = useForm({ - initialValues: { - imageUrl: app.spec.containers[activeContIndex]?.image || '', - pullSecret: 'TODO', - cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', - memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || '1', - - cpu: parseValue( - app.spec.containers[activeContIndex]?.resourceCpu?.max, - 250 - ), - - repoName: '', - repoImageTag: '', - repoImageUrl: '', - - selectedPlan: - app.metadata?.annotations[keyconstants.selectedPlan] || 'shared-1', - selectionMode: - app.metadata?.annotations[keyconstants.selectionModeKey] || 'quick', - manualCpuMin: parseValue( - app.spec.containers[activeContIndex].resourceCpu?.min, - 0 - ), - manualCpuMax: parseValue( - app.spec.containers[activeContIndex].resourceCpu?.max, - 0 - ), - manualMemMin: parseValue( - app.spec.containers[activeContIndex].resourceMemory?.min, - 0 - ), - manualMemMax: parseValue( - app.spec.containers[activeContIndex].resourceMemory?.max, - 0 - ), + const { app, setApp, setPage, markPageAsCompleted, activeContIndex } = + useAppState(); + const api = useConsoleApi(); + + const [accountName, setAccountName] = useState(''); + const [showImageUrl, setShowImageUrl] = useState(true); + + const { + data, + isLoading: repoLoading, + error: repoLoadingError, + } = useCustomSwr('/repos', async () => { + return api.listRepo({}); + }); + + const { values, errors, handleChange, isLoading, submit } = useForm({ + initialValues: { + imageUrl: app.spec.containers[activeContIndex]?.image || '', + pullSecret: 'TODO', + cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', + memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || '1', + + cpu: parseValue( + app.spec.containers[activeContIndex]?.resourceCpu?.max, + 250 + ), + + repoName: app.metadata?.annotations?.[keyconstants.repoName] || '', + repoImageTag: app.metadata?.annotations?.[keyconstants.imageTag] || '', + repoImageUrl: '', + + selectedPlan: + app.metadata?.annotations[keyconstants.selectedPlan] || 'shared-1', + selectionMode: + app.metadata?.annotations[keyconstants.selectionModeKey] || 'quick', + manualCpuMin: parseValue( + app.spec.containers[activeContIndex].resourceCpu?.min, + 0 + ), + manualCpuMax: parseValue( + app.spec.containers[activeContIndex].resourceCpu?.max, + 0 + ), + manualMemMin: parseValue( + app.spec.containers[activeContIndex].resourceMemory?.min, + 0 + ), + manualMemMax: parseValue( + app.spec.containers[activeContIndex].resourceMemory?.max, + 0 + ), + }, + validationSchema: Yup.object({ + // imageUrl: Yup.string().required(), + pullSecret: Yup.string(), + cpuMode: Yup.string().required(), + selectedPlan: Yup.string().required(), + // cpu: Yup.number().required().min(100).max(8000), + }), + onSubmit: (val) => { + setApp((s) => ({ + ...s, + metadata: { + ...s.metadata!, + annotations: { + ...(s.metadata?.annotations || {}), + [keyconstants.cpuMode]: val.cpuMode, + [keyconstants.memPerCpu]: val.memPerCpu, + [keyconstants.selectionModeKey]: val.selectionMode, + [keyconstants.selectedPlan]: val.selectedPlan, + [keyconstants.repoName]: val.repoName, + [keyconstants.imageTag]: val.repoImageTag, + }, }, - validationSchema: Yup.object({ - // imageUrl: Yup.string().required(), - pullSecret: Yup.string(), - cpuMode: Yup.string().required(), - selectedPlan: Yup.string().required(), - // cpu: Yup.number().required().min(100).max(8000), - }), - onSubmit: (val) => { - setApp((s) => ({ - ...s, - metadata: { - ...s.metadata!, - annotations: { - ...(s.metadata?.annotations || {}), - [keyconstants.cpuMode]: val.cpuMode, - [keyconstants.memPerCpu]: val.memPerCpu, - [keyconstants.selectionModeKey]: val.selectionMode, - [keyconstants.selectedPlan]: val.selectedPlan, + spec: { + ...s.spec, + containers: [ + { + ...(s.spec.containers?.[0] || {}), + image: val.imageUrl === '' ? val.repoImageUrl : val.imageUrl, + name: 'container-0', + resourceCpu: + val.selectionMode === 'quick' + ? { + max: `${val.cpu}m`, + min: `${val.cpu}m`, + } + : { + max: `${val.manualCpuMax}m`, + min: `${val.manualCpuMin}m`, }, - }, - spec: { - ...s.spec, - containers: [ - { - ...(s.spec.containers?.[0] || {}), - image: val.imageUrl == '' ? val.repoImageUrl : val.imageUrl, - name: 'container-0', - repoName: val.repoName, - // repoImage: val.repoImage, - resourceCpu: - val.selectionMode === 'quick' - ? { - max: `${val.cpu}m`, - min: `${val.cpu}m`, - } - : { - max: `${val.manualCpuMax}m`, - min: `${val.manualCpuMin}m`, - }, - resourceMemory: - val.selectionMode === 'quick' - ? { - max: `${( - (values.cpu || 1) * parseValue(values.memPerCpu, 4) - ).toFixed(2)}Mi`, - min: `${val.cpu}Mi`, - } - : { - max: `${val.manualMemMax}Mi`, - min: `${val.manualMemMin}Mi`, - }, - }, - ], - }, - })); + resourceMemory: + val.selectionMode === 'quick' + ? { + max: `${( + (values.cpu || 1) * parseValue(values.memPerCpu, 4) + ).toFixed(2)}Mi`, + min: `${val.cpu}Mi`, + } + : { + max: `${val.manualMemMax}Mi`, + min: `${val.manualMemMin}Mi`, + }, + }, + ], }, - }); - - const repos = useMapper(parseNodes(data), (val) => ({ - label: val.name, - value: val.name, - accName: val.accountName - })); + })); + }, + }); + + const repos = useMapper(parseNodes(data), (val) => ({ + label: val.name, + value: val.name, + accName: val.accountName, + })); + + const { + data: digestData, + isLoading: digestLoading, + error: digestError, + } = useCustomSwr( + () => `/digests_${values.repoName}`, + async () => { + return api.listDigest({ repoName: values.repoName }); + } + ); + + return ( + { + e.preventDefault(); - - useEffect(() => { (async () => { - const {data} = await api.listDigest({repoName: selectedRepo}) - - const digests = data.edges.map(item => ({ - digest: item.node.digest, - tags: item.node.tags, - })) - - setImageDigests(digests); - })() - - }, [selectedRepo]) - - return ( - { - e.preventDefault(); - - (async () => { - const res = await submit(); - if (res) { - setPage(3); - markPageAsCompleted(2); - } - })(); + const res = await submit(); + if (res) { + setPage(3); + markPageAsCompleted(2); + } + })(); + }} + > +
+ Compute refers to the processing power and resources used for data + manipulation and calculations in a system. +
+
+ {showImageUrl && ( + + } + size="lg" + value={values.imageUrl} + onChange={(e) => { + handleChange('imageUrl')( + dummyEvent(e.target.value.toLowerCase()) + ); + handleChange('repoName')(dummyEvent('')); + handleChange('repoImageTag')(dummyEvent('')); }} - > -
- Compute refers to the processing power and resources used for data - manipulation and calculations in a system. -
-
- { showImageUrl && ( - - } - size="lg" - value={values.imageUrl} - onChange={(e) => { - handleChange('imageUrl')(dummyEvent(e.target.value.toLowerCase())) - handleChange('repoName')(dummyEvent('')); - handleChange('repoImageTag')(dummyEvent('')); - }} - error={!!errors.imageUrl} - message={errors.imageUrl} - /> - )} - -
{ - setShowImageUrl(!showImageUrl) - // handleChange('imageUrl')(dummyEvent('')); - // handleChange('repoName')(dummyEvent('')); - // handleChange('repoImageTag')(dummyEvent('')); - }} - className={"bodyMd text-border-focus underline underline-offset-2"}> - { showImageUrl ? "Advanced options" : "Normal options"} -
- - {!showImageUrl && ( - { - handleChange('repoImageTag')(dummyEvent(val.value)); - handleChange('repoImageUrl')(dummyEvent(`registry.kloudlite.io/${accountName}/${values.repoName}:${val.value}`)) - }} - options={async () => [...new Set(imageDigests.map(item => item.tags).flat())].map(item => ({ - label: item, - value: item, - }))} - error={!!errors.repoImageTag} - message={errors.repoImageTag} - /> - )} + error={!!errors.imageUrl} + message={errors.imageUrl} + /> + )} + +
- -
-
-
- { - handleChange('selectionMode')(dummyEvent(e)); - }} - items={[ - {label: 'Quick', value: 'quick'}, - { - label: 'Manual', - value: 'manual', - }, - ]} - /> -
+
+ +
+
+
+ { + handleChange('selectionMode')(dummyEvent(e)); + }} + items={[ + { label: 'Quick', value: 'quick' }, + { + label: 'Manual', + value: 'manual', + }, + ]} + /> +
+
+ {values.selectionMode === 'quick' ? ( +
+ [ - ...Object.entries(plans).map(([_, vs]) => ({ - label: vs.label, - options: vs.options.map((op) => ({ - ...op, - render: () => ( -
-
{op.label}
-
{op.memoryPerCpu}GB/vCPU
-
- ), - })), - })), - ]} - valueRender={valueRender} - onChange={(v) => { - handleChange('selectedPlan')(dummyEvent(v.value)); - handleChange('memPerCpu')(dummyEvent(v.memoryPerCpu)); - handleChange('cpuMode')( - dummyEvent(v.isShared ? 'shared' : 'dedicated') - ); - }} - /> -
-
-
- Select CPU -
- - {((values.cpu || 1) / 1000).toFixed(2)}vCPU &{' '} - {( - ((values.cpu || 1) * parseValue(values.memPerCpu, 4)) / - 1000 - ).toFixed(2)} - GB Memory - -
- { - handleChange('cpu')(dummyEvent(value)); - }} - /> -
-
- ) : ( -
-
-
-
- + + {((values.cpu || 1) / 1000).toFixed(2)}vCPU &{' '} + {( + ((values.cpu || 1) * parseValue(values.memPerCpu, 4)) / + 1000 + ).toFixed(2)} + GB Memory + +
+ { + handleChange('cpu')(dummyEvent(value)); + }} + /> +
+
+ ) : ( +
+
+
+
+ 1000m = 1VCPU - } - /> -
-
- + } + /> +
+
+ 1000m = 1VCPU - } - size="lg" - suffix="m" - /> -
-
-
-
-
-
- -
-
- -
-
-
-
- )} + } + size="lg" + suffix="m" + /> +
+
+
+
+
+
+ +
+
+ +
+
+
+ )} +
- {/*
+ {/*
Select plan @@ -522,33 +513,33 @@ const AppCompute = () => {
*/} -
-
- - ); +
+
+ + ); }; // const ContainerRepoLayout = () => { @@ -568,4 +559,4 @@ const AppCompute = () => { // }; export default AppCompute; -// export default NewContainerRepo \ No newline at end of file +// export default NewContainerRepo diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-review.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-review.tsx index be67e0a45..ffd257ccb 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-review.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-review.tsx @@ -16,24 +16,28 @@ interface IReviewComponent { title: string; children: ReactNode; onEdit: () => void; + canEdit?: boolean; } export const ReviewComponent = ({ title = '', children, onEdit, + canEdit = true, }: IReviewComponent) => { return (
{title} - + {canEdit && ( + + )}
{children}
@@ -83,7 +87,7 @@ const AppReview = () => {
An assessment of the work, product, or performance.
- {}}> + { }}>
{app.displayName} @@ -92,7 +96,7 @@ const AppReview = () => {
- {}}> + { }}>
@@ -127,7 +131,7 @@ const AppReview = () => {
- {}}> + { }}>
@@ -147,7 +151,7 @@ const AppReview = () => {
- {}}> + { }}>
Ports exposed from the app diff --git a/src/apps/console/server/r-utils/key-constants.js b/src/apps/console/server/r-utils/key-constants.js index ebb602a1f..8c7a6a488 100644 --- a/src/apps/console/server/r-utils/key-constants.js +++ b/src/apps/console/server/r-utils/key-constants.js @@ -8,4 +8,6 @@ export const keyconstants = { selectedPlan: 'kloudlite.io/ui-selected-plan', memPerCpu: 'kloudlite.io/ui-mem-per-cpu', nodeType: 'kloudlite.io/ui-node-type', + repoName: 'kloudlite.io/ui-repoName', + imageTag: 'kloudlite.io/ui-imageTag', }; From 1300b45481346c593fa4426026b5b4c66e01966d Mon Sep 17 00:00:00 2001 From: Piyush Kumar Date: Mon, 26 Feb 2024 10:34:26 +0530 Subject: [PATCH 3/3] fix(console): Add advanced options as reponame and image tag for create app and update app --- .../app+/$app+/settings+/compute/route.tsx | 142 ++++++++++-------- .../$environment+/new-app/app-compute.tsx | 14 +- .../console/server/r-utils/key-constants.js | 2 + 3 files changed, 87 insertions(+), 71 deletions(-) diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx index 6cf949c8e..d70303d5e 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/settings+/compute/route.tsx @@ -65,7 +65,8 @@ const SettingCompute = () => { repoName: app.metadata?.annotations?.[keyconstants.repoName] || '', repoImageTag: app.metadata?.annotations?.[keyconstants.imageTag] || '', - repoImageUrl: '', + repoImageUrl: app.metadata?.annotations?.[keyconstants.repoImageUrl] || '', + image: app.metadata?.annotations?.[keyconstants.image] || '', cpu: parseValue( app.spec.containers[activeContIndex]?.resourceCpu?.max, @@ -110,6 +111,8 @@ const SettingCompute = () => { [keyconstants.selectedPlan]: val.selectedPlan, [keyconstants.repoName]: val.repoName, [keyconstants.imageTag]: val.repoImageTag, + [keyconstants.image]: val.image, + [keyconstants.repoImageUrl]: val.repoImageUrl, }, }, spec: { @@ -117,7 +120,8 @@ const SettingCompute = () => { containers: [ { ...(s.spec.containers?.[0] || {}), - image: val.imageUrl, + // image: val.imageUrl, + image: val.repoImageUrl == '' ? val.imageUrl : val.repoImageUrl, name: 'container-0', resourceCpu: val.selectionMode === 'quick' @@ -207,16 +211,18 @@ const SettingCompute = () => { }} >
- - } - size="lg" - value={values.imageUrl} - onChange={handleChange('imageUrl')} - error={!!errors.imageUrl} - message={errors.imageUrl} - /> + {!values.repoImageUrl && ( + + } + size="lg" + value={values.imageUrl} + onChange={handleChange('imageUrl')} + error={!!errors.imageUrl} + message={errors.imageUrl} + /> + )} {/* @@ -228,62 +234,66 @@ const SettingCompute = () => { // onChange={handleChange('pullSecret')} /> */} -
OR
+ {values.repoImageUrl && ( + { - handleChange('repoName')(dummyEvent(val.value)); - setAccountName(val.accName); - }} - options={async () => [...repos]} - error={!!errors.repos || !!repoLoadingError} - message={ - repoLoadingError ? 'Error fetching repositories.' : errors.app - } - loading={repoLoading} - /> + {values.repoImageUrl && ( + { - handleChange('repoImageTag')(dummyEvent(val.value)); - handleChange('repoImageUrl')( - dummyEvent( - `registry.kloudlite.io/${accountName}/${values.repoName}:${val.value}` - ) - ); - }} - options={async () => - [ - ...new Set( - parseNodes(digestData) - .map((item) => item.tags) - .flat() - ), - ].map((item) => ({ - label: item, - value: item, - })) - } - error={!!errors.repoImageTag || !!digestError} - message={ - errors.repoImageTag - ? errors.repoImageTag - : digestError - ? 'Failed to load Image tags.' - : '' - } - loading={digestLoading} - />
{/*
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx index 029bcabe2..57d220cae 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-compute.tsx @@ -68,7 +68,8 @@ const AppCompute = () => { repoName: app.metadata?.annotations?.[keyconstants.repoName] || '', repoImageTag: app.metadata?.annotations?.[keyconstants.imageTag] || '', - repoImageUrl: '', + repoImageUrl: app.metadata?.annotations?.[keyconstants.repoImageUrl] || '', + image: app.metadata?.annotations?.[keyconstants.image] || '', selectedPlan: app.metadata?.annotations[keyconstants.selectedPlan] || 'shared-1', @@ -111,6 +112,8 @@ const AppCompute = () => { [keyconstants.selectedPlan]: val.selectedPlan, [keyconstants.repoName]: val.repoName, [keyconstants.imageTag]: val.repoImageTag, + [keyconstants.image]: val.image, + [keyconstants.repoImageUrl]: val.repoImageUrl, }, }, spec: { @@ -118,7 +121,7 @@ const AppCompute = () => { containers: [ { ...(s.spec.containers?.[0] || {}), - image: val.imageUrl === '' ? val.repoImageUrl : val.imageUrl, + image: val.image === '' ? val.repoImageUrl : val.imageUrl, name: 'container-0', resourceCpu: val.selectionMode === 'quick' @@ -191,11 +194,12 @@ const AppCompute = () => { } size="lg" - value={values.imageUrl} + value={values.image} onChange={(e) => { handleChange('imageUrl')( dummyEvent(e.target.value.toLowerCase()) ); + handleChange('image')(dummyEvent(e.target.value.toLowerCase())); handleChange('repoName')(dummyEvent('')); handleChange('repoImageTag')(dummyEvent('')); }} @@ -208,7 +212,7 @@ const AppCompute = () => { onClick={() => { setShowImageUrl(!showImageUrl); }} - content={showImageUrl ? 'Advanced options' : 'Normal options'} + content={showImageUrl ? 'Advanced options' : 'Image option'} variant="primary-plain" size="sm" /> @@ -222,7 +226,7 @@ const AppCompute = () => { searchable onChange={(val) => { handleChange('repoName')(dummyEvent(val.value)); - handleChange('imageUrl')(dummyEvent('')); + handleChange('image')(dummyEvent('')); setAccountName(val.accName); }} options={async () => [...repos]} diff --git a/src/apps/console/server/r-utils/key-constants.js b/src/apps/console/server/r-utils/key-constants.js index 8c7a6a488..24506662f 100644 --- a/src/apps/console/server/r-utils/key-constants.js +++ b/src/apps/console/server/r-utils/key-constants.js @@ -10,4 +10,6 @@ export const keyconstants = { nodeType: 'kloudlite.io/ui-node-type', repoName: 'kloudlite.io/ui-repoName', imageTag: 'kloudlite.io/ui-imageTag', + image: 'kloudlite.io/ui-image', + repoImageUrl: 'kloudlite.io/ui-repoImageUrl', };