diff --git a/.gitignore b/.gitignore index f02da370b..9f711bc21 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ public/** out .firebase +.firebaseConfig.js storybook-dist diff --git a/package.json b/package.json index 9e44a406a..f36c2760f 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.0", + "@oshq/react-select": "^1.3.2", "@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 a6d305692..0846b6e63 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.0 - version: 1.3.0(@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.3.2 + version: 1.3.2(@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 @@ -2607,8 +2607,8 @@ packages: json-parse-even-better-errors: 2.3.1 dev: true - /@oshq/react-select@1.3.0(@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-Q9qZugOhG4Q8Cpt4U5lk4GuA8m2l4YED+fciVRVllsWy8H9If8zX6vo+pbSJhzd4YE5KwmjeKmt0Q7jHLS7Nzw==} + /@oshq/react-select@1.3.2(@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-G07uCRUqXMPM/0ZwMBaKHTIoA507D7pgbDOBEMETnGBfhLnk4rI6wcAioVxP5/IuletCyUZrARa7ixWuitgFow==} peerDependencies: '@radix-ui/react-portal': ^1.0.4 classnames: ^2.3.2 diff --git a/src/apps/console/components/multi-step-progress.tsx b/src/apps/console/components/multi-step-progress.tsx index 62d488885..a23b9280a 100644 --- a/src/apps/console/components/multi-step-progress.tsx +++ b/src/apps/console/components/multi-step-progress.tsx @@ -1,41 +1,42 @@ import { Check } from '@jengaicons/react'; -import React, { - Children, - ReactElement, - ReactNode, - useRef, - useState, -} from 'react'; +import React, { Children, ReactElement, ReactNode, useState } from 'react'; import { cn } from '~/components/utils'; interface IUseMultiStepProgress { defaultStep: number; totalSteps: number; + onChange?: (step: number) => void; } export const useMultiStepProgress = ({ defaultStep = 1, totalSteps = 1, + onChange, }: IUseMultiStepProgress) => { const [currentIndex, setCurrentIndex] = useState(defaultStep); const nextStep = () => { if (currentIndex < totalSteps) { + onChange?.(currentIndex + 1); setCurrentIndex((prev) => prev + 1); } }; const prevStep = () => { if (currentIndex > 1) { + onChange?.(currentIndex - 1); setCurrentIndex((prev) => prev - 1); } }; const jumpStep = (step: number) => { + onChange?.(step); setCurrentIndex(step); }; const reset = () => { + onChange?.(1); setCurrentIndex(1); }; + return { currentStep: currentIndex, nextStep, prevStep, reset, jumpStep }; }; @@ -45,9 +46,10 @@ type IProgressTrackerItem = { label: ReactNode; children?: ReactNode; onClick?: () => void; - hasBorder: boolean; index: number; - noJump?: boolean; + noJump?: (step: number) => boolean; + editable?: boolean; + step: number; }; function ProgressTrackerItem( @@ -59,17 +61,19 @@ function ProgressTrackerItem( completed = false, label, onClick, - hasBorder, index, noJump, + editable, + step, } = props; - return (
@@ -78,12 +82,17 @@ function ProgressTrackerItem( aria-label={`step-${index}`} onClick={onClick} className={cn( - 'border-2 border-surface-basic-default headingXs box-content w-3xl h-3xl rounded-full flex items-center justify-center absolute left-0 -ml-[12px]', - completed - ? 'bg-surface-primary-default text-text-on-primary' - : 'bg-surface-primary-selected', - active && !completed ? 'text-text-primary' : 'text-text-disabled', - onClick && !noJump ? 'cursor-pointer' : 'cursor-default' + 'border-2 border-surface-basic-default headingXs box-content w-3xl h-3xl rounded-full flex items-center justify-center absolute left-0 ', + onClick && !noJump?.(step) ? 'cursor-pointer' : 'cursor-default', + { + 'bg-surface-primary-default text-text-on-primary': + !!completed && !!editable, + 'bg-icon-disabled text-text-on-primary': !!completed && !editable, + 'bg-surface-basic-active text-text-disabled': + !completed && !active, + 'bg-surface-primary-selected text-text-default': + active && !completed, + } )} > {completed ? : index} @@ -91,8 +100,11 @@ function ProgressTrackerItem( @@ -109,8 +121,15 @@ interface IStep { label: ReactNode; step: number; className?: string; + completed?: boolean; } -const Step = ({ children, step, className, label: _label }: IStep) => { +const Step = ({ + children, + step, + className, + label: _label, + completed: _completed, +}: IStep) => { return (
{children} @@ -122,13 +141,15 @@ interface IMultiStepProgress { children: ReactElement | ReactElement[]; currentStep: number; jumpStep: (step: number) => void; - noJump?: boolean; + noJump?: (step: number) => boolean; + editable?: boolean; } const Root = ({ children, currentStep, jumpStep, noJump, + editable = true, }: IMultiStepProgress) => { let child = children; // @ts-ignore @@ -138,26 +159,26 @@ const Root = ({ } return ( -
+
{Children.map(child, (ch, index) => { return ( !(index + 1 < currentStep))} + editable={editable} + completed={currentStep > ch.props.step} onClick={() => { - if (index + 1 < currentStep) { - if (!noJump) { - jumpStep(index + 1); - } + if (noJump ? !noJump?.(ch.props.step) : index + 1 < currentStep) { + jumpStep(index + 1); } }} > {currentStep === ch.props.step ? ( -
{ch.props.children}
+
{ch.props.children}
) : null}
); diff --git a/src/apps/console/page-components/app-states.tsx b/src/apps/console/page-components/app-states.tsx index 6a7d8c7ed..ff15fce76 100644 --- a/src/apps/console/page-components/app-states.tsx +++ b/src/apps/console/page-components/app-states.tsx @@ -28,14 +28,6 @@ type ISetContainer = (fn: ((val: T) => T) | T, index?: number) => void; const CreateAppContext = createContext(null); -export type createAppTabs = - | 'Environment' - | 'Application details' - | 'Compute' - | 'Network' - | 'Review' - | NonNullableString; - export type createAppEnvPage = | 'environment_variables' | 'config_mounts' @@ -47,7 +39,7 @@ interface IappState { }; activeContIndex: number; envPage: createAppEnvPage; - page: createAppTabs; + page: number; app: AppIn; } @@ -99,7 +91,7 @@ export const useAppState = () => { }); }; - const setPage: ISetState = (fn) => { + const setPage: ISetState = (fn) => { if (typeof fn === 'function') { setState((s) => ({ ...s, page: fn(s.page) })); } else { @@ -117,7 +109,7 @@ export const useAppState = () => { useEffect(() => { if (!page) { - setPage('Application details'); + setPage(1); } if (!envPage) { setEnvPage('environment_variables'); @@ -159,7 +151,7 @@ export const useAppState = () => { } }; - const isPageComplete = (page: createAppTabs) => { + const isPageComplete = (page: number) => { if (completePages) return completePages[page]; setState((s) => { @@ -171,7 +163,7 @@ export const useAppState = () => { return false; }; - const markPageAsCompleted = (page: createAppTabs) => { + const markPageAsCompleted = (page: number) => { setState((s) => { return { ...s, @@ -185,7 +177,7 @@ export const useAppState = () => { const resetState = (iApp?: AppIn) => { setState({ - page: 'Application details', + page: 1, app: iApp || defaultApp, completePages: {}, envPage: 'environment_variables', diff --git a/src/apps/console/page-components/new-cluster.tsx b/src/apps/console/page-components/new-cluster.tsx index 24d60f3ee..08cd39979 100644 --- a/src/apps/console/page-components/new-cluster.tsx +++ b/src/apps/console/page-components/new-cluster.tsx @@ -65,6 +65,7 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { })); const { a: accountName } = useParams(); + const rootUrl = `/${accountName}/infra/clusters`; const { currentStep, jumpStep, nextStep } = useMultiStepProgress({ defaultStep: isOnboarding ? 4 : 1, @@ -151,7 +152,7 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { throw e[0]; } toast.success('Cluster created successfully'); - navigate(`/${accountName}/infra/clusters`); + navigate(rootUrl); } catch (err) { handleError(err); } @@ -173,7 +174,7 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { const getView = () => { return ( -
+
{ : { backButton: { content: 'Back to clusters', - to: `/${accountName}/infra/clusters`, + to: rootUrl, }, })} > isOnboarding || !(step < currentStep)} currentStep={currentStep} jumpStep={jumpStep} + editable={!isOnboarding} > {!isOnboarding ? ( <> @@ -328,7 +330,7 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { jumpStep(1); }} > -
+
Cluster name diff --git a/src/apps/console/page-components/new-project.tsx b/src/apps/console/page-components/new-project.tsx index d7d69c391..34717d7c6 100644 --- a/src/apps/console/page-components/new-project.tsx +++ b/src/apps/console/page-components/new-project.tsx @@ -28,6 +28,7 @@ const NewProject = () => { const params = useParams(); const { a: accountName } = params; + const rootUrl = `/${accountName}/projects`; const { currentStep, jumpStep, nextStep } = useMultiStepProgress({ defaultStep: 1, @@ -68,7 +69,7 @@ const NewProject = () => { throw e[0]; } toast.success('project created successfully'); - navigate(`/${accountName}/projects`); + navigate(rootUrl); } catch (err) { handleError(err); } @@ -102,7 +103,7 @@ const NewProject = () => { subTitle="Simplify Collaboration and Enhance Productivity with Kloudlite teams" backButton={{ content: 'Back to projects', - to: `/${accountName}/projects`, + to: rootUrl, }} > diff --git a/src/apps/console/routes/_a+/$a+/new-project.tsx b/src/apps/console/routes/_a+/$a+/new-project.tsx index 7d6656415..606513b55 100644 --- a/src/apps/console/routes/_a+/$a+/new-project.tsx +++ b/src/apps/console/routes/_a+/$a+/new-project.tsx @@ -5,7 +5,6 @@ import { getPagination, getSearch } from '~/console/server/utils/common'; import logger from '~/root/lib/client/helpers/log'; import { IRemixCtx } from '~/root/lib/types/common'; - const _NewProject = () => { return ; }; diff --git a/src/apps/console/routes/_a+/new-team.tsx b/src/apps/console/routes/_a+/new-team.tsx index 66c651f62..e0a7ef1f0 100644 --- a/src/apps/console/routes/_a+/new-team.tsx +++ b/src/apps/console/routes/_a+/new-team.tsx @@ -1,225 +1,34 @@ -import { ArrowRight, Plus, X } from '@jengaicons/react'; -import { useNavigate, useParams } from '@remix-run/react'; -import { Button, IconButton } from '~/components/atoms/button'; -import { TextInput } from '~/components/atoms/input'; +import { ArrowRight } from '@jengaicons/react'; +import { useNavigate } from '@remix-run/react'; +import { Button } from '~/components/atoms/button'; import { toast } from '~/components/molecule/toast'; import { useDataFromMatches } from '~/root/lib/client/hooks/use-custom-matches'; -import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; +import useForm from '~/root/lib/client/hooks/use-form'; import { UserMe } from '~/root/lib/server/gql/saved-queries'; import Yup from '~/root/lib/server/helpers/yup'; import { handleError } from '~/root/lib/utils/common'; -import { useEffect, useState } from 'react'; import { useConsoleApi } from '~/console/server/gql/api-provider'; -import RawWrapper, { TitleBox } from '~/console/components/raw-wrapper'; -import { FadeIn } from '~/console/page-components/util'; -import { IdSelector } from '~/console/components/id-selector'; -import ProgressWrapper from '~/console/components/progress-wrapper'; -import SelectPrimitive from '~/components/atoms/select-primitive'; -import DynamicPagination from '~/console/components/dynamic-pagination'; -import List from '~/console/components/list'; -import { - ListBody, - ListItem, -} from '~/console/components/console-list-components'; -import { usePagination } from '~/components/molecule/pagination'; -import { ACCOUNT_ROLES } from '~/console/utils/commons'; -import { Github__Com___Kloudlite___Api___Apps___Iam___Types__Role as Role } from '~/root/src/generated/gql/server'; -import { titleCase } from '~/components/utils'; +import { NameIdView } from '~/console/components/name-id-view'; +import MultiStepProgressWrapper from '~/console/components/multi-step-progress-wrapper'; +import MultiStepProgress, { + useMultiStepProgress, +} from '~/console/components/multi-step-progress'; -const InviteSection = () => { - const { a: accountName } = useParams(); - const api = useConsoleApi(); - const [inviting, setInviting] = useState(false); - - const [inviteMembers, setInviteMembers] = useState< - { userEmail: string; userRole: Role }[] - >([]); - - const { - values: valuesInvite, - errors: errorsInvite, - handleChange: handleChangeInvite, - handleSubmit: handleSubmitInvite, - resetValues: resetValuesInvite, - } = useForm({ - initialValues: { - userEmail: '', - userRole: 'account_member', - }, - validationSchema: Yup.object({ - userEmail: Yup.string() - .required() - .email() - .test('is-valid', 'Email already exists.', (value) => { - return !inviteMembers.find( - (im) => im.userEmail.toLowerCase() === value.toLowerCase() - ); - }), - userRole: Yup.string().required().oneOf(Object.keys(ACCOUNT_ROLES)), - }), - onSubmit: async () => { - setInviteMembers((prev) => [ - ...prev, - { - userEmail: valuesInvite.userEmail, - userRole: valuesInvite.userRole as Role, - }, - ]); - resetValuesInvite(); - }, - }); - - const { page, hasNext, hasPrevious, onNext, onPrev, setItems } = - usePagination({ - items: inviteMembers, - itemsPerPage: 5, - }); - - useEffect(() => { - setItems(inviteMembers); - }, [inviteMembers]); - - const removeMember = ({ item }: { item: (typeof inviteMembers)[number] }) => { - setInviteMembers(inviteMembers.filter((im) => im !== item)); - }; - - const sendInvitation = async () => { - if (inviting) { - return; - } - - if (inviteMembers.length > 0) { - try { - setInviting(true); - const { errors: e } = await api.inviteMembersForAccount({ - accountName: accountName!, - invitations: inviteMembers, - }); - if (e) { - throw e[0]; - } - toast.success('user invited'); - // navigate(`/onboarding/${accountName}/new-cloud-provider`); - } catch (err) { - handleError(err); - } finally { - setInviting(false); - } - } else { - // navigate(`/onboarding/${accountName}/new-cloud-provider`); - } - }; - return ( -
-
-
-
-
- -
- - - {Object.entries(ACCOUNT_ROLES).map(([key, value]) => { - return ( - - {value} - - ); - })} - -
-
-
-
-
- 0, - noItemsMessage: '0 teammates to invite.', - onNext, - onPrev, - headerClassName: 'bg-surface-basic-subdued', - header:
Team list
, - }} - className="rounded border border-border-default overflow-hidden min-h-[266px]" - > - - {page.map((item) => { - return ( - , - }, - { - key: 2, - render: () => , - }, - { - key: 3, - render: () => ( - } - variant="plain" - size="sm" - onClick={() => { - removeMember({ item }); - }} - /> - ), - }, - ]} - /> - ); - })} - -
-
- ); -}; const NewAccount = () => { const api = useConsoleApi(); const navigate = useNavigate(); const user = useDataFromMatches('user', {}); - const [isNameLoading, setIsNameLoading] = useState(false); const { values, handleChange, errors, isLoading, handleSubmit } = useForm({ initialValues: { name: '', displayName: '', + isNameError: false, }, validationSchema: Yup.object({ name: Yup.string().required(), displayName: Yup.string().required(), }), onSubmit: async (v) => { - if (isNameLoading) { - toast.error('id is being checked, please wait'); - return; - } try { const { errors: _errors } = await api.createAccount({ account: { @@ -239,70 +48,53 @@ const NewAccount = () => { }, }); - const progressItems = [ - { - label: 'Create Team', - active: true, - completed: false, - children: ( -
-
- - setIsNameLoading(v)} - name={values.displayName} - onChange={(v) => handleChange('name')(dummyEvent(v))} - resType="account" - className="pt-2xl" - /> -
- {/* */} -
-
- - ), - }, - { - label: 'Add your Cloud Provider', - active: false, - completed: false, - }, - { - label: 'Validate Cloud Provider', - active: false, - completed: false, - }, - { - label: 'Setup First Cluster', - active: false, - completed: false, - }, - ]; + const { currentStep, jumpStep } = useMultiStepProgress({ + defaultStep: 1, + totalSteps: 4, + }); return ( - + > + true} + jumpStep={jumpStep} + > + +
+ +
+
+
+
+ + + +
+ + ); }; diff --git a/src/apps/console/routes/_a+/onboarding+/$a+/$cloudprovider+/validate-cp.tsx b/src/apps/console/routes/_a+/onboarding+/$a+/$cloudprovider+/validate-cp.tsx index 2655de7c1..0e54073f8 100644 --- a/src/apps/console/routes/_a+/onboarding+/$a+/$cloudprovider+/validate-cp.tsx +++ b/src/apps/console/routes/_a+/onboarding+/$a+/$cloudprovider+/validate-cp.tsx @@ -19,6 +19,10 @@ import { LoadingPlaceHolder } from '~/console/components/loading'; import CodeView from '~/console/components/code-view'; import { asyncPopupWindow } from '~/console/utils/commons'; import ProgressWrapper from '~/console/components/progress-wrapper'; +import MultiStepProgressWrapper from '~/console/components/multi-step-progress-wrapper'; +import MultiStepProgress, { + useMultiStepProgress, +} from '~/console/components/multi-step-progress'; import { IAccountContext } from '../../../../_main+/$account+/_layout'; export const loader = async (ctx: IRemixCtx) => { @@ -45,7 +49,6 @@ export const loader = async (ctx: IRemixCtx) => { const Validator = ({ cloudProvider }: { cloudProvider: any }) => { const { account } = useOutletContext(); - const [showUnsavedChanges, setShowUnsavedChanges] = useState(false); const navigate = useNavigate(); const api = useConsoleApi(); @@ -73,126 +76,114 @@ const Validator = ({ cloudProvider }: { cloudProvider: any }) => { } ); - const progressItems = [ - { - label: 'Create Team', - active: false, - completed: true, - }, - { - label: 'Invite your Team Members', - active: false, - completed: true, - }, - { - label: 'Add your Cloud Provider', - active: false, - completed: true, - }, - { - label: 'Validate Cloud Provider', - active: true, - completed: false, - children: ( -
-
- Validate your cloud provider's credentials -
- {il ? ( -
- -
- ) : data?.result ? ( -
- }> - Your Credential is valid - -
- ) : ( -
-
- Account ID - - {cloudProvider.aws?.awsAccountId} - -
-
- - - - visit the link above or - - to create AWS cloudformation stack - -
-
- )} -
- {/*
-
- ), - }, - { - label: 'Setup First Cluster', - active: false, - completed: false, - }, - ]; + const { currentStep, jumpStep } = useMultiStepProgress({ + defaultStep: 3, + totalSteps: 4, + }); return ( - + > + true} + jumpStep={jumpStep} + > + + + +
+
+ Validate your cloud provider's credentials +
+ {il ? ( +
+ +
+ ) : data?.result ? ( +
+ }> + Your Credential is valid + +
+ ) : ( +
+
+ Account ID + + {cloudProvider.aws?.awsAccountId} + +
+
+ + + + visit the link above or + + to create AWS cloudformation stack + +
+
+ )} +
+
+
+
+ +
+ + ); }; 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 2ed12a137..5d599a4b9 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 @@ -10,8 +10,11 @@ import { handleError } from '~/root/lib/utils/common'; import { useState } from 'react'; import { useConsoleApi } from '~/console/server/gql/api-provider'; import { validateCloudProvider } from '~/console/server/r-utils/common'; -import ProgressWrapper from '~/console/components/progress-wrapper'; import { NameIdView } from '~/console/components/name-id-view'; +import MultiStepProgressWrapper from '~/console/components/multi-step-progress-wrapper'; +import MultiStepProgress, { + useMultiStepProgress, +} from '~/console/components/multi-step-progress'; const NewCloudProvider = () => { const { a: accountName } = useParams(); @@ -80,92 +83,89 @@ const NewCloudProvider = () => { }, }); - const progressItems = [ - { - label: 'Create Team', - active: false, - completed: true, - }, - { - label: 'Add your Cloud Provider', - active: true, - completed: false, - children: ( -
-
- A cloud provider offers remote computing resources and services over - the internet. -
-
- -
- { + handleChange('provider')(dummyEvent(value)); + }} + options={async () => providers} + /> + + {values.provider.value === 'aws' && ( + + )} +
+
+
+
+
+ + + + + + ); }; 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 8d775f13c..cf71f78d3 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 @@ -134,12 +134,11 @@ const AppCompute = () => { (async () => { const res = await submit(); if (res) { - setPage('Environment'); - markPageAsCompleted('Compute'); + setPage(3); + markPageAsCompleted(2); } })(); }} - className="py-3xl" >
Compute refers to the processing power and resources used for data @@ -396,7 +395,7 @@ const AppCompute = () => {
*/} -
+
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-environment.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-environment.tsx index 7bf68b801..502627173 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-environment.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-environment.tsx @@ -33,7 +33,7 @@ const AppEnvironment = () => { ]; return ( - +
{ -
+
diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-network.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-network.tsx index 27d547c4f..ef0ad5eed 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-network.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/new-app/app-network.tsx @@ -234,21 +234,20 @@ export const ExposedPorts = () => { const AppNetwork = () => { const { setPage, markPageAsCompleted } = useAppState(); - console.log('networkd'); return ( - +
Expose service ports that need to be exposed from container
-
+
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 9fca4b87e..be67e0a45 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 @@ -171,13 +171,13 @@ const AppReview = () => { })}
)} -
+