diff --git a/src/apps/console/components/sync-status.tsx b/src/apps/console/components/sync-status.tsx index 760996d3b..244b2dff8 100644 --- a/src/apps/console/components/sync-status.tsx +++ b/src/apps/console/components/sync-status.tsx @@ -7,7 +7,6 @@ import { CircleFill, WarningCircleFill, } from '~/console/components/icons'; -import Tooltip from '~/components/atoms/tooltip'; import { Github__Com___Kloudlite___Api___Pkg___Types__SyncState as ISyncState, Github__Com___Kloudlite___Api___Pkg___Types__SyncAction as ISyncAction, @@ -217,7 +216,7 @@ const parseOverallState = (item: IStatusMetaV2): OverallStates => { { value: 'idle', progress: 'init', - }, + } ); return (mainStatus?.value as OverallStates) || 'idle'; @@ -286,82 +285,84 @@ export const SyncStatusV2 = ({ return []; } - const items = checkList?.reduce( - (acc, curr) => { - const k = checks[curr.name]; - if (acc.progress === 'done') { - acc.items.push({ - ...curr, - result: 'idle', - message: '', - }); - return acc; - } + const items = checkList + ?.filter((cl) => !cl.hide) + .reduce( + (acc, curr) => { + const k = checks[curr.name]; + if (acc.progress === 'done') { + acc.items.push({ + ...curr, + result: 'idle', + message: '', + }); + return acc; + } - const res = ((): { - value: OverallStates; - progress: string; - message: string; - } => { - if (k) { - if (acc.value === 'idle' && k.state === 'yet-to-be-reconciled') { - return { - value: 'idle', - message: k.message, - progress: 'done', - }; + const res = ((): { + value: OverallStates; + progress: string; + message: string; + } => { + if (k) { + if (acc.value === 'idle' && k.state === 'yet-to-be-reconciled') { + return { + value: 'idle', + message: k.message, + progress: 'done', + }; + } + + if (k.state === 'under-reconcilation') { + return { + value: 'in-progress', + + message: k.message, + progress: 'done', + }; + } + + if (k.state === 'errored-during-reconcilation') { + return { + value: 'error', + message: k.message, + progress: 'done', + }; + } + + if (k.state === 'finished-reconcilation') { + return { + value: 'ready', + message: k.message, + progress: 'init', + }; + } } - if (k.state === 'under-reconcilation') { - return { - value: 'in-progress', - - message: k.message, - progress: 'done', - }; - } + return acc; + })(); - if (k.state === 'errored-during-reconcilation') { - return { - value: 'error', - message: k.message, - progress: 'done', - }; - } + acc.items.push({ + ...curr, + result: res?.value, + message: res.message, + }); - if (k.state === 'finished-reconcilation') { - return { - value: 'ready', - message: k.message, - progress: 'init', - }; - } - } + acc.value = res.value; + acc.progress = res.progress; return acc; - })(); - - acc.items.push({ - ...curr, - result: res?.value, - message: res.message, - }); - - acc.value = res.value; - acc.progress = res.progress; - - return acc; - }, - { - value: 'idle' as OverallStates, - items: [] as ({ - result: OverallStates; - message: string; - } & ICheckList)[], - message: '', - progress: 'init', - }, - ); + }, + { + value: 'idle' as OverallStates, + items: [] as ({ + result: OverallStates; + message: string; + } & ICheckList)[], + message: '', + progress: 'init', + } + ); return items?.items; }; diff --git a/src/apps/console/hooks/use-cluster-status.tsx b/src/apps/console/hooks/use-cluster-status.tsx new file mode 100644 index 000000000..8c7a92f5d --- /dev/null +++ b/src/apps/console/hooks/use-cluster-status.tsx @@ -0,0 +1,51 @@ +import { useCallback, useEffect, useState } from 'react'; +import { useConsoleApi } from '../server/gql/api-provider'; +import { parseNodes } from '../server/r-utils/common'; + +const findClusterStatus = (item?: { lastOnlineAt?: string }): boolean => { + if (!item || !item.lastOnlineAt) { + return false; + } + + const lastTime = new Date(item.lastOnlineAt); + const currentTime = new Date(); + + const timeDifference = + (currentTime.getTime() - lastTime.getTime()) / (1000 * 60); + + switch (true) { + case timeDifference <= 2: + return true; + default: + return false; + } +}; + +const useClusterStatus = () => { + const api = useConsoleApi(); + + const [clusters, setClusters] = useState([]); + const [loading, setLoading] = useState(false); + + const listCluster = useCallback(async () => { + setLoading(true); + try { + const clusters = await api.listAllClusters(); + setClusters(parseNodes(clusters.data) || []); + return clusters; + } catch (err) { + console.error(err); + return false; + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + listCluster(); + }, []); + + return { findClusterStatus, clusters, loading }; +}; + +export default useClusterStatus; diff --git a/src/apps/console/page-components/handle-environment.tsx b/src/apps/console/page-components/handle-environment.tsx index dc9f0627c..1acb02fe7 100644 --- a/src/apps/console/page-components/handle-environment.tsx +++ b/src/apps/console/page-components/handle-environment.tsx @@ -1,13 +1,11 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import Popup from '~/components/molecule/popup'; import { toast } from '~/components/molecule/toast'; import { useReload } from '~/root/lib/client/helpers/reloader'; 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 useCustomSwr from '~/root/lib/client/hooks/use-custom-swr'; import Select from '~/components/atoms/select'; -import { useAppend, useMapper } from '~/components/utils'; import { IDialog } from '../components/types.d'; import { useConsoleApi } from '../server/gql/api-provider'; import { DIALOG_TYPE } from '../utils/commons'; @@ -36,53 +34,30 @@ const HandleEnvironment = ({ show, setShow }: IDialog) => { const api = useConsoleApi(); const reloadPage = useReload(); - const { data: clustersData, isLoading: cIsLoading } = useCustomSwr( - 'clusters', - async () => - api.listClusters({ - pagination: { - first: 100, - }, - }), - true - ); - - const { data: byokClustersData, isLoading: byokCIsLoading } = useCustomSwr( - 'byokclusters', - async () => - api.listByokClusters({ - pagination: { - first: 100, - }, - }), - true - ); - - const cData = useMapper(parseNodes(clustersData), (item) => { - return { - label: item.displayName, - value: parseName(item), - ready: item.status?.isReady, - render: () => ( - - ), - }; - }); + const [clusterList, setClusterList] = useState([]); - const bCData = useMapper(parseNodes(byokClustersData), (item) => { - return { - label: item.displayName, - value: parseName(item), - ready: true, - render: () => ( - - ), - }; - }); + const getClusters = useCallback(async () => { + try { + const byokClusters = await api.listByokClusters({}); + const data = parseNodes(byokClusters.data).map((c) => ({ + label: c.displayName, + value: parseName(c), + ready: true, + render: () => ( + + ), + })); + setClusterList(data); + } catch (err) { + handleError(err); + } + }, []); - const clusterList = useAppend(cData, bCData); + useEffect(() => { + getClusters(); + }, []); - const [validationSchema, setValidationSchema] = useState( + const [validationSchema] = useState( Yup.object({ displayName: Yup.string().required(), name: Yup.string().required(), @@ -154,18 +129,13 @@ const HandleEnvironment = ({ show, setShow }: IDialog) => { }); useEffect(() => { - if (show && show.type === DIALOG_TYPE.EDIT) { + if (clusterList.length > 0) { setValues((v) => ({ ...v, - displayName: show.data?.displayName || '', + clusterName: clusterList.find((c) => c.ready)?.value || '', })); - setValidationSchema( - Yup.object({ - displayName: Yup.string().trim().required(), - }) - ); } - }, [show]); + }, [clusterList, show]); return ( ) => { label="Select Cluster" size="lg" value={values.clusterName} - disabled={cIsLoading} placeholder="Select a Cluster" options={async () => [ ...((clusterList && @@ -222,7 +191,6 @@ const HandleEnvironment = ({ show, setShow }: IDialog) => { }} error={!!errors.clusterName} message={errors.clusterName} - loading={cIsLoading || byokCIsLoading} /> {/* { const ListView = ({ items = [], onAction }: IResource) => { const { environment, account, cluster } = useOutletContext(); + const { findClusterStatus, clusters, loading } = useClusterStatus(); return ( { }, ], rows: items.map((i) => { - const isClusterOnline = getClusterStatus(cluster); + const isClusterOnline = findClusterStatus( + clusters.length > 0 + ? clusters.find((c) => parseName(c) === parseName(cluster)) + : cluster + ); const { name, id, updateInfo } = parseItem(i); return { @@ -316,15 +321,16 @@ const ListView = ({ items = [], onAction }: IResource) => { render: () => , }, status: { - render: () => ( -
- {isClusterOnline ? ( - - ) : ( - Cluster Offline - )} -
- ), + render: () => { + if (loading) { + return null; + } + if (!isClusterOnline) { + return Cluster Offline; + } + + return ; + }, }, updated: { render: () => ( diff --git a/src/apps/console/routes/_main+/$account+/env+/$environment+/external-apps/external-app-resource.tsx b/src/apps/console/routes/_main+/$account+/env+/$environment+/external-apps/external-app-resource.tsx index ac3060560..63e0b1e4b 100644 --- a/src/apps/console/routes/_main+/$account+/env+/$environment+/external-apps/external-app-resource.tsx +++ b/src/apps/console/routes/_main+/$account+/env+/$environment+/external-apps/external-app-resource.tsx @@ -126,7 +126,7 @@ const ExtraButton = ({ onAction, item }: IExtraButton) => { icon: , type: 'item', to: `/${account}/env/${environment}/external-app/${parseName( - item, + item )}/settings/general`, key: 'settings', }, @@ -311,7 +311,7 @@ const ListView = ({ items = [], onAction }: IResource) => { environment?.spec?.targetNamespace }.svc.${parseName(cluster)}.local` : `${parseName(i)}.${parseName( - environment, + environment )}.svc.${parseName(cluster)}.local` } /> @@ -337,7 +337,7 @@ const ListView = ({ items = [], onAction }: IResource) => { }, }, to: `/${parseName(account)}/env/${parseName( - environment, + environment )}/external-app/${id}`, }; }), @@ -357,9 +357,9 @@ const ExternalNameResource = ({ items = [] }: Omit) => { useWatchReload( items.map((i) => { return `account:${parseName(account)}.environment:${parseName( - environment, + environment )}.app:${parseName(i)}`; - }), + }) ); const interceptExternalApp = async (item: BaseType, intercept: boolean) => { diff --git a/src/apps/console/routes/_main+/$account+/env+/$environment+/managed-resources/handle-managed-resource-v2.tsx b/src/apps/console/routes/_main+/$account+/env+/$environment+/managed-resources/handle-managed-resource-v2.tsx index b8285b590..8366869e6 100644 --- a/src/apps/console/routes/_main+/$account+/env+/$environment+/managed-resources/handle-managed-resource-v2.tsx +++ b/src/apps/console/routes/_main+/$account+/env+/$environment+/managed-resources/handle-managed-resource-v2.tsx @@ -20,7 +20,7 @@ import { LoadingPlaceHolder } from '~/console/components/loading'; import ListV2 from '~/console/components/listV2'; import { ListItem } from '~/console/components/console-list-components'; import { CopyContentToClipboard } from '~/console/components/common-console-components'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { toast } from '~/components/molecule/toast'; import { ensureAccountClientSide } from '~/console/server/utils/auth-utils'; import { NameIdView } from '~/console/components/name-id-view'; @@ -48,72 +48,91 @@ const Root = (props: IDialog) => { const reloadPage = useReload(); const { environment } = useOutletContext(); - const { values, errors, handleChange, handleSubmit, resetValues, isLoading } = - useForm({ - initialValues: isUpdate - ? { - isNameError: false, - } - : { - isNameError: false, - name: '', - displayName: '', - managedServiceName: '', - managedResourceName: '', - }, - validationSchema: Yup.object({ - managedServiceName: Yup.string().required( - 'integrated service is required' - ), - managedResourceName: Yup.string().required( - 'integrated resource name is required' - ), - }), - onSubmit: async (val) => { - try { - if (!isUpdate) { - const { errors: e } = await api.importManagedResource({ - envName: parseName(environment), - msvcName: val.managedServiceName || '', - mresName: val.managedResourceName || '', - importName: val.name || '', - }); - if (e) { - throw e[0]; - } - } - reloadPage(); - resetValues(); - toast.success( - `integrated resource ${ - isUpdate ? 'updated' : 'imported' - } successfully` - ); - setVisible(false); - } catch (err) { - handleError(err); - } - }, - }); + const [msvcList, setMsvcList] = useState([]); - const { data: msvcData, isLoading: msvcIsLoading } = useCustomSwr( - () => 'managed-services', - async () => { - return api.listClusterMSvs(); + const getMsvcs = useCallback(async () => { + try { + const msvcs = await api.listClusterMSvs({}); + const data = parseNodes(msvcs.data).map((c) => ({ + label: c.displayName, + value: parseName(c), + ready: true, + render: () => , + })); + setMsvcList(data); + } catch (err) { + handleError(err); } - ); + }, []); - const msvcList = useMapper(parseNodes(msvcData), (item) => { - return { - label: item.displayName, - value: parseName(item), - ready: item.status?.isReady, - render: () => ( - + useEffect(() => { + getMsvcs(); + }, []); + + const { + values, + errors, + handleChange, + handleSubmit, + resetValues, + setValues, + isLoading, + } = useForm({ + initialValues: isUpdate + ? { + isNameError: false, + } + : { + isNameError: false, + name: '', + displayName: '', + managedServiceName: '', + managedResourceName: '', + }, + validationSchema: Yup.object({ + managedServiceName: Yup.string().required( + 'integrated service is required' ), - }; + managedResourceName: Yup.string().required( + 'integrated resource name is required' + ), + }), + onSubmit: async (val) => { + try { + if (!isUpdate) { + const { errors: e } = await api.importManagedResource({ + envName: parseName(environment), + msvcName: val.managedServiceName || '', + mresName: val.managedResourceName || '', + importName: val.name || '', + }); + if (e) { + throw e[0]; + } + } + reloadPage(); + resetValues(); + toast.success( + `integrated resource ${ + isUpdate ? 'updated' : 'imported' + } successfully` + ); + setVisible(false); + } catch (err) { + handleError(err); + } + }, }); + useEffect(() => { + if (msvcList.length > 0) { + setValues((v) => ({ + ...v, + managedServiceName: msvcList.find((c) => c.ready)?.value || '', + })); + } + }, [msvcList]); + const { data: mresData, isLoading: mresIsLoading } = useCustomSwr( () => `/managed-services${values.managedServiceName}`, async () => { @@ -166,7 +185,7 @@ const Root = (props: IDialog) => { label="Integrated Services" size="lg" value={values.managedServiceName} - disabled={msvcIsLoading} + // disabled={msvcIsLoading} placeholder="Select a Integrated Service" options={async () => [ ...((msvcList && @@ -179,8 +198,8 @@ const Root = (props: IDialog) => { handleChange('managedServiceName')(dummyEvent(value)); }} error={!!errors.managedServiceName} - message={errors.clusterName} - loading={msvcIsLoading} + message={errors.managedServiceName} + // loading={msvcIsLoading} />