From b9b0837f0a17a296b45da5749ddeec25330b0bbe Mon Sep 17 00:00:00 2001 From: typescreep Date: Wed, 16 Jul 2025 23:20:53 +0300 Subject: [PATCH] use namespace nav & navigation resource --- .env | 11 +++ .env.options.dist | 12 ++++ server/index.ts | 25 +++++++ .../organisms/HeaderSecond/HeaderSecond.tsx | 9 +-- .../organisms/Selector/Selector.tsx | 46 +++++++++---- .../SelectorNamespace/SelectorNamespace.tsx | 69 +++++++++++++++++++ .../organisms/SelectorNamespace/index.ts | 1 + .../organisms/HeaderSecond/organisms/index.ts | 1 + .../organisms/ListClusters/ListClusters.tsx | 3 +- .../customizationApiGroupAndVersion.ts | 13 ++++ 10 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 src/components/organisms/HeaderSecond/organisms/SelectorNamespace/SelectorNamespace.tsx create mode 100644 src/components/organisms/HeaderSecond/organisms/SelectorNamespace/index.ts diff --git a/.env b/.env index e7b7e38..2456e19 100644 --- a/.env +++ b/.env @@ -1,11 +1,22 @@ VITE_CUSTOMIZATION_API_GROUP=incloud.io VITE_CUSTOMIZATION_API_VERSION=v1alpha + +VITE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME=navigations +VITE_CUSTOMIZATION_NAVIGATION_RESOURCE=navigation + +VITE_USE_NAMESPACE_NAV=true + +VITE_NAVIGATE_FROM_CLUSTERLIST=/openapi-ui/clusters/~recordValue~ + VITE_PROJECTS_API_GROUP=incloud.io VITE_PROJECTS_VERSION=v1alpha VITE_PROJECTS_RESOURCE_NAME=projects + VITE_MARKETPLACE_RESOURCE_NAME=marketplacepanels VITE_MARKETPLACE_KIND=MarketplacePanel + VITE_INSTANCES_API_GROUP=incloud.io VITE_INSTANCES_VERSION=v1alpha1 VITE_INSTANCES_RESOURCE_NAME=v1alpha1 + VITE_BFF_URL= diff --git a/.env.options.dist b/.env.options.dist index e902edf..e57a22f 100644 --- a/.env.options.dist +++ b/.env.options.dist @@ -1,12 +1,24 @@ KUBE_API_URL= + CUSTOMIZATION_API_GROUP= CUSTOMIZATION_API_VERSION= + +CUSTOMIZATION_NAVIGATION_RESOURCE_NAME= +CUSTOMIZATION_NAVIGATION_RESOURCE= + +USE_NAMESPACE_NAV= + +NAVIGATE_FROM_CLUSTERLIST= + PROJECTS_API_GROUP= PROJECTS_VERSION= PROJECTS_RESOURCE_NAME= + MARKETPLACE_RESOURCE_NAME= MARKETPLACE_KIND= + INSTANCES_API_GROUP= INSTANCES_VERSION= INSTANCES_RESOURCE_NAME= + BFF_URL= diff --git a/server/index.ts b/server/index.ts index c2de64b..993b452 100644 --- a/server/index.ts +++ b/server/index.ts @@ -17,22 +17,41 @@ if (process.env.LOCAL === 'true') { } const KUBE_API_URL = process.env.LOCAL === 'true' ? options?.KUBE_API_URL : process.env.KUBE_API_URL + const CUSTOMIZATION_API_GROUP = process.env.LOCAL === 'true' ? options?.CUSTOMIZATION_API_GROUP : process.env.CUSTOMIZATION_API_GROUP const CUSTOMIZATION_API_VERSION = process.env.LOCAL === 'true' ? options?.CUSTOMIZATION_API_VERSION : process.env.CUSTOMIZATION_API_VERSION + +const CUSTOMIZATION_NAVIGATION_RESOURCE_NAME = + process.env.LOCAL === 'true' + ? options?.CUSTOMIZATION_NAVIGATION_RESOURCE_NAME + : process.env.CUSTOMIZATION_NAVIGATION_RESOURCE_NAME +const CUSTOMIZATION_NAVIGATION_RESOURCE = + process.env.LOCAL === 'true' + ? options?.CUSTOMIZATION_NAVIGATION_RESOURCE + : process.env.CUSTOMIZATION_NAVIGATION_RESOURCE + +const USE_NAMESPACE_NAV = process.env.LOCAL === 'true' ? options?.USE_NAMESPACE_NAV : process.env.USE_NAMESPACE_NAV + +const NAVIGATE_FROM_CLUSTERLIST = + process.env.LOCAL === 'true' ? options?.NAVIGATE_FROM_CLUSTERLIST : process.env.NAVIGATE_FROM_CLUSTERLIST + const PROJECTS_API_GROUP = process.env.LOCAL === 'true' ? options?.PROJECTS_API_GROUP : process.env.PROJECTS_API_GROUP const PROJECTS_VERSION = process.env.LOCAL === 'true' ? options?.PROJECTS_VERSION : process.env.PROJECTS_VERSION const PROJECTS_RESOURCE_NAME = process.env.LOCAL === 'true' ? options?.PROJECTS_RESOURCE_NAME : process.env.PROJECTS_RESOURCE_NAME + const MARKETPLACE_RESOURCE_NAME = process.env.LOCAL === 'true' ? options?.MARKETPLACE_RESOURCE_NAME : process.env.MARKETPLACE_RESOURCE_NAME const MARKETPLACE_KIND = process.env.LOCAL === 'true' ? options?.MARKETPLACE_KIND : process.env.MARKETPLACE_KIND + const INSTANCES_API_GROUP = process.env.LOCAL === 'true' ? options?.INSTANCES_API_GROUP : process.env.INSTANCES_API_GROUP const INSTANCES_VERSION = process.env.LOCAL === 'true' ? options?.INSTANCES_VERSION : process.env.INSTANCES_VERSION const INSTANCES_RESOURCE_NAME = process.env.LOCAL === 'true' ? options?.INSTANCES_RESOURCE_NAME : process.env.INSTANCES_RESOURCE_NAME + const BFF_URL = process.env.LOCAL === 'true' ? options?.BFF_URL : process.env.BFF_URL const healthcheck = require('express-healthcheck') @@ -140,6 +159,12 @@ app.get(`${basePrefix ? basePrefix : ''}/env.js`, (_, res) => { ${basePrefix ? ` BASEPREFIX: "${basePrefix}",` : ''} CUSTOMIZATION_API_GROUP: ${JSON.stringify(CUSTOMIZATION_API_GROUP) || '"check envs"'}, CUSTOMIZATION_API_VERSION: ${JSON.stringify(CUSTOMIZATION_API_VERSION) || '"check envs"'}, + CUSTOMIZATION_NAVIGATION_RESOURCE_NAME: ${ + JSON.stringify(CUSTOMIZATION_NAVIGATION_RESOURCE_NAME) || '"check envs"' + }, + CUSTOMIZATION_NAVIGATION_RESOURCE: ${JSON.stringify(CUSTOMIZATION_NAVIGATION_RESOURCE) || '"check envs"'}, + USE_NAMESPACE_NAV: ${!!USE_NAMESPACE_NAV}, + NAVIGATE_FROM_CLUSTERLIST: ${JSON.stringify(NAVIGATE_FROM_CLUSTERLIST) || '"check envs"'}, PROJECTS_API_GROUP: ${JSON.stringify(PROJECTS_API_GROUP) || '"check envs"'}, PROJECTS_VERSION: ${JSON.stringify(PROJECTS_VERSION) || '"check envs"'}, PROJECTS_RESOURCE_NAME: ${JSON.stringify(PROJECTS_RESOURCE_NAME) || '"check envs"'}, diff --git a/src/components/organisms/HeaderSecond/HeaderSecond.tsx b/src/components/organisms/HeaderSecond/HeaderSecond.tsx index ea7343b..68ddd33 100644 --- a/src/components/organisms/HeaderSecond/HeaderSecond.tsx +++ b/src/components/organisms/HeaderSecond/HeaderSecond.tsx @@ -2,7 +2,8 @@ import React, { FC } from 'react' import { theme } from 'antd' import { useParams } from 'react-router-dom' import { HEAD_SECOND_ROW } from 'constants/blocksSizes' -import { Selector, SelectorInside } from './organisms' +import { BASE_USE_NAMESPACE_NAV } from 'constants/customizationApiGroupAndVersion' +import { Selector, SelectorInside, SelectorNamespace } from './organisms' import { Styled } from './styled' type THeaderProps = { @@ -20,15 +21,15 @@ export const HeaderSecond: FC = ({ inside }) => { return ( - {inside ? ( - - ) : ( + {inside && !BASE_USE_NAMESPACE_NAV && } + {!inside && !BASE_USE_NAMESPACE_NAV && ( )} + {BASE_USE_NAMESPACE_NAV && } ) diff --git a/src/components/organisms/HeaderSecond/organisms/Selector/Selector.tsx b/src/components/organisms/HeaderSecond/organisms/Selector/Selector.tsx index a737ce8..36f1829 100644 --- a/src/components/organisms/HeaderSecond/organisms/Selector/Selector.tsx +++ b/src/components/organisms/HeaderSecond/organisms/Selector/Selector.tsx @@ -1,15 +1,15 @@ import React, { FC, useState } from 'react' import { Flex } from 'antd' import { useNavigate } from 'react-router-dom' -import { useSelector } from 'react-redux' -import { RootState } from 'store/store' +import { useDirectUnknownResource } from '@prorobotech/openapi-k8s-toolkit' import { useNavSelector } from 'hooks/useNavSelector' import { useMountEffect } from 'hooks/useMountEffect' import { EntrySelect } from 'components/atoms' import { - BASE_INSTANCES_API_GROUP, - BASE_INSTANCES_VERSION, - BASE_INSTANCES_RESOURCE_NAME, + BASE_API_GROUP, + BASE_API_VERSION, + BASE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME, + BASE_CUSTOMIZATION_NAVIGATION_RESOURCE, } from 'constants/customizationApiGroupAndVersion' type TSelectorProps = { @@ -20,7 +20,6 @@ type TSelectorProps = { export const Selector: FC = ({ clusterName, projectName, instanceName }) => { const navigate = useNavigate() - const baseprefix = useSelector((state: RootState) => state.baseprefix.baseprefix) const [selectedClusterName, setSelectedClusterName] = useState(clusterName) const [selectedProjectName, setSelectedProjectName] = useState(projectName) @@ -32,6 +31,15 @@ export const Selector: FC = ({ clusterName, projectName, instanc projectName, ) + const { data: navigationData } = useDirectUnknownResource<{ + spec: { projects: { clear: string; change: string }; instances: { clear: string; change: string } } + }>({ + uri: `/api/clusters/${clusterName}/k8s/apis/${BASE_API_GROUP}/${BASE_API_VERSION}/${BASE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME}/${BASE_CUSTOMIZATION_NAVIGATION_RESOURCE}`, + refetchInterval: false, + queryKey: ['navigation', clusterName || 'no-cluster'], + isEnabled: clusterName !== undefined, + }) + // const handleClusterChange = (value: string) => { // setSelectedClusterName(value) // navigate(`${baseprefix}/clusters/${value}`) @@ -40,20 +48,34 @@ export const Selector: FC = ({ clusterName, projectName, instanc const handleProjectChange = (value?: string) => { if (value) { setSelectedProjectName(value) - navigate(`${baseprefix}/clusters/${selectedClusterName}/projects/${value}`) + const changeUrl = + navigationData?.spec.projects.change + .replace('{selectedCluster}', selectedClusterName || 'no-cluster') + .replace('{value}', value) || 'no navigation data' + navigate(changeUrl) } else { - navigate(`${baseprefix}/clusters/${selectedClusterName}/`) + const clearUrl = + navigationData?.spec.projects.clear.replace('{selectedCluster}', selectedClusterName || 'no-cluster') || + 'no navigation data' + navigate(clearUrl) } } const handleInstanceChange = (value?: string) => { if (value) { setSelectedInstanceName(value) - navigate(`${baseprefix}/${selectedClusterName}/${value}/${selectedProjectName}/api-table/apps/v1/deployments`) + const changeUrl = + navigationData?.spec.instances.change + .replace('{selectedCluster}', selectedClusterName || 'no-cluster') + .replace('{selectedProject}', selectedProjectName || 'no-project') + .replace('{value}', value) || 'no navigation data' + navigate(changeUrl) } else { - navigate( - `${baseprefix}/${selectedClusterName}/${selectedProjectName}/api-table/${BASE_INSTANCES_API_GROUP}/${BASE_INSTANCES_VERSION}/${BASE_INSTANCES_RESOURCE_NAME}`, - ) + const clearUrl = + navigationData?.spec.instances.clear + .replace('{selectedCluster}', selectedClusterName || 'no-cluster') + .replace('{selectedProject}', selectedProjectName || 'no-project') || 'no navigation data' + navigate(clearUrl) } } diff --git a/src/components/organisms/HeaderSecond/organisms/SelectorNamespace/SelectorNamespace.tsx b/src/components/organisms/HeaderSecond/organisms/SelectorNamespace/SelectorNamespace.tsx new file mode 100644 index 0000000..6f7bdf9 --- /dev/null +++ b/src/components/organisms/HeaderSecond/organisms/SelectorNamespace/SelectorNamespace.tsx @@ -0,0 +1,69 @@ +import React, { FC, useState } from 'react' +import { Flex } from 'antd' +import { useNavigate } from 'react-router-dom' +import { useDirectUnknownResource } from '@prorobotech/openapi-k8s-toolkit' +import { useNavSelectorInside } from 'hooks/useNavSelectorInside' +import { useMountEffect } from 'hooks/useMountEffect' +import { EntrySelect } from 'components/atoms' +import { + BASE_API_GROUP, + BASE_API_VERSION, + BASE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME, + BASE_CUSTOMIZATION_NAVIGATION_RESOURCE, +} from 'constants/customizationApiGroupAndVersion' + +type TSelectorNamespaceProps = { + clusterName?: string + namespace?: string +} + +export const SelectorNamespace: FC = ({ clusterName, namespace }) => { + const navigate = useNavigate() + + const [selectedClusterName, setSelectedClusterName] = useState(clusterName) + const [selectedNamespace, setSelectedNamespace] = useState(namespace) + + const { namespacesInSidebar } = useNavSelectorInside(selectedClusterName) + + const { data: navigationData } = useDirectUnknownResource<{ + spec: { namespaces: { clear: string; change: string } } + }>({ + uri: `/api/clusters/${clusterName}/k8s/apis/${BASE_API_GROUP}/${BASE_API_VERSION}/${BASE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME}/${BASE_CUSTOMIZATION_NAVIGATION_RESOURCE}`, + refetchInterval: false, + queryKey: ['navigation', clusterName || 'no-cluster'], + isEnabled: clusterName !== undefined, + }) + + const handleNamepsaceChange = (value?: string) => { + if (value) { + setSelectedNamespace(value) + const changeUrl = + navigationData?.spec.namespaces.change + .replace('{selectedCluster}', selectedClusterName || 'no-cluster') + .replace('{value}', value) || 'no navigation data' + navigate(changeUrl) + } else { + const clearUrl = + navigationData?.spec.namespaces.clear.replace('{selectedCluster}', selectedClusterName || 'no-cluster') || + 'no navigation data' + navigate(clearUrl) + } + } + + useMountEffect(() => { + setSelectedClusterName(clusterName) + setSelectedNamespace(namespace) + }, [namespace, clusterName]) + + return ( + + + + ) +} diff --git a/src/components/organisms/HeaderSecond/organisms/SelectorNamespace/index.ts b/src/components/organisms/HeaderSecond/organisms/SelectorNamespace/index.ts new file mode 100644 index 0000000..d4890a6 --- /dev/null +++ b/src/components/organisms/HeaderSecond/organisms/SelectorNamespace/index.ts @@ -0,0 +1 @@ +export * from './SelectorNamespace' diff --git a/src/components/organisms/HeaderSecond/organisms/index.ts b/src/components/organisms/HeaderSecond/organisms/index.ts index cdc6d40..f6bfb75 100644 --- a/src/components/organisms/HeaderSecond/organisms/index.ts +++ b/src/components/organisms/HeaderSecond/organisms/index.ts @@ -1,2 +1,3 @@ export * from './Selector' export * from './SelectorInside' +export * from './SelectorNamespace' diff --git a/src/components/organisms/ListClusters/ListClusters.tsx b/src/components/organisms/ListClusters/ListClusters.tsx index 5454920..18a0369 100644 --- a/src/components/organisms/ListClusters/ListClusters.tsx +++ b/src/components/organisms/ListClusters/ListClusters.tsx @@ -3,6 +3,7 @@ import { EnrichedTable } from '@prorobotech/openapi-k8s-toolkit' import { useSelector } from 'react-redux' import { RootState } from 'store/store' import { TABLE_PROPS } from 'constants/tableProps' +import { BASE_NAVIGATE_FROM_CLUSTERLIST } from 'constants/customizationApiGroupAndVersion' export const ListClusters: FC = () => { const theme = useSelector((state: RootState) => state.openapiTheme.theme) @@ -40,7 +41,7 @@ export const ListClusters: FC = () => { key: 'description', }, ]} - pathToNavigate={`${baseprefix}/clusters/~recordValue~`} + pathToNavigate={BASE_NAVIGATE_FROM_CLUSTERLIST} recordKeysForNavigation={['name']} withoutControls tableProps={TABLE_PROPS} diff --git a/src/constants/customizationApiGroupAndVersion.ts b/src/constants/customizationApiGroupAndVersion.ts index 41a9420..1a39652 100644 --- a/src/constants/customizationApiGroupAndVersion.ts +++ b/src/constants/customizationApiGroupAndVersion.ts @@ -1,13 +1,26 @@ /* eslint-disable no-underscore-dangle */ export const BASE_API_GROUP = window._env_.CUSTOMIZATION_API_GROUP || import.meta.env.VITE_CUSTOMIZATION_API_GROUP export const BASE_API_VERSION = window._env_.CUSTOMIZATION_API_VERSION || import.meta.env.VITE_CUSTOMIZATION_API_VERSION + +export const BASE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME = + window._env_.CUSTOMIZATION_NAVIGATION_RESOURCE_NAME || import.meta.env.VITE_CUSTOMIZATION_NAVIGATION_RESOURCE_NAME +export const BASE_CUSTOMIZATION_NAVIGATION_RESOURCE = + window._env_.CUSTOMIZATION_NAVIGATION_RESOURCE || import.meta.env.VITE_CUSTOMIZATION_NAVIGATION_RESOURCE + +export const BASE_USE_NAMESPACE_NAV = window._env_.USE_NAMESPACE_NAV || import.meta.env.VITE_USE_NAMESPACE_NAV + +export const BASE_NAVIGATE_FROM_CLUSTERLIST = + window._env_.NAVIGATE_FROM_CLUSTERLIST || import.meta.env.VITE_NAVIGATE_FROM_CLUSTERLIST + export const BASE_PROJECTS_API_GROUP = window._env_.PROJECTS_API_GROUP || import.meta.env.VITE_PROJECTS_API_GROUP export const BASE_PROJECTS_VERSION = window._env_.PROJECTS_VERSION || import.meta.env.VITE_PROJECTS_VERSION export const BASE_PROJECTS_RESOURCE_NAME = window._env_.PROJECTS_RESOURCE_NAME || import.meta.env.VITE_PROJECTS_RESOURCE_NAME + export const BASE_MARKETPLACE_RESOURCE_NAME = window._env_.MARKETPLACE_RESOURCE_NAME || import.meta.env.VITE_MARKETPLACE_RESOURCE_NAME export const BASE_MARKETPLACE_KIND = window._env_.MARKETPLACE_KIND || import.meta.env.VITE_MARKETPLACE_KIND + export const BASE_INSTANCES_API_GROUP = window._env_.INSTANCES_API_GROUP || import.meta.env.VITE_INSTANCES_API_GROUP export const BASE_INSTANCES_VERSION = window._env_.INSTANCES_VERSION || import.meta.env.VITE_INSTANCES_VERSION export const BASE_INSTANCES_RESOURCE_NAME =