diff --git a/.github/workflows/build-container.yaml b/.github/workflows/build-container.yaml index 83f030dff..34bb65909 100644 --- a/.github/workflows/build-container.yaml +++ b/.github/workflows/build-container.yaml @@ -25,14 +25,14 @@ jobs: app: - auth - console - - website + #- website include: - app: auth dockerFile: Dockerfile - app: console dockerFile: Dockerfile - - app: website - dockerFile: Dockerfile.devdoc + #- app: website + #dockerFile: Dockerfile.devdoc runs-on: ubuntu-latest name: Deploy to Docker Image diff --git a/Taskfile.yaml b/Taskfile.yaml index cd33c7618..9eb24a05e 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -40,6 +40,13 @@ tasks: "vision") URL_SUFFIX="-vision" + ;; + "nxtcoder17") + URL_SUFFIX="-mohit" + ;; + + "piyush") + URL_SUFFIX="-piyush" ;; *) diff --git a/fake-data-generator/gen.ts b/fake-data-generator/gen.ts index 71563a97f..56c332d6f 100644 --- a/fake-data-generator/gen.ts +++ b/fake-data-generator/gen.ts @@ -22,6 +22,13 @@ const types: string[] = [ 'ConsoleListManagedResourceQuery', 'ConsoleListHelmChartQuery', 'ConsoleListConsoleVpnDevices', + 'ConsoleListProjectMSvsQuery', + 'ConsoleListMSvTemplatesQuery', + 'ConsoleListRoutersQuery', + 'ConsoleListManagedResourcesQuery', + 'ConsoleListDigestQuery', + 'ConsoleListBuildsQuery', + 'ConsoleListBuildRunsQuery', ]; async function fake(files: string[], types: string[] = []) { diff --git a/gql-queries-generator/doc/queries.graphql b/gql-queries-generator/doc/queries.graphql index 3cac9a950..f3fb11f3d 100644 --- a/gql-queries-generator/doc/queries.graphql +++ b/gql-queries-generator/doc/queries.graphql @@ -379,8 +379,6 @@ query consoleGetCluster($name: String!) { aws { k3sMasters { iamInstanceProfileRole - imageId - imageSSHUsername instanceType nodes nvidiaGpuEnabled @@ -590,8 +588,6 @@ query consoleGetNodePool($clusterName: String!, $poolName: String!) { nodes } iamInstanceProfileRole - imageId - imageSSHUsername nvidiaGpuEnabled poolType rootVolumeSize @@ -777,6 +773,11 @@ query consoleGetEnvironment($projectName: String!, $name: String!) { projectName spec { projectName + routing { + mode + privateIngressClass + publicIngressClass + } targetNamespace } status { diff --git a/lib/client/helpers/socket/context.tsx b/lib/client/helpers/socket/context.tsx index a6e7e1fa0..ccb5a23d9 100644 --- a/lib/client/helpers/socket/context.tsx +++ b/lib/client/helpers/socket/context.tsx @@ -24,7 +24,7 @@ export interface ISocketResp { } type IData = { - event?: string; + event?: 'subscribe' | 'unsubscribe'; id: string; }; diff --git a/lib/client/helpers/socket/useWatch.tsx b/lib/client/helpers/socket/useWatch.tsx new file mode 100644 index 000000000..17436a090 --- /dev/null +++ b/lib/client/helpers/socket/useWatch.tsx @@ -0,0 +1,36 @@ +import { useEffect } from 'react'; +import { ISocketResp, useSubscribe } from './context'; +import { useReload } from '../reloader'; + +export const useSocketWatch = ( + onUpdate: (v: ISocketResp[]) => void, + topic: string +) => { + const { responses, subscribed } = useSubscribe( + { + for: 'resource-update', + data: { + id: topic, + respath: topic, + }, + }, + [] + ); + + useEffect(() => { + if (subscribed) { + onUpdate(responses); + } + }, [responses]); +}; + +export const useWatchReload = (topic: string) => { + const reloadPage = useReload(); + useSocketWatch((rd) => { + console.log(rd); + if (rd.find((v) => v.id === topic)) { + console.log('reloading due to watch event', rd); + reloadPage(); + } + }, topic); +}; diff --git a/package.json b/package.json index 2b9ab13e1..eb44b4420 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@codemirror/legacy-modes": "^6.3.3", "@jengaicons/react": "^1.3.0", "@mdx-js/react": "^2.3.0", - "@oshq/react-select": "^1.3.3", + "@oshq/react-select": "^1.4.1", "@radix-ui/primitive": "^1.0.1", "@radix-ui/react-alert-dialog": "1.0.4", "@radix-ui/react-checkbox": "^1.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5d87b58c..af9b6a1c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^2.3.0 version: 2.3.0(react@18.2.0) '@oshq/react-select': - specifier: ^1.3.3 - version: 1.3.3(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0) + specifier: ^1.4.1 + version: 1.4.1(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0) '@radix-ui/primitive': specifier: ^1.0.1 version: 1.0.1 @@ -2610,8 +2610,8 @@ packages: json-parse-even-better-errors: 2.3.1 dev: true - /@oshq/react-select@1.3.3(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0): - resolution: {integrity: sha512-ygM/Wp562SiesSJRGfgrb7CfMN8gTvdhnz+cjtBXAyDWTHTw7yDXgKyAwdj52VT6zrsDCWJEFFOrq8kb02CLeQ==} + /@oshq/react-select@1.4.1(@radix-ui/react-portal@1.0.4)(classnames@2.5.1)(framer-motion@10.17.8)(rc-virtual-list@3.11.3)(react@18.2.0): + resolution: {integrity: sha512-ftP4UtE7SflDloFUUar45PzwFcivUCOHeJsO/59g2NiCQ7flAF1FGsBValIRZx3h8pe3/nPotvLL+u118oySJQ==} peerDependencies: '@radix-ui/react-portal': ^1.0.4 classnames: ^2.3.2 diff --git a/src/apps/console/components/commons.tsx b/src/apps/console/components/commons.tsx index d4d267469..45e81e4bd 100644 --- a/src/apps/console/components/commons.tsx +++ b/src/apps/console/components/commons.tsx @@ -10,11 +10,21 @@ import { } from '~/root/src/generated/gql/server'; import Tooltip from '~/components/atoms/tooltip'; import { Link } from '@remix-run/react'; +import { Button, IButton } from '~/components/atoms/button'; import { ListItem } from './console-list-components'; import { parseUpdateOrCreatedBy, parseUpdateOrCreatedOn, } from '../server/r-utils/common'; +import { + ArrowLeft, + ArrowRight, + Pencil, + GitBranchFill, + GitlabLogoFill, + GithubLogoFill, +} from './icons'; +import { IGIT_PROVIDERS } from '../hooks/use-git'; export const BlackProdLogo = ({ size = 16 }) => { return ; @@ -232,3 +242,107 @@ export const SubHeaderTitle = ({ ); }; + +type Optional = Pick, K> & Omit; + +export const BottomNavigation = ({ + primaryButton, + secondaryButton, +}: { + primaryButton?: Optional; + secondaryButton?: Optional; +}) => { + return ( +
+ {secondaryButton && ( +
+ ); +}; + +interface IReviewComponent { + title: string; + children: ReactNode; + onEdit: () => void; + canEdit?: boolean; +} +export const ReviewComponent = ({ + title = '', + children, + onEdit, + canEdit = true, +}: IReviewComponent) => { + return ( +
+
+ {title} + {canEdit && ( + + )} +
+ {children} +
+ ); +}; + +export const GitDetail = ({ + provider, + repository, + branch, + onEdit, +}: { + provider: IGIT_PROVIDERS; + repository: string; + branch: string; + onEdit?: (step?: number) => void; +}) => { + const gitIconSize = 16; + return ( + onEdit?.(1)}> +
+
+
Source
+
+
+ {provider === 'github' ? ( + + ) : ( + + )} + + {repository.replace('https://', '').replace('.git', '')} + +
+
+ + {branch} +
+
+
+
+
+ ); +}; diff --git a/src/apps/console/components/git.tsx b/src/apps/console/components/git.tsx index 5b3012e56..1bfa2a4b1 100644 --- a/src/apps/console/components/git.tsx +++ b/src/apps/console/components/git.tsx @@ -17,8 +17,6 @@ import { useAppend, useMapper } from '~/components/utils'; import { ReactNode, useEffect, useState } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { Button } from '~/components/atoms/button'; -import { useReload } from '~/root/lib/client/helpers/reloader'; -import { useLocation } from '@remix-run/react'; import { ILoginUrls, ILogins } from '../server/gql/queries/git-queries'; import Pulsable from './pulsable'; import useGit, { IGIT_PROVIDERS } from '../hooks/use-git'; @@ -320,7 +318,7 @@ const Git = ({ size="lg" valueRender={valueRender} options={async () => accountsModified} - value={{ label: org, value: org }} + value={org} onChange={(res) => { switch (res.value) { case extraAddOption: @@ -434,14 +432,7 @@ const Git = ({ parseName(c) === values.clusterName) - ?.displayName || values.clusterName, - value: values.clusterName, - }} + value={values.clusterName} options={async () => [ - ...clusters.map((clster) => ({ - label: clster.displayName, - value: parseName(clster), - cluster: clster, - render: () => ( -
-
{clster.displayName}
-
- {parseName(clster)} + ...clusters + .filter( + (clster) => parseStatus({ item: clster }) !== 'deleting' + ) + .map((clster) => ({ + label: clster.displayName, + value: parseName(clster), + cluster: clster, + render: () => ( +
+ {parseStatus({ item: clster }) === 'ready' ? ( +
+
{clster.displayName}
+
+ {parseName(clster)} +
+
+ ) : ( + // parseStatus({ item: clster }) === 'syncing' || + // parseStatus({ item: clster }) === 'notready' ? +
+
+
+
{clster.displayName}
+
+ {parseName(clster)} +
+
+
+
+ {statusRender( + clster as ExtractNodeType + ).render()} +
+
+ )}
-
- ), - })), + ), + })), ]} onChange={(v) => { - handleChange('clusterName')(dummyEvent(v.value)); + if (parseStatus({ item: v.cluster }) === 'ready') + handleChange('clusterName')(dummyEvent(v.value)); }} />
diff --git a/src/apps/console/page-components/new-scope.tsx b/src/apps/console/page-components/new-scope.tsx index 0a86b3339..bab214b96 100644 --- a/src/apps/console/page-components/new-scope.tsx +++ b/src/apps/console/page-components/new-scope.tsx @@ -2,25 +2,24 @@ import { useParams } from '@remix-run/react'; import { useEffect, useState } from 'react'; import Popup from '~/components/molecule/popup'; import { toast } from '~/components/molecule/toast'; -import { parseName, parseTargetNs } from '~/console/server/r-utils/common'; +import { parseName } from '~/console/server/r-utils/common'; import { useReload } from '~/root/lib/client/helpers/reloader'; -import { useDataFromMatches } from '~/root/lib/client/hooks/use-custom-matches'; import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; import { Switch } from '~/components/atoms/switch'; +import { Checkbox } from '~/components/atoms/checkbox'; import { IDialog } from '../components/types.d'; import { useConsoleApi } from '../server/gql/api-provider'; import { DIALOG_TYPE } from '../utils/commons'; import { IEnvironment } from '../server/gql/queries/environment-queries'; import { NameIdView } from '../components/name-id-view'; -const HandleScope = ({ show, setShow }: IDialog & {}) => { +const HandleScope = ({ show, setShow }: IDialog) => { const api = useConsoleApi(); const reloadPage = useReload(); const { project: projectName } = useParams(); - const project = useDataFromMatches('project', {}); const [validationSchema, setValidationSchema] = useState( Yup.object({ @@ -149,7 +148,7 @@ const HandleScope = ({ show, setShow }: IDialog & {}) => { nameErrorLabel="isNameError" isUpdate={show?.type !== DIALOG_TYPE.ADD} /> - { diff --git a/src/apps/console/routes/_a+/onboarding+/$a+/new-cloud-provider.tsx b/src/apps/console/routes/_a+/onboarding+/$a+/new-cloud-provider.tsx index 5d599a4b9..4b0a42a62 100644 --- a/src/apps/console/routes/_a+/onboarding+/$a+/new-cloud-provider.tsx +++ b/src/apps/console/routes/_a+/onboarding+/$a+/new-cloud-provider.tsx @@ -28,22 +28,19 @@ const NewCloudProvider = () => { initialValues: { displayName: '', name: '', - provider: providers[0], + provider: providers[0].value, awsAccountId: '', isNameError: false, }, validationSchema: Yup.object({ displayName: Yup.string().required(), name: Yup.string().required(), - provider: Yup.object({ - label: Yup.string().required(), - value: Yup.string().required(), - }).required(), + provider: Yup.string().required(), awsAccountId: Yup.string().required('AccountId is required.'), }), onSubmit: async (val) => { const addProvider = async () => { - switch (val.provider.value) { + switch (val.provider) { case 'aws': return api.createProviderSecret({ secret: { @@ -54,7 +51,7 @@ const NewCloudProvider = () => { aws: { awsAccountId: val.awsAccountId, }, - cloudProviderName: validateCloudProvider(val.provider.value), + cloudProviderName: validateCloudProvider(val.provider), }, }); @@ -131,13 +128,13 @@ const NewCloudProvider = () => { value={values.provider} size="lg" label="Provider" - onChange={(value) => { + onChange={(_, value) => { handleChange('provider')(dummyEvent(value)); }} options={async () => providers} /> - {values.provider.value === 'aws' && ( + {values.provider === 'aws' && ( { - const { app, setApp, getContainer, activeContIndex } = useAppState(); + const { + app, + setApp, + getContainer, + activeContIndex, + getRepoMapper, + getRepoName, + getImageTag, + } = useAppState(); const { setPerformAction, hasChanges, loading } = useUnsavedChanges(); + const api = useConsoleApi(); + + 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 +67,15 @@ const SettingCompute = () => { cpuMode: app.metadata?.annotations?.[keyconstants.cpuMode] || 'shared', memPerCpu: app.metadata?.annotations?.[keyconstants.memPerCpu] || 1, + repoName: getContainer(0)?.image + ? getRepoName(app.spec.containers[activeContIndex]?.image) + : '', + repoImageTag: getContainer(0)?.image + ? getImageTag(app.spec.containers[activeContIndex]?.image) + : '', + repoAccountName: + app.metadata?.annotations?.[keyconstants.repoAccountName] || '', + cpu: parseValue( app.spec.containers[activeContIndex]?.resourceCpu?.max, 250 @@ -88,6 +117,7 @@ const SettingCompute = () => { ...(s.metadata?.annotations || {}), [keyconstants.cpuMode]: val.cpuMode, [keyconstants.selectedPlan]: val.selectedPlan, + [keyconstants.repoAccountName]: val.repoAccountName, }, }, spec: { @@ -95,30 +125,34 @@ const SettingCompute = () => { containers: [ { ...(s.spec.containers?.[0] || {}), - image: val.imageUrl, + image: + values.repoAccountName === undefined || + values.repoAccountName === '' + ? `${values.repoName}:${values.repoImageTag}` + : `${registryHost}/${values.repoAccountName}/${values.repoName}:${values.repoImageTag}`, name: 'container-0', resourceCpu: val.selectionMode === 'quick' ? { - max: `${val.cpu}m`, - min: `${val.cpu}m`, - } + max: `${val.cpu}m`, + min: `${val.cpu}m`, + } : { - max: `${val.manualCpuMax}m`, - min: `${val.manualCpuMin}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: `${( + (values.cpu || 1) * parseValue(values.memPerCpu, 4) + ).toFixed(2)}Mi`, + min: `${val.cpu}Mi`, + } : { - max: `${val.manualMemMax}Mi`, - min: `${val.manualMemMin}Mi`, - }, + max: `${val.manualMemMax}Mi`, + min: `${val.manualMemMin}Mi`, + }, }, ], }, @@ -132,6 +166,19 @@ const SettingCompute = () => { // ); // }, [values.cpuMode, values.selectedPlan]); + const repos = getRepoMapper(data); + + const { + data: digestData, + isLoading: digestLoading, + error: digestError, + } = useCustomSwr( + () => `/digests_${values.repoName}`, + async () => { + return api.listDigest({ repoName: values.repoName }); + } + ); + useEffect(() => { submit(); }, [values]); @@ -166,26 +213,59 @@ const SettingCompute = () => { }} >
- - } + */} + placeholder="Select Image Tag" + value={values.repoImageTag} + creatable + onChange={(_, val) => { + handleChange('repoImageTag')(dummyEvent(val)); + }} + 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} + />
{/*
@@ -306,7 +386,7 @@ const SettingCompute = () => { {values.selectionMode === 'quick' ? (
- {/* + placeholder="Select Repo" + // value={{ label: '', value: values.repoName }} + value={values.repoName} + // searchable + creatable + onChange={(val) => { + handleChange('repoName')(dummyEvent(val.value)); + if (val.accName === undefined || val.accName === '') { + handleChange('repoAccountName')(dummyEvent('')); + } else { + handleChange('repoAccountName')(dummyEvent(val.accName)); + } + }} + options={async () => [...repos]} + error={!!errors.repos || !!repoLoadingError} + message={ + repoLoadingError ? 'Error fetching repositories.' : errors.app } + loading={repoLoading} + /> + + [ @@ -424,4 +514,21 @@ const AppCompute = () => { ); }; +// const ContainerRepoLayout = () => { +// const { promise } = useLoaderData(); +// return ( +// +// {({ repository }) => { +// const repoList = parseNodes(repository); +// return ; +// }} +// +// ); +// }; +// +// const NewContainerRepo = () => { +// return ; +// }; + export default AppCompute; +// 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..1154f13cb 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 @@ -1,6 +1,6 @@ -import { ArrowLeft, ArrowRight, PencilLine } from '@jengaicons/react'; +import { ArrowLeft, ArrowRight } from '@jengaicons/react'; import { useNavigate, useParams } from '@remix-run/react'; -import { ReactNode, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { Button } from '~/components/atoms/button'; import { toast } from '~/components/molecule/toast'; import { useAppState } from '~/console/page-components/app-states'; @@ -11,34 +11,8 @@ import { handleError } from '~/root/lib/utils/common'; import { validateType } from '~/root/src/generated/gql/validator'; import { parseName } from '~/console/server/r-utils/common'; import { FadeIn } from '~/console/page-components/util'; +import { ReviewComponent } from '~/console/components/commons'; -interface IReviewComponent { - title: string; - children: ReactNode; - onEdit: () => void; -} -export const ReviewComponent = ({ - title = '', - children, - onEdit, -}: IReviewComponent) => { - return ( -
-
- {title} - -
- {children} -
- ); -}; const AppReview = () => { const { app, setPage, resetState } = useAppState(); diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-managed-resource/_index.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-managed-resource/_index.tsx index 515b96238..12ce051a8 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-managed-resource/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-managed-resource/_index.tsx @@ -34,7 +34,7 @@ import MultiStepProgressWrapper from '~/console/components/multi-step-progress-w import MultiStepProgress, { useMultiStepProgress, } from '~/console/components/multi-step-progress'; -import { ReviewComponent } from '../new-app/app-review'; +import { ReviewComponent } from '~/console/components/commons'; import { IProjectContext } from '../../_layout'; export const loader = (ctx: IRemixCtx) => { @@ -251,7 +251,7 @@ const TemplateView = ({ label="Service" size="lg" placeholder="Select service" - value={{ label: '', value: values.selectedService?.value || '' }} + value={values.selectedService?.value} searchable onChange={(val) => { handleChange('selectedService')(dummyEvent(val)); @@ -272,11 +272,7 @@ const TemplateView = ({ label="Resource type" size="lg" placeholder="Select resource type" - value={ - values.selectedResource?.value - ? { label: '', value: values.selectedResource?.value || '' } - : undefined - } + value={values.selectedResource?.value} searchable onChange={(val) => { handleChange('selectedResource')(dummyEvent(val)); diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx index 14fd00d13..f19065f70 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/router+/$router+/routes/handle-route.tsx @@ -21,6 +21,7 @@ import { NN } from '~/root/lib/types/common'; import { TextInput } from '~/components/atoms/input'; import { useEffect, useState } from 'react'; import { IApps } from '~/console/server/gql/queries/app-queries'; +import { Switch } from '~/components/atoms/switch'; import { ModifiedRouter } from './_index'; type IDialog = IDialogBase< @@ -50,18 +51,20 @@ const Root = (props: IDialog) => { useForm({ initialValues: isUpdate ? { - path: - props.data.path?.[0] === '/' - ? props.data.path.substring(1) - : props.data.path, - app: props.data.app || '', - port: `${props.data.port}`, - } + path: + props.data.path?.[0] === '/' + ? props.data.path.substring(1) + : props.data.path, + app: props.data.app || '', + port: `${props.data.port}`, + reWrite: false, + } : { - path: '', - app: '', - port: '', - }, + path: '', + app: '', + port: '', + reWrite: false, + }, validationSchema: Yup.object({ path: Yup.string().test( 'is-valid', @@ -93,11 +96,13 @@ const Root = (props: IDialog) => { path: r.path, app: r.app, port: r.port, + rewrite: r.rewrite, })) || []), { path: `/${val.path}`, app: val.app, port: parseInt(val.port, 10), + rewrite: val.reWrite, }, ], }, @@ -132,7 +137,7 @@ const Root = (props: IDialog) => { port: route.port, })) || []), { - path: val.path, + path: `/${val.path}`, app: val.app, port: parseInt(val.port, 10), }, @@ -185,21 +190,42 @@ const Root = (props: IDialog) => { return ( - { - handleChange('path')(dummyEvent(e.target.value.toLowerCase())); - }} - error={!!errors.path} - message={errors.path} - prefix="/" - /> +
+
+
+ { + handleChange('path')( + dummyEvent(e.target.value.toLowerCase()) + ); + }} + error={!!errors.path} + message={errors.path} + prefix="/" + /> +
+
+ +
+
Rewrite
+
+ { + handleChange('reWrite')(dummyEvent(val)); + }} + /> +
+
+
[...combinedDomains]} - onChange={(val) => { - setSelectedDomains(val); - handleChange('domains')(dummyEvent([...val.map((v) => v.value)])); + onChange={(val, v) => { + handleChange('domains')(dummyEvent(v)); }} error={!!errors.domains || !!domainLoadingError} message={ diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx index d15abeeef..40f9c4e5e 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/routers/router-resources.tsx @@ -25,6 +25,8 @@ import { listStatus } from '~/console/components/sync-status'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { toast } from '~/components/molecule/toast'; +import { Button } from '~/components/atoms/button'; +import Tooltip from '~/components/atoms/tooltip'; import HandleRouter from './handle-router'; const RESOURCE_NAME = 'domain'; @@ -54,6 +56,11 @@ type IExtraButton = { item: BaseType; }; +const formatDomain = (domain: string) => { + const d = domain.startsWith('https://') ? domain : `https://${domain}`; + return { full: d, short: d.replace('https://', '') }; +}; + const ExtraButton = ({ onAction, item }: IExtraButton) => { return ( { {items.map((item, index) => { const { name, id, updateInfo } = parseItem(item); const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; + const firstDomain = item.spec.domains?.[0]; return ( { /> ), }, + { + key: generateKey(keyPrefix, 'extra_domain'), + render: () => ( + +
+ } + > +
+ } + /> + ), + }, { key: generateKey(keyPrefix, updateInfo.author), render: () => ( @@ -129,6 +184,7 @@ const ListView = ({ items, onAction }: IResource) => { const { name, id, updateInfo } = parseItem(item); const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; const status = listStatus({ key: `${keyPrefix}status`, item }); + const firstDomain = item.spec.domains?.[0]; return ( { render: () => , }, status, + { + key: generateKey(keyPrefix, 'extra_domain'), + render: () => ( + +
+ } + > +
+ } + /> + ), + }, listFlex({ key: 'flex-1' }), { key: generateKey(keyPrefix, updateInfo.author), diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx index 9e36fe076..e86696a03 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/settings+/general/route.tsx @@ -1,5 +1,5 @@ import { CopySimple } from '@jengaicons/react'; -import { useNavigate, useOutletContext } from '@remix-run/react'; +import { useLocation, useNavigate, useOutletContext } from '@remix-run/react'; import { useEffect, useState } from 'react'; import { Button } from '~/components/atoms/button'; import { TextInput } from '~/components/atoms/input'; @@ -8,51 +8,26 @@ import { Box, DeleteContainer, } from '~/console/components/common-console-components'; -import SubNavAction from '~/console/components/sub-nav-action'; import { useConsoleApi } from '~/console/server/gql/api-provider'; -import { ConsoleApiType } from '~/console/server/gql/saved-queries'; -import { ExtractNodeType, parseName } from '~/console/server/r-utils/common'; +import { parseName } from '~/console/server/r-utils/common'; import useClipboard from '~/root/lib/client/hooks/use-clipboard'; -import useForm from '~/root/lib/client/hooks/use-form'; +import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import { useUnsavedChanges } from '~/root/lib/client/hooks/use-unsaved-changes'; import { consoleBaseUrl } from '~/root/lib/configs/base-url.cjs'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; -import { IEnvironment } from '~/console/server/gql/queries/environment-queries'; import DeleteDialog from '~/console/components/delete-dialog'; import { useReload } from '~/root/lib/client/helpers/reloader'; +import Wrapper from '~/console/components/wrapper'; +import { Checkbox } from '~/components/atoms/checkbox'; import { IEnvironmentContext } from '../../_layout'; -export const updateEnvironment = async ({ - api, - data, - project, -}: { - project: string; - api: ConsoleApiType; - data: ExtractNodeType; -}) => { - try { - const { errors: e } = await api.updateEnvironment({ - projectName: project, - env: { - displayName: data.displayName, - metadata: data.metadata, - }, - }); - if (e) { - throw e[0]; - } - } catch (err) { - handleError(err); - } -}; - const EnvironmentSettingsGeneral = () => { const { environment, project, account } = useOutletContext(); const { setHasChanges, resetAndReload } = useUnsavedChanges(); + const [success, setSuccess] = useState(false); const [deleteEnvironment, setDeleteEnvironment] = useState(false); const api = useConsoleApi(); @@ -68,146 +43,193 @@ const EnvironmentSettingsGeneral = () => { const { values, handleChange, submit, isLoading, resetValues } = useForm({ initialValues: { displayName: environment.displayName, + environmentRoutingMode: environment.spec?.routing?.mode === 'public', }, validationSchema: Yup.object({ displayName: Yup.string().required('Name is required.'), }), onSubmit: async (val) => { - await updateEnvironment({ - project: parseName(project), - api, - data: { ...environment, displayName: val.displayName }, - }); + try { + const { errors: e } = await api.updateEnvironment({ + projectName: parseName(project), + env: { + displayName: val.displayName, + metadata: { + name: parseName(environment), + }, + spec: { + projectName: environment.projectName, + routing: { + mode: val.environmentRoutingMode ? 'public' : 'private', + }, + }, + }, + }); + if (e) { + throw e[0]; + } + setSuccess(true); + } catch (err) { + handleError(err); + } + resetAndReload(); }, }); + const hasChanges = () => + values.displayName !== environment.displayName || + values.environmentRoutingMode !== + (environment.spec?.routing?.mode === 'public'); + useEffect(() => { - setHasChanges(values.displayName !== environment.displayName); + setHasChanges(hasChanges()); }, [values]); useEffect(() => { resetValues(); }, [environment]); + const location = useLocation(); + + useEffect(() => { + setSuccess(false); + }, [location]); + return ( - <> - - {values.displayName !== environment.displayName && ( - <> -
+ ), + }} + > + + + { + handleChange('environmentRoutingMode')(dummyEvent(checked)); + }} + /> +
+
+ - - -
- } - /> -
-
- - +
+ } + /> + +
+ - - -
- } - disabled - /> + + + } + disabled + /> + - -
+ + { + setDeleteEnvironment(true); + }} + > + Permanently remove your environment and all of its contents from the “ + {environment.displayName}” environment. This action is not reversible, + so please continue with caution. + + { + try { + const { errors } = await api.deleteEnvironment({ + envName: parseName(environment), + projectName: parseName(project), + }); - { - setDeleteEnvironment(true); - }} - > - Permanently remove your environment and all of its contents from the “ - {environment.displayName}” environment. This action is not reversible, - so please continue with caution. - - { - try { - const { errors } = await api.deleteEnvironment({ - envName: parseName(environment), - projectName: parseName(project), - }); - - if (errors) { - throw errors[0]; + if (errors) { + throw errors[0]; + } + reload(); + toast.success(`Environment deleted successfully`); + setDeleteEnvironment(false); + navigate( + `/${parseName(account)}/${parseName(project)}/environments/` + ); + } catch (err) { + handleError(err); } - reload(); - toast.success(`Environment deleted successfully`); - setDeleteEnvironment(false); - navigate( - `/${parseName(account)}/${parseName(project)}/environments/` - ); - } catch (err) { - handleError(err); - } - }} - /> - + }} + /> + + ); }; export default EnvironmentSettingsGeneral; diff --git a/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx b/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx index bc1e8c447..f99ea660c 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/_layout.tsx @@ -32,11 +32,13 @@ import { import { useActivePath } from '~/root/lib/client/hooks/use-active-path'; import { cn } from '~/components/utils'; import { IMSvTemplates } from '~/console/server/gql/queries/managed-templates-queries'; +import { ICluster } from '~/console/server/gql/queries/cluster-queries'; import { IAccountContext } from '../_layout'; export interface IProjectContext extends IAccountContext { project: IProject; msvtemplates: IMSvTemplates; + cluster: ICluster; } const iconSize = tabIconSize; const tabs = [ @@ -75,10 +77,10 @@ const tabs = [ const Project = () => { const rootContext = useOutletContext(); - const { project, msvtemplates } = useLoaderData(); + const { project, msvtemplates, cluster } = useLoaderData(); return ( - + ); }; @@ -154,6 +156,16 @@ export const loader = async (ctx: IRemixCtx) => { ctx.request ).listMSvTemplates({}); + if (!data.clusterName) { + throw new Error('No cluster in project.'); + } + + const { data: clusterData, errors: clusterError } = await GQLServerHandler( + ctx.request + ).getCluster({ + name: data.clusterName, + }); + if (errors) { throw errors[0]; } @@ -162,9 +174,14 @@ export const loader = async (ctx: IRemixCtx) => { throw msvError[0]; } + if (clusterError) { + throw clusterError[0]; + } + return { project: data || {}, msvtemplates: msvTemplates || {}, + cluster: clusterData, }; } catch (err) { logger.log(err); diff --git a/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx b/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx index 4045c417b..be98a9c54 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/environments/clone-environment.tsx @@ -1,5 +1,6 @@ /* eslint-disable react/destructuring-assignment */ import { useParams } from '@remix-run/react'; +import { Checkbox } from '~/components/atoms/checkbox'; import { Switch } from '~/components/atoms/switch'; import Popup from '~/components/molecule/popup'; import { toast } from '~/components/molecule/toast'; @@ -85,7 +86,7 @@ const Root = (props: IDialog) => { handleChange={handleChange} nameErrorLabel="isNameError" /> - { diff --git a/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx b/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx index e35a21563..eaae6ab74 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/managed-services/handle-backend-service.tsx @@ -1,28 +1,19 @@ /* eslint-disable guard-for-in */ /* eslint-disable react/destructuring-assignment */ -import { ArrowRight, Search, Check } from '@jengaicons/react'; import { useParams } from '@remix-run/react'; import { useEffect, useRef, useState } from 'react'; -import ActionList from '~/components/atoms/action-list'; import { NumberInput, TextInput } from '~/components/atoms/input'; import Popup from '~/components/molecule/popup'; -import { toast } from '~/components/molecule/toast'; -import { ListTitle } from '~/console/components/console-list-components'; -import { IdSelector } from '~/console/components/id-selector'; -import List from '~/console/components/list'; -import NoResultsFound from '~/console/components/no-results-found'; import { IDialogBase } from '~/console/components/types.d'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { ExtractNodeType, parseName } from '~/console/server/r-utils/common'; import { useReload } from '~/root/lib/client/helpers/reloader'; -import { useInputSearch } from '~/root/lib/client/helpers/search-filter'; import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; import { NN } from '~/root/lib/types/common'; import { handleError } from '~/root/lib/utils/common'; import { IMSvTemplates } from '~/console/server/gql/queries/managed-templates-queries'; import { Switch } from '~/components/atoms/switch'; -import { cn } from '~/components/utils'; import { IProjectMSvs } from '~/console/server/gql/queries/project-managed-services-queries'; import { getManagedTemplate } from '~/console/utils/commons'; import { NameIdView } from '~/console/components/name-id-view'; @@ -216,8 +207,6 @@ const Fill = ({ return acc[curr]; }, null); - console.log('x', x); - return ( { const { isUpdate, setVisible, templates } = props; - const [selectedService, setSelectedService] = - useState(null); const api = useConsoleApi(); const reload = useReload(); const { project } = useParams(); - const [step, setStep] = useState<'choose' | 'fill'>('choose'); - const { values, errors, handleChange, handleSubmit, resetValues, isLoading } = - useForm({ - initialValues: isUpdate - ? { - name: parseName(props.data), - displayName: props.data.displayName, - isNameError: false, - res: { - ...props.data.spec?.msvcSpec.serviceTemplate.spec, - }, - } - : { - name: '', - displayName: '', - res: {}, - isNameError: false, + const { values, errors, handleChange, handleSubmit, isLoading } = useForm({ + initialValues: isUpdate + ? { + name: parseName(props.data), + displayName: props.data.displayName, + isNameError: false, + res: { + ...props.data.spec?.msvcSpec.serviceTemplate.spec, }, - validationSchema: Yup.object({}), - onSubmit: async (val) => { - if (isUpdate) { - try { - if (!project) { - throw new Error('Project is required!.'); - } - const { errors: e } = await api.updateProjectMSv({ - projectName: project, - pmsvc: { - displayName: val.displayName, - metadata: { - name: val.name, - }, + } + : { + name: '', + displayName: '', + res: {}, + isNameError: false, + }, + validationSchema: Yup.object({}), + onSubmit: async (val) => { + if (isUpdate) { + try { + if (!project) { + throw new Error('Project is required!.'); + } + const { errors: e } = await api.updateProjectMSv({ + projectName: project, + pmsvc: { + displayName: val.displayName, + metadata: { + name: val.name, + }, - spec: { - msvcSpec: { - serviceTemplate: { - apiVersion: - props.data.spec?.msvcSpec.serviceTemplate.apiVersion || - '', - kind: - props.data.spec?.msvcSpec.serviceTemplate.kind || '', - spec: { - ...val.res, - }, + spec: { + msvcSpec: { + serviceTemplate: { + apiVersion: + props.data.spec?.msvcSpec.serviceTemplate.apiVersion || + '', + kind: props.data.spec?.msvcSpec.serviceTemplate.kind || '', + spec: { + ...val.res, }, }, - targetNamespace: '', }, + targetNamespace: '', }, - }); - if (e) { - throw e[0]; - } - setVisible(false); - reload(); - } catch (err) { - handleError(err); + }, + }); + if (e) { + throw e[0]; } + setVisible(false); + reload(); + } catch (err) { + handleError(err); } - }, - }); + } + }, + }); const getService = () => { if (isUpdate) @@ -321,14 +305,7 @@ const Root = (props: IDialog) => { return ( { - console.log('name error..', values.isNameError); - handleSubmit(e); - // if (!values.isNameError) { - // handleSubmit(e); - // } else { - // e.preventDefault(); - // } }} > diff --git a/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx index 2c578b1dd..cedf45b6e 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/managed-services/route.tsx @@ -9,6 +9,7 @@ import { parseNodes } from '~/console/server/r-utils/common'; import { IRemixCtx } from '~/root/lib/types/common'; import Tools from './tools'; import BackendServicesResources from './backend-services-resources'; +import fake from "~/root/fake-data-generator/fake"; export const loader = (ctx: IRemixCtx) => { const { project } = ctx.params; @@ -40,7 +41,13 @@ const KlOperatorServices = () => { const { promise } = useLoaderData(); return ( - + {({ managedServices, templates: templatesData }) => { const backendServices = parseNodes(managedServices); diff --git a/src/apps/console/routes/_main+/$account+/$project+/new-managed-service/_index.tsx b/src/apps/console/routes/_main+/$account+/$project+/new-managed-service/_index.tsx index 00211445a..42a1f4862 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/new-managed-service/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/new-managed-service/_index.tsx @@ -22,8 +22,8 @@ import MultiStepProgress, { useMultiStepProgress, } from '~/console/components/multi-step-progress'; import MultiStepProgressWrapper from '~/console/components/multi-step-progress-wrapper'; +import { ReviewComponent } from '~/console/components/commons'; import { IProjectContext } from '../_layout'; -import { ReviewComponent } from '../$environment+/new-app/app-review'; const valueRender = ({ label, icon }: { label: string; icon: string }) => { return ( @@ -189,11 +189,7 @@ const TemplateView = ({ label="Template" size="lg" placeholder="Select templates" - value={ - values.selectedTemplate - ? { label: '', value: values.selectedTemplate?.template.name || '' } - : undefined - } + value={values.selectedTemplate?.template.name} valueRender={valueRender} searchable error={!!errors.selectedTemplate} diff --git a/src/apps/console/routes/_main+/$account+/$project+/settings+/general/route.tsx b/src/apps/console/routes/_main+/$account+/$project+/settings+/general/route.tsx index 993ad99ae..812fc0983 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/settings+/general/route.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/settings+/general/route.tsx @@ -8,7 +8,6 @@ import { Box, DeleteContainer, } from '~/console/components/common-console-components'; -import SubNavAction from '~/console/components/sub-nav-action'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { IProject } from '~/console/server/gql/queries/project-queries'; import { ConsoleApiType } from '~/console/server/gql/saved-queries'; diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx index bb0670550..5de217bb9 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/_index.tsx @@ -11,6 +11,7 @@ import { IRemixCtx } from '~/root/lib/types/common'; import Tools from './tools'; import HelmChartResources from './helm-chart-resources'; import HandleHelmChart from './handle-helm-chart'; +import fake from "~/root/fake-data-generator/fake"; export const loader = (ctx: IRemixCtx) => { const { cluster } = ctx.params; @@ -36,7 +37,12 @@ const HelmCharts = () => { return ( <> - + {({ helmChartData }) => { const helmCharts = parseNodes(helmChartData); diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/handle-helm-chart.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/handle-helm-chart.tsx index cc7a02337..fab57ded2 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/handle-helm-chart.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/helm-charts/handle-helm-chart.tsx @@ -90,6 +90,7 @@ const Root = (props: IDialog) => { { label: string; value: string } | undefined >(undefined); const [isRepoCreatable, setIsRepoCreatable] = useState(false); + const [selectedRepo, setSelectedRepo] = useState(''); const [repoErrors, setRepoErrors] = useState(false); useEffect(() => { @@ -169,6 +170,7 @@ const Root = (props: IDialog) => { ts_query_web: text, }, }); + setRepos( r.data.packages.map( (hc: { @@ -184,7 +186,7 @@ const Root = (props: IDialog) => { }; }) => ({ label: hc.name, - value: hc.repository.url, + value: hc.package_id, repoUrl: hc.repository.url, render: () => (
@@ -271,26 +273,26 @@ const Root = (props: IDialog) => { useForm({ initialValues: !isUpdate ? { - displayName: '', - name: '', - namespace: '', - chartName: '', - chartRepoURL: '', - values: '', - isNameError: false, - } + displayName: '', + name: '', + namespace: '', + chartName: '', + chartRepoURL: '', + values: '', + isNameError: false, + } : { - isNameError: false, - displayName: props.data.displayName, - name: props.data.metadata?.name || '', - values: - Object.keys(props.data.spec?.values).length > 0 - ? yaml.dump(props.data.spec?.values) - : '', - namespace: props.data.metadata?.namespace, - chartName: props.data.spec?.chartName, - chartRepoURL: props.data.spec?.chartRepoURL, - }, + isNameError: false, + displayName: props.data.displayName, + name: props.data.metadata?.name || '', + values: + Object.keys(props.data.spec?.values).length > 0 + ? yaml.dump(props.data.spec?.values) + : '', + namespace: props.data.metadata?.namespace, + chartName: props.data.spec?.chartName, + chartRepoURL: props.data.spec?.chartRepoURL, + }, validationSchema: Yup.object({ displayName: Yup.string().required(), name: Yup.string().required(), @@ -423,21 +425,17 @@ const Root = (props: IDialog) => { options={async () => isUpdate ? [ - { - label: values.namespace || '', - value: values.namespace || '', - }, - ] + { + label: values.namespace || '', + value: values.namespace || '', + }, + ] : namespaces } - value={ - values.namespace - ? { label: values.namespace, value: values.namespace } - : undefined - } + value={values.namespace} creatable={!isUpdate} - onChange={(val) => { - handleChange('namespace')(dummyEvent(val.value.toLowerCase())); + onChange={(_, val) => { + handleChange('namespace')(dummyEvent(val.toLowerCase())); }} noOptionMessage={
@@ -455,17 +453,14 @@ const Root = (props: IDialog) => { searchable creatable={isRepoCreatable} options={async () => repos} - value={{ - label: values.chartRepoURL || '', - value: values.chartRepoURL || '', - }} + value={selectedRepo} onChange={(value) => { if (!repoSearchText.startsWith('https://')) { handleChange('chartRepoURL')(dummyEvent(value.repoUrl)); } else { handleChange('chartRepoURL')(dummyEvent(value.value)); } - console.log(value.repoUrl, values.chartRepoURL); + setSelectedRepo(value.value); setHelmCharts([]); }} onSearch={(text) => { @@ -495,7 +490,7 @@ const Root = (props: IDialog) => { placeholder="Chart name" searchable disabled={hemlCharts.length === 0 || reposLoading || repoErrors} - value={chartName} + value={chartName?.value} options={async () => hemlCharts} loading={!repoErrors && helmChartsLoading} onChange={(val) => { @@ -519,7 +514,7 @@ const Root = (props: IDialog) => { repoErrors)) || (isUpdate && helmChartsLoading) } - value={chartVersion} + value={chartVersion?.value} options={async () => [ ...chartVersions.map((cv) => ({ label: cv.version, diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/handle-nodepool.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/handle-nodepool.tsx index efcace3d5..1c4366ffe 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/handle-nodepool.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/handle-nodepool.tsx @@ -221,24 +221,16 @@ const Root = (props: IDialog) => { mapper( awsRegions.find((v) => v.Name === clusterRegion)?.Zones || @@ -249,8 +241,8 @@ const Root = (props: IDialog) => { }) ) } - onChange={(v) => { - handleChange('awsAvailabilityZone')(dummyEvent(v.value)); + onChange={(_, v) => { + handleChange('awsAvailabilityZone')(dummyEvent(v)); }} /> @@ -258,7 +250,7 @@ const Root = (props: IDialog) => { // eslint-disable-next-line react-hooks/rules-of-hooks value={useMemo(() => { const plan = findNodePlan(values.instanceType); - return plan; + return plan?.value; }, [values.instanceType])} label="Node plan" options={async () => nodePlans} diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx index 319925dec..6be804808 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx @@ -16,7 +16,7 @@ import { parseUpdateOrCreatedBy, parseUpdateOrCreatedOn, } from '~/console/server/r-utils/common'; -import { memo, useState } from 'react'; +import { useState } from 'react'; import Popup from '~/components/molecule/popup'; import { HighlightJsLogs } from 'react-highlightjs-logs'; import { yamlDump } from '~/console/components/diff-viewer'; @@ -32,13 +32,13 @@ import AnimateHide from '~/components/atoms/animate-hide'; import { ISetState } from '~/console/page-components/app-states'; import { Button } from '~/components/atoms/button'; import { dayjs } from '~/components/molecule/dayjs'; +import LogComp from '~/root/lib/client/components/logger'; import HandleNodePool from './handle-nodepool'; import { findNodePlanWithCategory, findNodePlanWithSpec, } from './nodepool-utils'; import { IAccountContext } from '../../../_layout'; -import LogComp from '~/root/lib/client/components/logger'; const RESOURCE_NAME = 'nodepool'; type BaseType = ExtractNodeType; @@ -186,7 +186,6 @@ const ListDetail = (
); case 'azure': - case 'do': case 'gcp': default: return null; diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/route.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/route.tsx index a7ed28151..4397ca6f5 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/route.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/route.tsx @@ -15,6 +15,7 @@ import { getPagination, getSearch } from '~/console/server/utils/common'; import HandleNodePool from './handle-nodepool'; import Tools from './tools'; import NodepoolResources from './nodepool-resources'; +import fake from "~/root/fake-data-generator/fake"; export const loader = async (ctx: IRemixCtx) => { ensureAccountSet(ctx); @@ -47,10 +48,9 @@ const Nodepools = () => { <> {({ nodePoolData }) => { const nodepools = nodePoolData?.edges?.map(({ node }) => node); diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/settings+/general/route.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/settings+/general/route.tsx index 846b05280..c9d56b307 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/settings+/general/route.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/settings+/general/route.tsx @@ -12,7 +12,6 @@ import { DeleteContainer, } from '~/console/components/common-console-components'; import { LoadingComp, pWrapper } from '~/console/components/loading-component'; -import SubNavAction from '~/console/components/sub-nav-action'; import { awsRegions } from '~/console/dummy/consts'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { ICluster } from '~/console/server/gql/queries/cluster-queries'; @@ -22,7 +21,6 @@ import { } from '~/console/server/gql/saved-queries'; import { ExtractNodeType, - ensureResource, parseName, parseNodes, } from '~/console/server/r-utils/common'; @@ -211,7 +209,7 @@ const Layout = ({ label="Cloud Provider" placeholder="Select cloud provider" disabled - value={defaultProvider} + value={defaultProvider?.value} options={async () => providerSecrets} />
@@ -220,11 +218,7 @@ const Layout = ({ disabled label="Region" placeholder="Select region" - value={{ - value: defaultRegion?.Name || '', - label: defaultRegion?.Name || '', - region: defaultRegion as any, - }} + value={defaultRegion?.Name} options={async () => mapper(awsRegions, (v) => { return { diff --git a/src/apps/console/routes/_main+/$account+/infra+/clusters/cluster-resources.tsx b/src/apps/console/routes/_main+/$account+/infra+/clusters/cluster-resources.tsx index dcec3dfde..9e67f760f 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/clusters/cluster-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/clusters/cluster-resources.tsx @@ -38,7 +38,6 @@ const getProvider = (item: ExtractNodeType) => { ({item.spec.aws?.region}) ); - case 'do': case 'gcp': case 'azure': return ( diff --git a/src/apps/console/routes/_main+/$account+/packages+/_layout.tsx b/src/apps/console/routes/_main+/$account+/packages+/_layout.tsx index 8a35579f4..d2960b31c 100644 --- a/src/apps/console/routes/_main+/$account+/packages+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/packages+/_layout.tsx @@ -2,8 +2,10 @@ import { Outlet, useOutletContext } from '@remix-run/react'; import SidebarLayout from '~/console/components/sidebar-layout'; import { IAccountContext } from '../_layout'; +export interface IPackageContext extends IAccountContext {} + const ContainerRegistry = () => { - const rootContext = useOutletContext(); + const rootContext = useOutletContext(); return ( { return ( ; const RESOURCE_NAME = 'project'; @@ -156,6 +158,8 @@ const ListView = ({ items }: { items: BaseType[] }) => { }; const ProjectResources = ({ items = [] }: { items: BaseType[] }) => { + const { account } = useOutletContext(); + useWatchReload(`account:${parseName(account)}`); return ( } diff --git a/src/apps/console/routes/_main+/$account+/projects/route.tsx b/src/apps/console/routes/_main+/$account+/projects/route.tsx index 6ccde0bdf..caa66e659 100644 --- a/src/apps/console/routes/_main+/$account+/projects/route.tsx +++ b/src/apps/console/routes/_main+/$account+/projects/route.tsx @@ -72,7 +72,6 @@ export const loader = (ctx: IRemixCtx) => { const Projects = () => { // return ; - console.log('projects'); const { account } = useParams(); const { promise } = useLoaderData(); @@ -168,7 +167,6 @@ const Projects = () => { if (!projects) { return null; } - console.log(projects); return ( { const { repo, account } = useParams(); + const repoName = atob(repo || ''); return (
{ } /> {repo}} + content={{repoName}} />
); @@ -95,9 +110,40 @@ export const handle = () => { }; }; +export const loader = async (ctx: IRemixCtx) => { + try { + const { data, errors } = await GQLServerHandler(ctx.request).getLogins({}); + + if (errors) { + throw errors[0]; + } + + const { data: e, errors: dErrors } = await GQLServerHandler( + ctx.request + ).loginUrls({}); + + if (dErrors) { + throw dErrors[0]; + } + + return { + loginUrls: e, + logins: data, + }; + } catch (err) { + logger.error(err); + } + + return { + logins: {}, + loginUrls: {}, + }; +}; + const Repo = () => { - const rootContext = useOutletContext(); - return ; + const rootContext = useOutletContext(); + const { logins, loginUrls } = useLoaderData(); + return ; }; export default Repo; diff --git a/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/route.tsx b/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/route.tsx index b3f69940b..c66b84aa8 100644 --- a/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/route.tsx +++ b/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/route.tsx @@ -36,7 +36,7 @@ const BuildRuns = () => { {({ buildRunData }) => { diff --git a/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/_index.tsx b/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/_index.tsx index 41dfc75e1..c544f5407 100644 --- a/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/_index.tsx +++ b/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/_index.tsx @@ -13,6 +13,7 @@ import { Button } from '~/components/atoms/button'; import BuildResources from './build-resources'; import HandleBuild from './handle-builds'; import Tools from './tools'; +import fake from "~/root/fake-data-generator/fake"; export const loader = async (ctx: IRemixCtx) => { const { repo } = ctx.params; @@ -42,7 +43,12 @@ const Builds = () => { return ( <> - + {({ buildData }) => { const builds = buildData.edges?.map(({ node }) => node); diff --git a/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/handle-builds.tsx b/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/handle-builds.tsx index 5d2c3bac7..53d6abf67 100644 --- a/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/handle-builds.tsx +++ b/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/handle-builds.tsx @@ -1,12 +1,5 @@ /* eslint-disable react/destructuring-assignment */ import { IDialogBase } from '~/console/components/types.d'; - -import { - GitBranch, - GithubLogoFill, - GitlabLogoFill, - PencilSimple, -} from '@jengaicons/react'; import { useOutletContext, useParams } from '@remix-run/react'; import { Checkbox } from '~/components/atoms/checkbox'; import Select from '~/components/atoms/select'; @@ -16,9 +9,6 @@ import { useConsoleApi } from '~/console/server/gql/api-provider'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { handleError } from '~/root/lib/utils/common'; import { useEffect, useState } from 'react'; -import { TextArea, TextInput } from '~/components/atoms/input'; -import { Button } from '~/components/atoms/button'; -import MultiStep, { useMultiStep } from '~/console/components/multi-step'; import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; import Yup from '~/root/lib/server/helpers/yup'; import Popup from '~/components/molecule/popup'; @@ -31,14 +21,12 @@ import { } from '~/console/server/r-utils/common'; import useCustomSwr from '~/root/lib/client/hooks/use-custom-swr'; import KeyValuePair from '~/console/components/key-value-pair'; -import { useLog } from '~/root/lib/client/hooks/use-log'; -import { IAccountContext } from '../../../_layout'; - -interface ISource { - repo: string; - branch: string; - provider: 'github' | 'gitlab'; -} +import Git from '~/console/components/git'; +import { IGIT_PROVIDERS } from '~/console/hooks/use-git'; +import MultiStep, { useMultiStep } from '~/console/components/multi-step'; +import { TextArea, TextInput } from '~/components/atoms/input'; +import { GitDetail } from '~/console/components/commons'; +import { IRepoContext } from '../_layout'; const BuildPlatforms = ({ value, @@ -105,18 +93,15 @@ const Root = (props: IDialog) => { const api = useConsoleApi(); const reloadPage = useReload(); - const { user } = useOutletContext(); - - useLog(user); + const { loginUrls, logins } = useOutletContext(); - const { data: clusters, error: errorCluster } = useCustomSwr( - '/clusters', - async () => api.listClusters({}) - ); + const { + data: clusters, + error: errorCluster, + isLoading: clusterLoading, + } = useCustomSwr('/clusters', async () => api.listClusters({})); const clusterData = useMapper(parseNodes(clusters), (item) => { - console.log(errorCluster); - return { label: item.displayName, value: parseName(item), @@ -129,21 +114,7 @@ const Root = (props: IDialog) => { }; }); - useEffect(() => { - if (errorCluster) { - toast.error('Error loading clusters.'); - } - }, [errorCluster]); - - const [source, setSource] = useState(null); - - useEffect(() => { - if (isUpdate) { - setSource({ ...props.data.source, repo: props.data.source.repository }); - } - }, [isUpdate]); - - const { currentStep, onPrevious } = useMultiStep({ + const { currentStep, onPrevious, onNext } = useMultiStep({ defaultStep: isUpdate ? 2 : 1, totalSteps: 2, }); @@ -161,131 +132,75 @@ const Root = (props: IDialog) => { const { values, errors, handleChange, handleSubmit, resetValues } = useForm({ initialValues: isUpdate ? { - name: props.data.name, - tags: - props.data.spec.registry.repo.tags.map((t) => ({ - label: t, - value: t, - })) || [], - buildClusterName: '', - repository: repo, - advanceOptions: isAdvanceOptions(props.data.spec.buildOptions), - ...props.data.spec.buildOptions, - ...(props.data.spec.buildOptions?.buildArgs || props), - } - : { - name: '', - tags: [], - buildClusterName: '', - advanceOptions: false, - repository: repo, - buildArgs: {}, - buildContexts: {}, - contextDir: '', - dockerfilePath: '', - dockerfileContent: '', - }, + name: props.data.name, + source: { + branch: props.data.source.branch, + repository: props.data.source.repository, + provider: props.data.source.provider, + }, + tags: props.data.spec.registry.repo.tags, + buildClusterName: props.data.buildClusterName, + repository: props.data.spec.registry.repo.name, + advanceOptions: isAdvanceOptions(props.data.spec.buildOptions), + ...props.data.spec.buildOptions, + ...(props.data.spec.buildOptions?.buildArgs || props), + } + : {}, validationSchema: Yup.object({ - name: Yup.string().required(), - buildClusterName: Yup.string().required(), - tags: Yup.array() - .required() - .test('is-valid', 'Tags is required', (value) => { - return value.length > 0; - }), - // buildArgs: Yup.object().when('advanceOptions', { - // is: true, - // then: (schema) => - // schema - // .required() - // .test('is-valid', 'Build args is required', (value) => { - // return Object.keys(value).length > 0; - // }), - // }), - // buildContexts: Yup.object().when('advanceOptions', { - // is: true, - // then: (schema) => - // schema - // .required() - // .test('is-valid', 'Build contexts is required', (value) => { - // return Object.keys(value).length > 0; - // }), - // }), + source: Yup.object() + .shape({ + branch: Yup.string().required('Branch is required'), + }) + .required('Branch is required'), + name: Yup.string().test('required', 'Name is required', (v) => { + return !(currentStep === 2 && !v); + }), + buildClusterName: Yup.string().test( + 'required', + 'Build cluster name is required', + (v) => { + return !(currentStep === 2 && !v); + } + ), + tags: Yup.array().test('required', 'Tags is required', (value = []) => { + return !(currentStep === 2 && !(value.length > 0)); + }), }), + onSubmit: async (val) => { - if (source) { + const submit = async () => { try { - if (!isUpdate) { - const { errors: e } = await api.createBuild({ - build: { - name: val.name, - buildClusterName: val.buildClusterName, - source: { - branch: source.branch!, - repository: source.repo!, - provider: source.provider!, - }, - spec: { - ...{ - ...(val.advanceOptions - ? { - buildOptions: { - buildArgs: val.buildArgs, - buildContexts: val.buildContexts, - contextDir: val.contextDir, - dockerfileContent: val.dockerfileContent, - dockerfilePath: val.dockerfilePath, - targetPlatforms: [], - }, - } - : {}), - }, - registry: { - repo: { - name: val.repository || '', - tags: val.tags.map((t: any) => t.value), - }, - }, - resource: { - cpu: 500, - memoryInMb: 1000, - }, - }, - }, - }); - if (e) { - throw e[0]; - } - } else { + if (isUpdate) { const { errors: e } = await api.updateBuild({ crUpdateBuildId: props.data.id, build: { name: val.name, buildClusterName: val.buildClusterName, source: { - branch: source.branch!, - repository: source.repo!, - provider: source.provider!, + branch: val.source.branch, + provider: + val.source.provider === 'github' ? 'github' : 'gitlab', + repository: val.source.repository, }, spec: { ...{ ...(val.advanceOptions ? { - buildOptions: { - buildArgs: val.buildArgs, - buildContexts: val.buildContexts, - contextDir: val.contextDir, - dockerfileContent: val.dockerfileContent, - dockerfilePath: val.dockerfilePath, - targetPlatforms: [], - }, - } + buildOptions: { + buildArgs: val.buildArgs, + buildContexts: val.buildContexts, + contextDir: val.contextDir, + dockerfileContent: val.dockerfileContent, + dockerfilePath: val.dockerfilePath, + targetPlatforms: [], + }, + } : {}), }, registry: { repo: { - name: val.repository || '', - tags: val.tags.map((t: any) => t.value), + name: val.repository, + tags: val.tags, }, }, resource: { @@ -308,64 +223,55 @@ const Root = (props: IDialog) => { } catch (err) { handleError(err); } + }; + switch (currentStep) { + case 1: + onNext(); + break; + case 2: + await submit(); + break; + default: + break; } }, }); - const getProviderLogo = (provider: string) => { - const logoSize = 24; - switch (provider) { - case 'github': - return ; - case 'gitlab': - return ; - default: - return null; - } - }; - return ( -
- {/* { - setSource({ ...val, branch: val.branch! }); - onNext(); - }} - /> */} -
+ { + handleChange('source')( + dummyEvent({ + branch: git.branch, + repository: git.repo, + provider: git.provider, + }) + ); + }} + value={{ + branch: values.source.branch, + repo: values.source.repository, + provider: + (values.source.provider as IGIT_PROVIDERS) || 'github', + }} + />
+
-
-
- {getProviderLogo(source?.provider || '')}{' '} -
{source?.repo}
-
-
- {' '} -
{source?.branch}
-
- -
-
-
-
-
-
Target
{ creatable multiple value={values.tags} - options={async () => values.tags} - onChange={(val) => { + options={async () => + values.tags.map((t: string) => ({ label: t, value: t })) + } + onChange={(_, val) => { handleChange('tags')(dummyEvent(val)); }} error={!!errors.tags} @@ -391,15 +299,20 @@ const Root = (props: IDialog) => {