diff --git a/Taskfile.yaml b/Taskfile.yaml index b210e68ff..bdac9c97d 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -8,7 +8,7 @@ tasks: interactive: true cmds: - | - BASE_URL=dev.kloudlite.io + BASE_URL=gcp-production.kloudlite.io COOKIE_DOMAIN=".kloudlite.io" GATEWAY_URL="http://gateway.kloudlite.svc.cluster.local" case {{.app}} in @@ -55,7 +55,7 @@ tasks: esac - REMIX_DEV_ORIGIN="https://{{.app}}$URL_SUFFIX.dev.kloudlite.io" + REMIX_DEV_ORIGIN="https://{{.app}}$URL_SUFFIX.$BASE_URL" cp -r ./static/common/. ./public/ cp -r ./static/{{.app}}/. ./public/ diff --git a/gql-queries-generator/doc/queries.graphql b/gql-queries-generator/doc/queries.graphql index 0a4a45977..0413536dc 100644 --- a/gql-queries-generator/doc/queries.graphql +++ b/gql-queries-generator/doc/queries.graphql @@ -77,6 +77,7 @@ query consoleGetAccount($accountName: String!) { name annotations } + targetNamespace updateTime contactEmail displayName @@ -300,10 +301,6 @@ query consoleListClusters($search: SearchCluster, $pagination: CursorPaginationI spec { messageQueueTopicName kloudliteRelease - credentialsRef { - namespace - name - } clusterTokenRef { key name @@ -398,18 +395,6 @@ query consoleGetCluster($name: String!) { name namespace } - credentialKeys { - keyAccessKey - keyAWSAccountId - keyAWSAssumeRoleExternalID - keyAWSAssumeRoleRoleARN - keyIAMInstanceProfileRole - keySecretKey - } - credentialsRef { - name - namespace - } kloudliteRelease messageQueueTopicName output { @@ -480,15 +465,15 @@ query consoleListProviderSecrets($search: SearchProviderSecret, $pagination: Cur edges { cursor node { - aws { - awsAccountId - } cloudProviderName createdBy { userEmail userId userName } + aws { + authMechanism + } creationTime displayName lastUpdatedBy { @@ -528,9 +513,6 @@ mutation consoleDeleteProviderSecret($secretName: String!) { query consoleGetProviderSecret($name: String!) { infra_getProviderSecret(name: $name) { - aws { - awsAccountId - } cloudProviderName createdBy { userEmail @@ -3250,8 +3232,6 @@ query consoleListManagedResources($projectName: String!, $envName: String!, $sea } creationTime displayName - enabled - environmentName lastUpdatedBy { userEmail userId @@ -3259,13 +3239,17 @@ query consoleListManagedResources($projectName: String!, $envName: String!, $sea } markedForDeletion metadata { + annotations + creationTimestamp + deletionTimestamp generation + labels name namespace } - projectName recordVersion spec { + resourceName resourceTemplate { apiVersion kind @@ -3293,6 +3277,12 @@ query consoleListManagedResources($projectName: String!, $envName: String!, $sea namespace } } + syncedOutputSecretRef { + metadata { + name + namespace + } + } syncStatus { action error diff --git a/lib/client/helpers/socket/context.tsx b/lib/client/helpers/socket/context.tsx index ccb5a23d9..0fbbc6045 100644 --- a/lib/client/helpers/socket/context.tsx +++ b/lib/client/helpers/socket/context.tsx @@ -56,7 +56,7 @@ const Context = createContext<{ }); export const useSubscribe = ( - msg: ISocketMsg, + msg: ISocketMsg | ISocketMsg[], dep: never[] ) => { const { @@ -74,8 +74,31 @@ export const useSubscribe = ( useEffect(() => { (async () => { - setResp(responses[msg.for]?.[msg.data.id || 'default'] || []); + if (Array.isArray(msg)) { + setResp(resp); + + const tr: ISocketResp[] = []; + const terr: ISocketResp[] = []; + const ti: ISocketResp[] = []; + + for (let k = 0; k < msg.length; k += 1) { + const m = msg[k]; + + tr.push(...(responses[m.for]?.[m.data.id || 'default'] || [])); + terr.push(...(e[m.for]?.[m.data.id || 'default'] || [])); + ti.push(...(i[m.for]?.[m.data.id || 'default'] || [])); + } + setResp(tr); + setErrors(terr); + setInfos(ti); + if (tr.length || ti.length) { + setSubscribed(true); + } + return; + } + + setResp(responses[msg.for]?.[msg.data.id || 'default'] || []); setErrors(e[msg.for]?.[msg.data.id || 'default'] || []); setInfos(i[msg.for]?.[msg.data.id || 'default'] || []); @@ -88,10 +111,25 @@ export const useSubscribe = ( useDebounce( () => { console.log('subscribing'); - sendMsg({ ...msg, data: { ...msg.data, event: 'subscribe' } }); + if (Array.isArray(msg)) { + msg.forEach((m) => { + sendMsg({ ...m, data: { ...m.data, event: 'subscribe' } }); + }); + } else { + sendMsg({ ...msg, data: { ...msg.data, event: 'subscribe' } }); + } return () => { console.log('unsubscribing'); + if (Array.isArray(msg)) { + msg.forEach((m) => { + clear(m); + setSubscribed(false); + sendMsg({ ...m, data: { ...m.data, event: 'unsubscribe' } }); + }); + return; + } + clear(msg); setSubscribed(false); sendMsg({ ...msg, data: { ...msg.data, event: 'unsubscribe' } }); diff --git a/lib/client/helpers/socket/useWatch.tsx b/lib/client/helpers/socket/useWatch.tsx index 17436a090..0b80d9176 100644 --- a/lib/client/helpers/socket/useWatch.tsx +++ b/lib/client/helpers/socket/useWatch.tsx @@ -1,19 +1,29 @@ -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { ISocketResp, useSubscribe } from './context'; import { useReload } from '../reloader'; export const useSocketWatch = ( onUpdate: (v: ISocketResp[]) => void, - topic: string + topic: string | string[] ) => { const { responses, subscribed } = useSubscribe( - { - for: 'resource-update', - data: { - id: topic, - respath: topic, - }, - }, + Array.isArray(topic) + ? topic.map((t) => { + return { + for: 'resource-update', + data: { + id: t, + respath: t, + }, + }; + }) + : { + for: 'resource-update', + data: { + id: topic, + respath: topic, + }, + }, [] ); @@ -24,12 +34,23 @@ export const useSocketWatch = ( }, [responses]); }; -export const useWatchReload = (topic: string) => { +export const useWatchReload = (topic: string | string[]) => { const reloadPage = useReload(); + const topicMap: { + [key: string]: boolean; + } = useCallback( + () => + Array.isArray(topic) + ? topic.reduce((acc, curr) => { + return { ...acc, [curr]: true }; + }, {}) + : { [topic]: true }, + [topic] + )(); + useSocketWatch((rd) => { - console.log(rd); - if (rd.find((v) => v.id === topic)) { - console.log('reloading due to watch event', rd); + if (rd.find((v) => topicMap[v.id])) { + console.log('reloading due to watch event'); reloadPage(); } }, topic); diff --git a/src/apps/console/components/code-view.tsx b/src/apps/console/components/code-view.tsx index 290460987..232b74597 100644 --- a/src/apps/console/components/code-view.tsx +++ b/src/apps/console/components/code-view.tsx @@ -14,7 +14,7 @@ interface ICodeView { const CodeView = ({ data, copy, - showShellPrompt, + showShellPrompt: _, language = 'shell', title, }: ICodeView) => { @@ -55,7 +55,7 @@ const CodeView = ({ }} className="group/sha cursor-pointer p-lg rounded-md bodyMd flex flex-row gap-xl items-center hljs w-full" > -
+          
             {data}
           
diff --git a/src/apps/console/components/commons.tsx b/src/apps/console/components/commons.tsx index ab26a947f..5dde3b8f5 100644 --- a/src/apps/console/components/commons.tsx +++ b/src/apps/console/components/commons.tsx @@ -1,21 +1,21 @@ -import { CopySimple, Question } from '@jengaicons/react'; -import { ReactNode, useState } from 'react'; -import { ProdLogo } from '~/components/branding/prod-logo'; -import { WorkspacesLogo } from '~/components/branding/workspace-logo'; -import useClipboard from '~/root/lib/client/hooks/use-clipboard'; -import { generateKey, titleCase } from '~/components/utils'; +import { CopySimple, Question } from "@jengaicons/react"; +import { ReactNode, useState } from "react"; +import { ProdLogo } from "~/components/branding/prod-logo"; +import { WorkspacesLogo } from "~/components/branding/workspace-logo"; +import useClipboard from "~/root/lib/client/hooks/use-clipboard"; +import { generateKey, titleCase } from "~/components/utils"; import { Github__Com___Kloudlite___Api___Pkg___Types__SyncState as SyncState, Github__Com___Kloudlite___Api___Pkg___Types__SyncAction as SyncAction, -} 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'; +} 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'; +} from "../server/r-utils/common"; import { ArrowLeft, ArrowRight, @@ -23,8 +23,8 @@ import { GitBranchFill, GitlabLogoFill, GithubLogoFill, -} from './icons'; -import { IGIT_PROVIDERS } from '../hooks/use-git'; +} from "./icons"; +import { IGIT_PROVIDERS } from "../hooks/use-git"; export const BlackProdLogo = ({ size = 16 }) => { return ; @@ -129,7 +129,7 @@ interface IUpdateMeta { } // Component for Status parsing -export type IStatus = 'deleting' | 'notready' | 'syncing' | 'none'; +export type IStatus = "deleting" | "notready" | "syncing" | "none"; interface IStatusMeta { markedForDeletion?: boolean; @@ -145,17 +145,17 @@ interface IStatusMeta { }; } -type IResourceType = 'nodepool'; +type IResourceType = "nodepool"; type ICommonMeta = IUpdateMeta & IStatusMeta; const parseStatusComponent = ({ status }: { status: IStatus }) => { switch (status) { - case 'deleting': + case "deleting": return
Deleting...
; - case 'notready': + case "notready": return
Not Ready
; - case 'syncing': + case "syncing": return
Syncing
; default: return null; @@ -169,17 +169,17 @@ export const parseStatus = ({ item: IStatusMeta; type?: IResourceType; }) => { - let status: IStatus = 'none'; + let status: IStatus = "none"; if (item.markedForDeletion) { - status = 'deleting'; + status = "deleting"; } else if (!item.status?.isReady) { switch (type) { - case 'nodepool': - status = 'syncing'; + case "nodepool": + status = "syncing"; break; default: - status = 'notready'; + status = "notready"; } } @@ -200,7 +200,7 @@ export const listRender = ({ time: parseUpdateOrCreatedOn(resource), }; return { - key: generateKey(keyPrefix, 'author'), + key: generateKey(keyPrefix, "author"), className, render: () => ( @@ -215,7 +215,7 @@ export const listRender = ({ type?: IResourceType; }) => { return { - key: generateKey(keyPrefix, 'status'), + key: generateKey(keyPrefix, "status"), className, render: () => parseStatus({ item: resource, type }).component, status: parseStatus({ item: resource, type }).status, @@ -249,8 +249,8 @@ export const BottomNavigation = ({ primaryButton, secondaryButton, }: { - primaryButton?: Optional; - secondaryButton?: Optional; + primaryButton?: Optional; + secondaryButton?: Optional; }) => { return (
@@ -283,7 +283,7 @@ interface IReviewComponent { canEdit?: boolean; } export const ReviewComponent = ({ - title = '', + title = "", children, onEdit, canEdit = true, @@ -327,13 +327,13 @@ export const GitDetail = ({
Source
- {provider === 'github' ? ( + {provider === "github" ? ( ) : ( )} - {repository.replace('https://', '').replace('.git', '')} + {repository.replace("https://", "").replace(".git", "")}
diff --git a/src/apps/console/components/console-list-components.tsx b/src/apps/console/components/console-list-components.tsx index 7135a10d0..5e239c1b7 100644 --- a/src/apps/console/components/console-list-components.tsx +++ b/src/apps/console/components/console-list-components.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { ReactNode, forwardRef } from 'react'; import Tooltip from '~/components/atoms/tooltip'; import { cn } from '~/components/utils'; @@ -72,16 +72,37 @@ const ListItem = ({ return (
- {data && ( -
- {data} -
- )} - {subtitle && ( -
- {subtitle} + + {data && ( +
+ {data} +
+ )} + {subtitle && ( +
+ {subtitle} +
+ )} +
+ } + > +
+ {data && ( +
+ {data} +
+ )} + {subtitle && ( +
+ {subtitle} +
+ )}
- )} +
{action}
@@ -106,23 +127,39 @@ const ListTitle = ({
{avatar}
- {title && ( -
- {title}
} - > - {title} - -
- )} + + {title && ( +
+ {title} +
+ )} - {subtitle && ( -
- {subtitle} + {subtitle && ( +
+ {subtitle} +
+ )} +
+ } + > +
+ {title && ( +
+ {title} +
+ )} + + {subtitle && ( +
+ {subtitle} +
+ )}
- )} +
{action} @@ -132,7 +169,7 @@ const ListTitle = ({ const listFlex = ({ key }: { key: string }) => ({ key, - className: 'basis-full', + className: 'flex-1', render: () =>
, }); diff --git a/src/apps/console/components/empty-state.tsx b/src/apps/console/components/empty-state.tsx index 4f56f7924..009afc075 100644 --- a/src/apps/console/components/empty-state.tsx +++ b/src/apps/console/components/empty-state.tsx @@ -12,6 +12,7 @@ interface EmptyStateProps { shadow?: boolean; border?: boolean; compact?: boolean; + padding?: boolean; } export const EmptyState = ({ @@ -24,21 +25,24 @@ export const EmptyState = ({ shadow = true, border = true, compact = false, + padding = true, }: EmptyStateProps) => { return (
{image && image} -
+
{heading &&
{heading}
} {children && (
{children}
diff --git a/src/apps/console/components/git.tsx b/src/apps/console/components/git.tsx index 1bfa2a4b1..649e9332b 100644 --- a/src/apps/console/components/git.tsx +++ b/src/apps/console/components/git.tsx @@ -17,6 +17,7 @@ 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 { toast } from '~/components/molecule/toast'; import { ILoginUrls, ILogins } from '../server/gql/queries/git-queries'; import Pulsable from './pulsable'; import useGit, { IGIT_PROVIDERS } from '../hooks/use-git'; @@ -286,7 +287,6 @@ const Git = ({ useEffect(() => { window.addEventListener('message', (e) => { - console.log(e); if (e.data) { const { type, status, provider } = e.data; if (type === 'add-provider' && status === 'success') { @@ -307,6 +307,12 @@ const Git = ({ } }, [showProviderOverlay]); + useEffect(() => { + if (!!installations.error || !!repos.error || !!branches.error) { + toast.error(installations.error || repos.error || branches.error); + } + }, [installations.error, repos.error, branches.error]); + return (
@@ -320,6 +326,7 @@ const Git = ({ options={async () => accountsModified} value={org} onChange={(res) => { + console.log(res); switch (res.value) { case extraAddOption: popupWindow({ @@ -330,7 +337,7 @@ const Git = ({ setShowProviderOverlay(true); break; default: - setOrg(res.label); + setOrg(res.value); break; } }} @@ -425,6 +432,9 @@ const Git = ({ installations.isLoading || repos.isLoading || branches.isLoading || + branches.error || + repos.error || + installations.error || loading } > diff --git a/src/apps/console/components/icons.tsx b/src/apps/console/components/icons.tsx index 7133b9fdc..6bd74f5c6 100644 --- a/src/apps/console/components/icons.tsx +++ b/src/apps/console/components/icons.tsx @@ -1,24 +1,3 @@ -import { - ArrowLeftFill, - ArrowRightFill, - Plus, - Trash, - PencilLine, - Check, - GithubLogoFill, - GitBranchFill, - GitlabLogoFill, - Users, - ChevronLeft, - ChevronRight, - X, - SmileySad, - InfoFill, - CheckCircleFill, - WarningFill, - WarningOctagonFill, -} from '@jengaicons/react'; - export { ArrowLeftFill as ArrowLeft, ArrowRightFill as ArrowRight, @@ -38,4 +17,26 @@ export { CheckCircleFill, WarningFill, WarningOctagonFill, -}; + LockSimple, + XCircleFill, + LockSimpleOpen, + MinusCircle, + Search, + ArrowsCounterClockwise, + Copy, + GearSix, + QrCode, + WireGuardlogo, + ChevronUpDown, + ChevronDown, + Buildings, + Project, + InfraAsCode, + Container, + File, + TreeStructure, + CirclesFour, + BackingServices, + VirtualMachine, + Database, +} from '@jengaicons/react'; diff --git a/src/apps/console/components/key-value-pair.tsx b/src/apps/console/components/key-value-pair.tsx index 1d329a3f9..733e6f768 100644 --- a/src/apps/console/components/key-value-pair.tsx +++ b/src/apps/console/components/key-value-pair.tsx @@ -1,9 +1,9 @@ -import { MinusCircle } from '@jengaicons/react'; import { ReactNode, useEffect, useState } from 'react'; import AnimateHide from '~/components/atoms/animate-hide'; import { Button, IconButton } from '~/components/atoms/button'; import { TextInput } from '~/components/atoms/input'; import { cn, uuid } from '~/components/utils'; +import { MinusCircle, Plus } from '~/console/components/icons'; interface IKeyValuePair { onChange?( @@ -15,6 +15,7 @@ interface IKeyValuePair { message?: ReactNode; error?: boolean; size?: 'lg' | 'md'; + addText?: string; } const KeyValuePair = ({ onChange, @@ -23,6 +24,7 @@ const KeyValuePair = ({ message, error, size, + addText, }: IKeyValuePair) => { const newItem = [{ key: '', value: '', id: uuid() }]; const [items, setItems] = useState>>(newItem); @@ -121,8 +123,9 @@ const KeyValuePair = ({
{fillerImage ? ( -
{fillerImage}
+
+
+ {fillerImage} +
+
) : (
)} diff --git a/src/apps/console/hooks/use-git.tsx b/src/apps/console/hooks/use-git.tsx index b4ef489e2..03b8f4841 100644 --- a/src/apps/console/hooks/use-git.tsx +++ b/src/apps/console/hooks/use-git.tsx @@ -82,10 +82,11 @@ const useGit = ({ const revalidate = (prov?: IGIT_PROVIDERS) => { if (prov) { - setProvider(prov); + console.log('here'); if (provider === prov) { setRevalidate((prev) => prev + 1); } + setProvider(prov); } else { setRevalidate((prev) => prev + 1); } @@ -102,7 +103,7 @@ const useGit = ({ ); const repos = useCustomSwr( - () => (org ? `${data[provider].repoKey}_${org}_${debouncedSearch}` : null), + () => (org ? `repos_${org}_${debouncedSearch}` : null), async () => { switch (provider) { case 'github': @@ -136,7 +137,7 @@ const useGit = ({ } ); const branches = useCustomSwr( - () => (repo ? `${data[provider].branchKey}_${repo}` : null), + () => (repo ? `branches_${repo}` : null), async () => { switch (provider) { case 'github': diff --git a/src/apps/console/page-components/app-states.tsx b/src/apps/console/page-components/app-states.tsx index 221c2ec92..fe6d5ccf9 100644 --- a/src/apps/console/page-components/app-states.tsx +++ b/src/apps/console/page-components/app-states.tsx @@ -6,8 +6,9 @@ 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"; +import { mapper } from '~/components/utils'; +import { parseNodes } from '~/console/server/r-utils/common'; +// import logger from '~/root/lib/client/helpers/log'; const defaultApp: AppIn = { metadata: { @@ -192,29 +193,28 @@ export const useAppState = () => { }; const getRepoMapper = (resources: IparseNodes | undefined) => { - return useMapper(parseNodes(resources), (val) => ({ + return mapper(parseNodes(resources), (val) => ({ label: val.name, value: val.name, - accName: val.accountName - })) - } + accName: val.accountName, + })); + }; const getRepoName = (imageUrl: string) => { const parts: string[] = imageUrl.split(':'); const repoParts: string[] = parts[0].split('/'); - if (repoParts.length == 1) { + if (repoParts.length === 1) { return repoParts[repoParts.length - 1]; - } else { - const repoSlicePart: string[] = repoParts.slice(2) - return repoSlicePart.join("/") } - } + const repoSlicePart: string[] = repoParts.slice(2); + return repoSlicePart.join('/'); + }; const getImageTag = (imageUrl: string) => { const parts: string[] = imageUrl.split(':'); - console.log("image tag", parts[1]) + // logger.log('image tag', parts[1]); return parts[1]; - } + }; return { resetState, @@ -236,7 +236,7 @@ export const useAppState = () => { setServices, getRepoMapper, getRepoName, - getImageTag + getImageTag, }; }; @@ -278,7 +278,7 @@ export const AppContextProvider = ({ useEffect(() => { if (typeof window === 'undefined' || initialAppState) return; - console.log(initialAppState, 'hrere'); + // logger.log(initialAppState, 'hrere'); sessionStorage.setItem('state', JSON.stringify(state || {})); }, [state]); diff --git a/src/apps/console/page-components/config-resource.tsx b/src/apps/console/page-components/config-resource.tsx index c94005fc7..0ab1f9c3a 100644 --- a/src/apps/console/page-components/config-resource.tsx +++ b/src/apps/console/page-components/config-resource.tsx @@ -6,6 +6,7 @@ import { generateKey, titleCase } from '~/components/utils'; import List from '~/console/components/list'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { handleError } from '~/root/lib/utils/common'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; import { ListBody, ListItem, @@ -224,7 +225,16 @@ const ConfigResources = ({ const api = useConsoleApi(); const reloadPage = useReload(); - const { project, environment } = useParams(); + const { project, environment, account } = useParams(); + + useWatchReload( + items.map((i) => { + return `account:${account}.project:${project}.environment:${environment}.config:${parseName( + i + )}`; + }) + ); + const props: IResource = { items, hasActions, diff --git a/src/apps/console/page-components/handle-console-devices.tsx b/src/apps/console/page-components/handle-console-devices.tsx index 8ed927edc..99e777544 100644 --- a/src/apps/console/page-components/handle-console-devices.tsx +++ b/src/apps/console/page-components/handle-console-devices.tsx @@ -442,32 +442,32 @@ const Root = (props: IDialog) => { useForm({ initialValues: isUpdate ? { - displayName: props.data.displayName, - name: parseName(props.data), - ports: props.data.spec?.ports || [], - isNameError: false, - projectName: props.data.projectName, - environmentName: props.data.environmentName, - } + displayName: props.data.displayName, + name: parseName(props.data), + ports: props.data.spec?.ports || [], + isNameError: false, + projectName: props.data.projectName, + environmentName: props.data.environmentName, + } : { - displayName: '', - name: '', - ports: [], - isNameError: false, - projectName: '', - environmentName: '', - }, + displayName: '', + name: '', + ports: [], + isNameError: false, + projectName: '', + environmentName: '', + }, validationSchema: isUpdate ? Yup.object({ - name: Yup.string().required(), - displayName: Yup.string().required(), - projectName: Yup.string().required(), - environmentName: Yup.string().required(), - }) + name: Yup.string().required(), + displayName: Yup.string().required(), + projectName: Yup.string().required(), + environmentName: Yup.string().required(), + }) : Yup.object({ - name: Yup.string().required(), - displayName: Yup.string().required(), - }), + name: Yup.string().required(), + displayName: Yup.string().required(), + }), onSubmit: async (val) => { try { if (!isUpdate) { diff --git a/src/apps/console/page-components/new-cluster.tsx b/src/apps/console/page-components/new-cluster.tsx index 6417bb714..31a2fef63 100644 --- a/src/apps/console/page-components/new-cluster.tsx +++ b/src/apps/console/page-components/new-cluster.tsx @@ -1,4 +1,4 @@ -import { useNavigate, useParams } from '@remix-run/react'; +import { useNavigate, useOutletContext, useParams } from '@remix-run/react'; import { useMemo, useState } from 'react'; import Select from '~/components/atoms/select'; import { toast } from '~/components/molecule/toast'; @@ -28,6 +28,7 @@ import MultiStepProgressWrapper from '../components/multi-step-progress-wrapper' import { TitleBox } from '../components/raw-wrapper'; import { BottomNavigation, ReviewComponent } from '../components/commons'; import FillerCluster from '../assets/filler-cluster'; +import { IAccountContext } from '../routes/_main+/$account+/_layout'; type props = | { @@ -50,6 +51,8 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { [providerSecrets] ); + const { account } = useOutletContext(); + const options = useMapper(cloudProviders, (provider) => ({ value: parseName(provider), label: provider.displayName, @@ -122,15 +125,19 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { spec: { cloudProvider: validateClusterCloudProvider(val.cloudProvider), aws: { + credentials: { + authMechanism: 'secret_keys', + secretRef: { + name: val.credentialsRef, + namespace: account.targetNamespace, + }, + }, region: selectedRegion.Name, k3sMasters: { nvidiaGpuEnabled: true, instanceType: 'c6a.xlarge', }, }, - credentialsRef: { - name: val.credentialsRef, - }, availabilityMode: validateAvailabilityMode( val.availabilityMode ), @@ -345,10 +352,10 @@ export const NewCluster = ({ providerSecrets, cloudProvider }: props) => { step={2} label="Add your cloud provider" /> - + {/* */} {getView()} diff --git a/src/apps/console/page-components/secret-resource.tsx b/src/apps/console/page-components/secret-resource.tsx index e48ec2f5f..2a9c332ad 100644 --- a/src/apps/console/page-components/secret-resource.tsx +++ b/src/apps/console/page-components/secret-resource.tsx @@ -12,6 +12,7 @@ import { } from '~/console/server/r-utils/common'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { handleError } from '~/root/lib/utils/common'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; import { ListBody, ListItem, @@ -224,7 +225,16 @@ const SecretResources = ({ const api = useConsoleApi(); const reloadPage = useReload(); - const { project, environment } = useParams(); + const { project, environment, account } = useParams(); + + useWatchReload( + items.map((i) => { + return `account:${account}.project:${project}.environment:${environment}.secret:${parseName( + i + )}`; + }) + ); + const props: IResource = { items, hasActions, diff --git a/src/apps/console/routes/_a+/new-team.tsx b/src/apps/console/routes/_a+/new-team.tsx index e849a3b34..31dfd877f 100644 --- a/src/apps/console/routes/_a+/new-team.tsx +++ b/src/apps/console/routes/_a+/new-team.tsx @@ -89,7 +89,7 @@ const NewAccount = () => {
- + {/* */} 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 dd9c55132..ba5f98eaa 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 @@ -1,5 +1,3 @@ -/* eslint-disable react/no-unescaped-entities */ -/* eslint-disable no-nested-ternary */ import { IRemixCtx } from '~/root/lib/types/common'; import { useLoaderData, useNavigate, useOutletContext } from '@remix-run/react'; import { defer } from '@remix-run/node'; @@ -22,6 +20,10 @@ import MultiStepProgress, { } from '~/console/components/multi-step-progress'; import { Check } from '~/console/components/icons'; import { BottomNavigation } from '~/console/components/commons'; +import useForm from '~/root/lib/client/hooks/use-form'; +import Yup from '~/root/lib/server/helpers/yup'; +import { PasswordInput } from '~/components/atoms/input'; +import FillerCloudProvider from '~/console/assets/filler-cloud-provider'; import { IAccountContext } from '../../../../_main+/$account+/_layout'; export const loader = async (ctx: IRemixCtx) => { @@ -64,25 +66,90 @@ const Validator = ({ cloudProvider }: { cloudProvider: any }) => { const [isLoading, setIsLoading] = useState(false); const { data, isLoading: il } = useCustomSwr( - () => cloudProvider.metadata!.name + isLoading, + () => parseName(cloudProvider) + isLoading, async () => { - if (!cloudProvider.metadata!.name) { + if (!parseName(cloudProvider.metadata!.name)) { throw new Error('Invalid cloud provider name'); } return api.checkAwsAccess({ - cloudproviderName: cloudProvider.metadata.name, + cloudproviderName: parseName(cloudProvider), }); } ); + const { values, handleChange, errors, handleSubmit } = useForm({ + initialValues: { + accessKey: '', + secretKey: '', + }, + validationSchema: Yup.object({ + accessKey: Yup.string().test( + 'provider', + 'access key is required', + // @ts-ignores + // eslint-disable-next-line react/no-this-in-sfc + function (item) { + return data?.result || item; + } + ), + secretKey: Yup.string().test( + 'provider', + 'secret key is required', + // eslint-disable-next-line func-names + // @ts-ignore + function (item) { + return data?.result || item; + } + ), + }), + onSubmit: async (val) => { + if (data?.result) { + navigate( + `/onboarding/${parseName(account)}/${parseName( + cloudProvider + )}/new-cluster` + ); + return; + } + + try { + const { errors } = await api.updateProviderSecret({ + secret: { + metadata: { + name: parseName(cloudProvider), + }, + cloudProviderName: cloudProvider.cloudProviderName, + displayName: cloudProvider.displayName, + aws: { + authMechanism: 'secret_keys', + authSecretKeys: { + accessKey: val.accessKey, + secretKey: val.secretKey, + }, + }, + }, + }); + + if (errors) { + throw errors[0]; + } + + setIsLoading((s) => !s); + } catch (err) { + handleError(err); + } + }, + }); + const { currentStep, jumpStep } = useMultiStepProgress({ defaultStep: 3, totalSteps: 4, }); return ( -
+
} title="Setup your account!" subTitle="Simplify Collaboration and Enhance Productivity with Kloudlite teams" @@ -101,10 +168,11 @@ const Validator = ({ cloudProvider }: { cloudProvider: any }) => { /> -
+
- Validate your cloud provider's credentials + Validate your cloud provider's credentials
+ {/* eslint-disable-next-line no-nested-ternary */} {il ? (
{ ) : (
- Account ID + + {cloudProvider.displayName} + - {cloudProvider.aws?.awsAccountId} + ({parseName(cloudProvider)})
@@ -161,27 +231,65 @@ const Validator = ({ cloudProvider }: { cloudProvider: any }) => { to create AWS cloudformation stack
+ + {!data?.result && ( + <> +
+ Once you have created the cloudformation stack, please + enter the access key and secret key below to validate + your cloud Provider, you can get the access key and + secret key from the output of the cloudformation stack. +
+ + + + + + )}
)} { + navigate( + `/onboarding/${parseName(account)}/${parseName( + cloudProvider + )}/new-cluster` + ); + }, + } + } primaryButton={{ variant: 'primary', - content: data?.result ? 'Next' : 'Skip', - onClick: () => { - navigate( - `/onboarding/${parseName(account)}/${parseName( - cloudProvider - )}/new-cluster` - ); - }, + content: data?.result ? 'Continue' : 'Update', + type: 'submit', }} /> -
+ - +
); }; 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 ec7842c19..454a25db2 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 @@ -1,5 +1,4 @@ import { useNavigate, useParams } from '@remix-run/react'; -import { TextInput } from '~/components/atoms/input'; import Select from '~/components/atoms/select'; import { toast } from '~/components/molecule/toast'; import useForm, { dummyEvent } from '~/root/lib/client/hooks/use-form'; @@ -29,14 +28,12 @@ const NewCloudProvider = () => { displayName: '', name: '', provider: providers[0].value, - awsAccountId: '', isNameError: false, }, validationSchema: Yup.object({ displayName: Yup.string().required(), name: Yup.string().required(), provider: Yup.string().required(), - awsAccountId: Yup.string().required('AccountId is required.'), }), onSubmit: async (val) => { const addProvider = async () => { @@ -49,7 +46,7 @@ const NewCloudProvider = () => { name: val.name, }, aws: { - awsAccountId: val.awsAccountId, + authMechanism: 'secret_keys', }, cloudProviderName: validateCloudProvider(val.provider), }, @@ -73,7 +70,7 @@ const NewCloudProvider = () => { toast.success('provider secret created successfully'); - navigate(`/onboarding/${accountName}/${val.name}/validate-cp`); + navigate(`/onboarding/${accountName}/${values.name}/validate-cp`); } catch (err) { handleError(err); } @@ -134,18 +131,6 @@ const NewCloudProvider = () => { }} options={async () => providers} /> - - {values.provider === 'aws' && ( - - )}
{ />
- + {/* */} diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx index f2d81e619..eca956c1c 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/_layout.tsx @@ -1,14 +1,15 @@ import { BackingServices, - ChevronDown, CirclesFour, - Database, GearSix, Plus, Search, File, TreeStructure, -} from '@jengaicons/react'; + Check, + ChevronUpDown, + ChevronDown, +} from '~/console/components/icons'; import { redirect } from '@remix-run/node'; import { Link, @@ -17,9 +18,8 @@ import { useOutletContext, useParams, } from '@remix-run/react'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; import OptionList from '~/components/atoms/option-list'; -import Breadcrum from '~/console/components/breadcrum'; import { CommonTabs } from '~/console/components/common-navbar-tabs'; import HandleScope from '~/console/page-components/new-scope'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; @@ -39,17 +39,14 @@ import useDebounce from '~/root/lib/client/hooks/use-debounce'; import { IRemixCtx } from '~/root/lib/types/common'; import { handleError } from '~/root/lib/utils/common'; import { useConsoleApi } from '~/console/server/gql/api-provider'; -import { - BreadcrumButtonContent, - BreadcrumSlash, - tabIconSize, -} from '~/console/utils/commons'; +import { BreadcrumSlash, tabIconSize } from '~/console/utils/commons'; import { IEnvironment, IEnvironments, } from '~/console/server/gql/queries/environment-queries'; -import { useActivePath } from '~/root/lib/client/hooks/use-active-path'; import { cn } from '~/components/utils'; +import { Button } from '~/components/atoms/button'; +import useCustomSwr from '~/root/lib/client/hooks/use-custom-swr'; import { IProjectContext } from '../_layout'; export interface IEnvironmentContext extends IProjectContext { @@ -136,64 +133,50 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { const params = useParams(); const [showPopup, setShowPopup] = useState(null); - const [environments, setEnvironments] = useState< - ExtractNodeType[] - >([]); const api = useConsoleApi(); const [search, setSearch] = useState(''); + const [searchText, setSearchText] = useState(''); const { project, account } = params; + const { data: environments, isLoading } = useCustomSwr( + () => `/environments/${searchText}`, + async () => + api.listEnvironments({ + search: { + text: { + matchType: 'regex', + regex: searchText, + }, + }, + projectName: project || '', + }) + ); + useDebounce( - async () => { - ensureClusterClientSide(params); + () => { ensureAccountClientSide(params); - if (!project) { - throw new Error('Project is required.!'); - } - try { - const { data, errors } = await api.listEnvironments({ - projectName: project, - }); - if (errors) { - throw errors[0]; - } - setEnvironments(parseNodes(data)); - } catch (err) { - handleError(err); - } + setSearchText(search); }, 300, [search] ); - const { activePath } = useActivePath({ - parent: `/${account}/${project}/${parseName(environment)}`, - }); + const [open, setOpen] = useState(false); return ( <> - - + + + - - tab.to === activePath) - ? 'bodyMd-semibold' - : '' - )} - content={environment.displayName} - /> - -
- } + variant="plain" + suffix={} /> @@ -203,25 +186,50 @@ const CurrentBreadcrum = ({ environment }: { environment: IEnvironment }) => { onChange={(e) => setSearch(e.target.value)} prefixIcon={} focusRing={false} - placeholder="Search" + placeholder="Search environments" compact className="border-0 rounded-none" />
- - {environments.map((item) => { + {parseNodes(environments)?.map((item) => { return ( - {item.displayName} + {item.displayName} + {parseName(item) === parseName(environment) && ( + + + + )} ); })} + {parseNodes(environments).length === 0 && !isLoading && ( +
+
+ No environments found +
+
+ Your search for "{search}" did not match and environments. +
+
+ )} + + {isLoading && parseNodes(environments).length === 0 && ( +
+ )} + { ); }; + export const handle = ({ environment }: any) => { return { navbar: , diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/apps/apps-resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/apps/apps-resources.tsx index 5dfe56618..53dd6ade7 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/apps/apps-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/apps/apps-resources.tsx @@ -22,7 +22,8 @@ import ResourceExtraAction, { import { useConsoleApi } from '~/console/server/gql/api-provider'; import { IApps } from '~/console/server/gql/queries/app-queries'; import { - ExtractNodeType, parseName, + ExtractNodeType, + parseName, parseName as pn, parseUpdateOrCreatedBy, parseUpdateOrCreatedOn, @@ -31,6 +32,9 @@ import { handleError } from '~/root/lib/utils/common'; import { toast } from '~/components/molecule/toast'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { listStatus } from '~/console/components/sync-status'; +import { IAccountContext } from '~/console/routes/_main+/$account+/_layout'; +import { IProjectContext } from '~/console/routes/_main+/$account+/$project+/_layout'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; import { IEnvironmentContext } from '../_layout'; const RESOURCE_NAME = 'app'; @@ -69,7 +73,9 @@ const ExtraButton = ({ onAction, item }: IExtraButton) => { label: 'Settings', icon: , type: 'item', - to: `/${account}/${project}/${environment}/app/${parseName(item)}/settings/general`, + to: `/${account}/${project}/${environment}/app/${parseName( + item + )}/settings/general`, key: 'settings', }, ]; @@ -233,6 +239,14 @@ const AppsResources = ({ items = [] }: Omit) => { const { devicesForUser } = useOutletContext(); const reload = useReload(); + useWatchReload( + items.map((i) => { + return `account:${account}.project:${project}.environment:${environment}.app:${parseName( + i + )}`; + }) + ); + // useWatchItems(items, (item) => ({ // account, // project, diff --git a/src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/managed-resources-resource.tsx b/src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/managed-resources-resource.tsx index b79f144a4..bd6631ce3 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/managed-resources-resource.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/$environment+/managed-resources/managed-resources-resource.tsx @@ -23,9 +23,11 @@ import { useReload } from '~/root/lib/client/helpers/reloader'; import { useState } from 'react'; import { handleError } from '~/root/lib/utils/common'; import { toast } from '~/components/molecule/toast'; -import { useParams } from '@remix-run/react'; +import { Link, useParams } from '@remix-run/react'; import { IManagedResources } from '~/console/server/gql/queries/managed-resources-queries'; import { listStatus } from '~/console/components/sync-status'; +import { Button } from '~/components/atoms/button'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; import HandleManagedResources from './handle-managed-resource'; const RESOURCE_NAME = 'managed resource'; @@ -122,6 +124,8 @@ const GridView = ({ items = [], onAction }: IResource) => { }; const ListView = ({ items = [], onAction }: IResource) => { + const { environment, project, account } = useParams(); + const preUrl = `/${account}/${project}/${environment}/secret/`; return ( {items.map((item, index) => { @@ -139,6 +143,19 @@ const ListView = ({ items = [], onAction }: IResource) => { render: () => , }, status, + item.syncedOutputSecretRef + ? { + key: generateKey(keyPrefix, 'secret'), + render: () => ( +
- } + to={`/${account}/${parseName(project)}`} /> -
+ {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/*
*/} + {/* setSearch(e.target.value)} */} + {/* prefixIcon={} */} + {/* focusRing={false} */} + {/* placeholder="Search projects" */} + {/* compact */} + {/* className="border-0 rounded-none" */} + {/* /> */} + {/*
*/} + {/* */} + + {/* {!isLoading && */} + {/* (parseNodes(projects) || [])?.map((item) => { */} + {/* return ( */} + {/* */} + {/* {item.displayName} */} + {/* {parseName(item) === parseName(project) && ( */} + {/* */} + {/* */} + {/* */} + {/* )} */} + {/* */} + {/* ); */} + {/* })} */} + + {/* {parseNodes(projects).length === 0 && !isLoading && ( */} + {/*
*/} + {/*
*/} + {/* No projects found */} + {/*
*/} + {/*
*/} + {/* Your search for "{search}" did not match and projects. */} + {/*
*/} + {/*
*/} + {/* )} */} + + {/* {isLoading && parseNodes(projects).length === 0 && ( */} + {/*
*/} + {/* )} */} + + {/* */} + {/* */} + {/* Create project */} + {/* */} + {/* */} + {/* */} + ); }; @@ -138,7 +266,7 @@ export const handle = ({ }) => { return { navbar: , - breadcrum: () => , + breadcrum: () => , logo: , }; }; diff --git a/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx index e1c13f013..45d791ef5 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/environments/resources.tsx @@ -1,5 +1,5 @@ import { Copy, GearSix } from '@jengaicons/react'; -import { Link, useParams } from '@remix-run/react'; +import { Link, useOutletContext, useParams } from '@remix-run/react'; import { useState } from 'react'; import { generateKey, titleCase } from '~/components/utils'; import ConsoleAvatar from '~/console/components/console-avatar'; @@ -21,6 +21,9 @@ import { parseUpdateOrCreatedOn, } from '~/console/server/r-utils/common'; import { listStatus } from '~/console/components/sync-status'; +import { IAccountContext } from '~/console/routes/_main+/$account+/_layout'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; +import { IProjectContext } from '~/console/routes/_main+/$account+/$project+/_layout'; import CloneEnvironment from './clone-environment'; const RESOURCE_NAME = 'workspace'; @@ -166,6 +169,16 @@ const ListView = ({ items, onAction }: IResource) => { }; const Resources = ({ items = [] }: { items: BaseType[] }) => { + const { account } = useOutletContext(); + const { project } = useOutletContext(); + useWatchReload( + items.map((i) => { + return `account:${parseName(account)}.project:${parseName( + project + )}.environment:${parseName(i)}`; + }) + ); + const [visible, setVisible] = useState(null); const props: IResource = { items, diff --git a/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx b/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx index 9f57c99f0..3f5ab607e 100644 --- a/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/$project+/managed-services/backend-services-resources.tsx @@ -24,9 +24,12 @@ import { useReload } from '~/root/lib/client/helpers/reloader'; import { useState } from 'react'; import { handleError } from '~/root/lib/utils/common'; import { toast } from '~/components/molecule/toast'; -import { useParams } from '@remix-run/react'; +import { useOutletContext, useParams } from '@remix-run/react'; import { IProjectMSvs } from '~/console/server/gql/queries/project-managed-services-queries'; import { listStatus } from '~/console/components/sync-status'; +import { IAccountContext } from '~/console/routes/_main+/$account+/_layout'; +import { IProjectContext } from '~/console/routes/_main+/$account+/$project+/_layout'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; import HandleBackendService from './handle-backend-service'; const RESOURCE_NAME = 'managed service'; @@ -198,6 +201,16 @@ const BackendServicesResources = ({ const reloadPage = useReload(); const params = useParams(); + const { account } = useOutletContext(); + const { project } = useOutletContext(); + useWatchReload( + items.map((i) => { + return `account:${parseName(account)}.project:${parseName( + project + )}.project_managed_service:${parseName(i)}`; + }) + ); + const props: IResource = { items, templates, 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 cedf45b6e..3b34f024c 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 @@ -7,9 +7,9 @@ import Wrapper from '~/console/components/wrapper'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; import { parseNodes } from '~/console/server/r-utils/common'; import { IRemixCtx } from '~/root/lib/types/common'; +import fake from '~/root/fake-data-generator/fake'; 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; @@ -42,11 +42,13 @@ const KlOperatorServices = () => { 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 b7aa7d629..f453ecea9 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 @@ -14,7 +14,7 @@ import { import { Switch } from '~/components/atoms/switch'; import { NumberInput, TextInput } from '~/components/atoms/input'; import { handleError } from '~/root/lib/utils/common'; -import {titleCase, useMapper} from '~/components/utils'; +import { titleCase, useMapper } from '~/components/utils'; import { flatMapValidations, flatM } from '~/console/utils/commons'; import MultiStepProgress, { useMultiStepProgress, @@ -24,11 +24,11 @@ import { BottomNavigation, ReviewComponent, } from '~/console/components/commons'; +import { parseName, parseNodes } from '~/console/server/r-utils/common'; +import useCustomSwr from '~/lib/client/hooks/use-custom-swr'; +import { INodepools } from '~/console/server/gql/queries/nodepool-queries'; +import { keyconstants } from '~/console/server/r-utils/key-constants'; import { IProjectContext } from '../_layout'; -import {parseName, parseNodes} from "~/console/server/r-utils/common"; -import useCustomSwr from "~/lib/client/hooks/use-custom-swr"; -import {INodepools} from "~/console/server/gql/queries/nodepool-queries"; -import {keyconstants} from "~/console/server/r-utils/key-constants"; const valueRender = ({ label, icon }: { label: string; icon: string }) => { return ( @@ -254,7 +254,7 @@ const FieldView = ({ values: Record; errors: Record; selectedTemplate: ISelectedTemplate | null; - nodepools: {label: string, value: string}[] + nodepools: { label: string; value: string }[]; }) => { const nameRef = useRef(null); useEffect(() => { @@ -284,17 +284,17 @@ const FieldView = ({ /> provisionTypes} - onChange={(_, value) => { - handleChange('poolType')(dummyEvent(value)); - }} - /> + <> + - mapper( - awsRegions.find((v) => v.Name === clusterRegion)?.Zones || - [], - (v) => ({ - value: v, - label: v, - }) - ) - } - onChange={(_, v) => { - handleChange('awsAvailabilityZone')(dummyEvent(v)); - }} - /> + { - const plan = findNodePlan(values.instanceType); - return plan?.value; - }, [values.instanceType])} - label="Node plan" - options={async () => nodePlans} - onChange={(value) => { - handleChange('instanceType')(dummyEvent(value.value)); - handleChange('nvidiaGpuEnabled')( - dummyEvent(!!value.gpuEnabled) - ); - }} - /> -
+
+
+
+ providerSecrets} - /> -
+ {/*
*/} + {/* {' '} */} + {/* ; @@ -176,6 +179,15 @@ const StorageResources = ({ items = [] }: { items: BaseType[] }) => { const reloadPage = useReload(); const api = useConsoleApi(); + const { account } = useOutletContext(); + useWatchReload( + items.map((i) => { + return `account:${parseName( + account + )}.cluster:${cluster}.persistance_volume:${parseName(i)}`; + }) + ); + const props: IResource = { items, onAction: ({ action, item }) => { 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 9e67f760f..cc89e03c4 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 @@ -1,5 +1,5 @@ import { GearSix } from '@jengaicons/react'; -import { Link, useParams } from '@remix-run/react'; +import { Link, useOutletContext, useParams } from '@remix-run/react'; import { cn, generateKey, titleCase } from '~/components/utils'; import { IStatus, listRender } from '~/console/components/commons'; import ConsoleAvatar from '~/console/components/console-avatar'; @@ -23,6 +23,8 @@ import { } from '~/console/server/r-utils/common'; import { renderCloudProvider } from '~/console/utils/commons'; import logger from '~/root/lib/client/helpers/log'; +import { IAccountContext } from '~/console/routes/_main+/$account+/_layout'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; const RESOURCE_NAME = 'cluster'; @@ -216,6 +218,13 @@ const ClusterResources = ({ }: { items: ExtractNodeType[]; }) => { + const { account } = useOutletContext(); + useWatchReload( + items.map((i) => { + return `account:${parseName(account)}.cluster:${parseName(i)}`; + }) + ); + return ( } diff --git a/src/apps/console/routes/_main+/$account+/projects/project-resources-v2.tsx b/src/apps/console/routes/_main+/$account+/projects/project-resources-v2.tsx new file mode 100644 index 000000000..7f89efb27 --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/projects/project-resources-v2.tsx @@ -0,0 +1,190 @@ +import { GearSix } from '@jengaicons/react'; +import { Link, useOutletContext, useParams } from '@remix-run/react'; +import { generateKey, titleCase } from '~/components/utils'; +import ConsoleAvatar from '~/console/components/console-avatar'; +import { + ListItem, + ListTitle, +} from '~/console/components/console-list-components'; +import Grid from '~/console/components/grid'; +import ListGridView from '~/console/components/list-grid-view'; +import ResourceExtraAction from '~/console/components/resource-extra-action'; +import { listStatus } from '~/console/components/sync-status'; +import { IProjects } from '~/console/server/gql/queries/project-queries'; +import { + ExtractNodeType, + parseName, + parseUpdateOrCreatedBy, + parseUpdateOrCreatedOn, +} from '~/console/server/r-utils/common'; +import { useWatchReload } from '~/root/lib/client/helpers/socket/useWatch'; +import ListV2 from '~/console/components/listV2'; +import { IAccountContext } from '../_layout'; + +type BaseType = ExtractNodeType; +const RESOURCE_NAME = 'project'; + +const parseItem = (item: ExtractNodeType) => { + return { + name: item.displayName, + id: parseName(item), + path: `/projects/${parseName(item)}`, + updateInfo: { + author: `Updated by ${titleCase(parseUpdateOrCreatedBy(item))}`, + time: parseUpdateOrCreatedOn(item), + }, + }; +}; + +const ExtraButton = ({ project }: { project: BaseType }) => { + const { account } = useParams(); + return ( + , + type: 'item', + + to: `/${account}/${parseName(project)}/settings`, + key: 'settings', + }, + ]} + /> + ); +}; + +const GridView = ({ items = [] }: { items: BaseType[] }) => { + const { account } = useParams(); + return ( + + {items.map((item, index) => { + const { name, id, updateInfo } = parseItem(item); + const keyPrefix = `${RESOURCE_NAME}-${id}-${index}`; + return ( + ( + } + avatar={} + /> + ), + }, + { + key: generateKey(keyPrefix, updateInfo.author), + render: () => ( + + ), + }, + ]} + /> + ); + })} + + ); +}; + +const ListView = ({ items }: { items: BaseType[] }) => { + const { account } = useParams(); + return ( + ( +
+ + Name +
+ ), + name: 'name', + className: 'w-[180px]', + }, + { + render: () => '', + name: 'status', + className: 'flex-1 min-w-[30px] flex items-center justify-center', + }, + { + render: () => 'Cluster', + name: 'cluster', + className: 'w-[180px]', + }, + { + render: () => 'Updated', + name: 'updated', + className: 'w-[180px]', + }, + { + render: () => '', + name: 'action', + className: 'w-[24px]', + }, + ], + rows: items.map((i) => { + const { name, id, updateInfo } = parseItem(i); + const tempStatus = listStatus({ + key: '', + item: i, + className: 'text-center', + }); + return { + columns: { + name: { + render: () => ( + } + /> + ), + }, + status: { + render: () => ( +
{tempStatus.render()}
+ ), + }, + cluster: { render: () => }, + updated: { + render: () => ( + + ), + }, + action: { + render: () => , + }, + }, + to: `/${account}/${id}/environments`, + }; + }), + }} + /> + ); +}; + +const ProjectResourcesV2 = ({ items = [] }: { items: BaseType[] }) => { + const { account } = useOutletContext(); + useWatchReload(`account:${parseName(account)}`); + return ( + } + gridView={} + /> + ); +}; + +export default ProjectResourcesV2; diff --git a/src/apps/console/routes/_main+/$account+/projects/project-resources.tsx b/src/apps/console/routes/_main+/$account+/projects/project-resources.tsx index 0beb95125..0b02ec4e4 100644 --- a/src/apps/console/routes/_main+/$account+/projects/project-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/projects/project-resources.tsx @@ -159,7 +159,12 @@ const ListView = ({ items }: { items: BaseType[] }) => { const ProjectResources = ({ items = [] }: { items: BaseType[] }) => { const { account } = useOutletContext(); - useWatchReload(`account:${parseName(account)}`); + useWatchReload( + items.map((i) => { + return `account:${parseName(account)}.project:${parseName(i)}`; + }) + ); + // n account:acc1.project:pro1.environment:env1. return ( } diff --git a/src/apps/console/routes/_main+/$account+/projects/route.tsx b/src/apps/console/routes/_main+/$account+/projects/route.tsx index caa66e659..640dd417d 100644 --- a/src/apps/console/routes/_main+/$account+/projects/route.tsx +++ b/src/apps/console/routes/_main+/$account+/projects/route.tsx @@ -10,8 +10,8 @@ import { IRemixCtx } from '~/root/lib/types/common'; import fake from '~/root/fake-data-generator/fake'; import { ensureAccountSet } from '~/console/server/utils/auth-utils'; import { GQLServerHandler } from '~/console/server/gql/saved-queries'; -import ProjectResources from './project-resources'; import Tools from './tools'; +import ProjectResourcesV2 from './project-resources-v2'; export const loader = (ctx: IRemixCtx) => { const promise = pWrapper(async () => { @@ -189,7 +189,7 @@ const Projects = () => { })} tools={} > - + ); }} diff --git a/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/build-resources.tsx b/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/build-resources.tsx index 293464998..f537ff3e2 100644 --- a/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/build-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/repo+/$repo+/builds/build-resources.tsx @@ -17,12 +17,15 @@ import { useConsoleApi } from '~/console/server/gql/api-provider'; import { IBuilds } from '~/console/server/gql/queries/build-queries'; import { ExtractNodeType, + parseName, parseUpdateOrCreatedBy, parseUpdateOrCreatedOn, } from '~/console/server/r-utils/common'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { handleError } from '~/root/lib/utils/common'; -import { Link } from '@remix-run/react'; +import { IAccountContext } from '~/console/routes/_main+/$account+/_layout'; +import { useWatchReload } from '~/lib/client/helpers/socket/useWatch'; +import { useOutletContext } from '@remix-run/react'; import HandleBuild from './handle-builds'; type BaseType = ExtractNodeType; @@ -187,6 +190,13 @@ const BuildResources = ({ items = [] }: { items: BaseType[] }) => { const api = useConsoleApi(); const reloadPage = useReload(); + const { account } = useOutletContext(); + useWatchReload( + items.map((i) => { + return `account:${parseName(account)}.id:${i.id}`; + }) + ); + const props: IResource = { items, onDelete: (item) => { diff --git a/src/apps/console/routes/_main+/$account+/repo+/$repo+/new-build/build-details.tsx b/src/apps/console/routes/_main+/$account+/repo+/$repo+/new-build/build-details.tsx index 8b7c91063..5bc7f8744 100644 --- a/src/apps/console/routes/_main+/$account+/repo+/$repo+/new-build/build-details.tsx +++ b/src/apps/console/routes/_main+/$account+/repo+/$repo+/new-build/build-details.tsx @@ -102,10 +102,6 @@ const BuildDetails = ({ ref.current?.focus(); }, [ref.current]); - useEffect(() => { - console.log(values.tags); - }, [values.tags]); - return (
({ label: t, value: t })) } onChange={(_, val) => { - console.log(val); handleChange('tags')(dummyEvent(val)); }} error={!!errors.tags} diff --git a/src/apps/console/routes/_main+/$account+/settings+/cloud-providers/handle-provider.tsx b/src/apps/console/routes/_main+/$account+/settings+/cloud-providers/handle-provider.tsx index ddff412dc..fb7617ac4 100644 --- a/src/apps/console/routes/_main+/$account+/settings+/cloud-providers/handle-provider.tsx +++ b/src/apps/console/routes/_main+/$account+/settings+/cloud-providers/handle-provider.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/destructuring-assignment */ import { ReactNode } from 'react'; -import { TextInput } from '~/components/atoms/input'; +import { PasswordInput } from '~/components/atoms/input'; import Select from '~/components/atoms/select'; import Popup from '~/components/molecule/popup'; import { toast } from '~/components/molecule/toast'; @@ -63,28 +63,25 @@ const Root = (props: IDialog) => { displayName: props.data.displayName, name: parseName(props.data), provider: props.data.cloudProviderName as string, - awsAccountId: '', isNameError: false, + accessKey: '', + secretKey: '', } : { displayName: '', name: '', + accessKey: '', + secretKey: '', provider: providers[0].value, - awsAccountId: '', isNameError: false, }, validationSchema: isUpdate ? Yup.object({ displayName: Yup.string().required(), name: Yup.string().required(), - }) - : Yup.object({ - displayName: Yup.string().required(), - name: Yup.string().required(), - provider: Yup.string().required(), - awsAccountId: Yup.string().test( + accessKey: Yup.string().test( 'provider', - 'Account id is required', + 'access key is required', function (item) { return ( // @ts-ignores @@ -96,55 +93,41 @@ const Root = (props: IDialog) => { ); } ), + secretKey: Yup.string().test( + 'provider', + 'secret key is required', + function (item) { + return ( + // @ts-ignores + // eslint-disable-next-line react/no-this-in-sfc + this.parent.provider && + // eslint-disable-next-line react/no-this-in-sfc + this.parent.provider === 'aws' && + item + ); + } + ), + }) + : Yup.object({ + displayName: Yup.string().required(), + name: Yup.string().required(), + provider: Yup.string().required(), }), onSubmit: async (val) => { - // const validateAccountIdAndPerform = async ( - // fn: () => T - // ): Promise => { - // const { data, errors } = await api.checkAwsAccess({ - // accountId: val.accountId, - // }); - // - // if (errors) { - // throw errors[0]; - // } - // - // if (!data.result) { - // await asyncPopupWindow({ - // url: data.installationUrl || '', - // }); - // - // const { data: d2 } = await api.checkAwsAccess({ - // accountId: val.accountId, - // }); - // - // if (!d2.result) { - // throw new Error('invalid account id'); - // } - // - // return fn(); - // } - // - // return fn(); - // }; - const addProvider = async () => { switch (val?.provider) { case 'aws': - // return validateAccountIdAndPerform(async () => { - // }); - return api.createProviderSecret({ secret: { displayName: val.displayName, metadata: { name: val.name, }, + cloudProviderName: validateCloudProvider(val.provider), aws: { - awsAccountId: val.awsAccountId, + authMechanism: 'secret_keys', }, - cloudProviderName: validateCloudProvider(val.provider), }, }); @@ -167,7 +150,13 @@ const Root = (props: IDialog) => { metadata: { name: parseName(props.data, true), }, - aws: { ...props.data.aws }, + // aws: { + // authMechanism: 'secret_keys', + // authSecretKeys: { + // accessKey: val.accessKey, + // secretKey: val.secretKey, + // }, + // }, }, }); default: @@ -209,35 +198,6 @@ const Root = (props: IDialog) => { >
- {/* {isUpdate && ( - - )} */} - - {/* - {!isUpdate && ( - { - handleChange('name')({ target: { value: id } }); - }} - /> - )} */} { nameErrorLabel="isNameError" isUpdate={isUpdate} /> + {!isUpdate && (