diff --git a/.env b/.env
index 1844f4b..d117d9b 100644
--- a/.env
+++ b/.env
@@ -1,3 +1,9 @@
+VITE_TITLE_TEXT="OpenAPI UI"
+VITE_LOGO_TEXT="In-Cloud"
+VITE_FOOTER_TEXT="PRO Robotech"
+VITE_CUSTOM_LOGO_SVG=
+VITE_CUSTOM_TENANT_TEXT=
+
VITE_CUSTOMIZATION_API_GROUP=incloud.io
VITE_CUSTOMIZATION_API_VERSION=v1alpha
@@ -6,7 +12,9 @@ VITE_CUSTOMIZATION_NAVIGATION_RESOURCE=navigation
VITE_USE_NAMESPACE_NAV=true
-VITE_NAVIGATE_FROM_CLUSTERLIST=/openapi-ui/clusters/~recordValue~
+VITE_HIDE_INSIDE=false
+
+VITE_NAVIGATE_FROM_CLUSTERLIST=/openapi-ui/~recordValue~/builtin-table/namespaces
VITE_PROJECTS_API_GROUP=incloud.io
VITE_PROJECTS_VERSION=v1alpha
@@ -33,3 +41,13 @@ VITE_REMOVE_BACKLINK_TEXT=true
VITE_DOCS_URL=https://in-cloud.io/docs/tech-docs/introduction/
VITE_SEARCH_TABLE_CUSTOMIZATION_PREFIX=stock-
+
+VITE_BASE_FACTORY_NAMESPACED_API_KEY=base-factory-namespaced-api
+VITE_BASE_FACTORY_CLUSTERSCOPED_API_KEY=base-factory-clusterscoped-api
+VITE_BASE_FACTORY_NAMESPACED_BUILTIN_KEY=base-factory-namespaced-builtin
+VITE_BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY=base-factory-clusterscoped-builtin
+VITE_BASE_NAMESPACE_FACTORY_KEY=base-factory-clusterscoped-builtin
+
+VITE_CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP=
+VITE_CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION=
+VITE_CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME=
diff --git a/.env.options.dist b/.env.options.dist
index a04925e..d36b462 100644
--- a/.env.options.dist
+++ b/.env.options.dist
@@ -1,3 +1,9 @@
+TITLE_TEXT=
+LOGO_TEXT=
+FOOTER_TEXT=
+CUSTOM_LOGO_SVG=
+CUSTOM_TENANT_TEXT=
+
KUBE_API_URL=
CUSTOMIZATION_API_GROUP=
@@ -8,6 +14,8 @@ CUSTOMIZATION_NAVIGATION_RESOURCE=
USE_NAMESPACE_NAV=
+HIDE_INSIDE=
+
NAVIGATE_FROM_CLUSTERLIST=
PROJECTS_API_GROUP=
@@ -35,3 +43,13 @@ REMOVE_BACKLINK_TEXT=
DOCS_URL=
SEARCH_TABLE_CUSTOMIZATION_PREFIX=
+
+BASE_FACTORY_NAMESPACED_API_KEY=
+BASE_FACTORY_CLUSTERSCOPED_API_KEY=
+BASE_FACTORY_NAMESPACED_BUILTIN_KEY=
+BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY=
+BASE_NAMESPACE_FACTORY_KEY=
+
+CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP=
+CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION=
+CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME=
diff --git a/README.md b/README.md
index e9e09cd..5614994 100644
--- a/README.md
+++ b/README.md
@@ -9,30 +9,45 @@ Define interfaces in YAML; the app discovers CRDs, watches their objects, and bu
This app can be configured through environment variables.
-| Variable | Type | Description |
-| ---------------------------------------- | --------- | --------------------------------------------------------------------------------------- |
-| `BASEPREFIX` | `string` | Base URL for the app. `/openapi-ui` |
-| `KUBE_API_URL` | `string` | URL for the Kubernetes API. `http://api.incloud-web.svc.default.in-cloud.internal:8081` |
-| `BFF_URL` | `string` | URL for the BFF |
-| `LOGIN_URL` | `string` | Login endpoint. `/oauth/token` |
-| `LOGOUT_URL` | `string` | Logout endpoint. `/oauth/logout` |
-| `LOGIN_USERNAME_FIELD` | `string` | Field from login endpoint response. `name` |
-| `CUSTOMIZATION_API_GROUP` | `string` | API group for customization resources. `front.in-cloud.io` |
-| `CUSTOMIZATION_API_VERSION` | `string` | API version for customization resources. `v1alpha1` |
-| `CUSTOMIZATION_NAVIGATION_RESOURCE_NAME` | `string` | Resource plural name for navigation settings. `navigations` |
-| `CUSTOMIZATION_NAVIGATION_RESOURCE` | `string` | Resource name for navigation settings. `navigation` |
-| `USE_NAMESPACE_NAV` | `boolean` | Use namespaces instead of project/instances. `true` |
-| `NAVIGATE_FROM_CLUSTERLIST` | `string` | Location to be navigated after selecting cluster. `/openapi-ui/clusters/~recordValue~` |
-| `PROJECTS_API_GROUP` | `string` | API group for projects resources. If not using namespace nav. |
-| `PROJECTS_VERSION` | `string` | API version for projects resources. If not using namespace nav. |
-| `PROJECTS_RESOURCE_NAME` | `string` | Plural name for projects resources. If not using namespace nav. |
-| `INSTANCES_API_GROUP` | `string` | API group for instances resources. If not using namespace nav. |
-| `INSTANCES_VERSION` | `string` | API version for instances resources. If not using namespace nav. |
-| `INSTANCES_RESOURCE_NAME` | `string` | Plural name for instances resources. If not using namespace nav. |
-| `MARKETPLACE_RESOURCE_NAME` | `string` | Plural name for marketplace resources for related factory component. |
-| `MARKETPLACE_KIND` | `string` | Kind name for marketplace resources for related factory component. |
-| `NODE_TERMINAL_DEFAULT_PROFILE` | `string` | Default profile for node terminal component. `baseline` |
-| `REMOVE_BACKLINK` | `boolean` | Remove backlink arrow from right-side navigation |
-| `REMOVE_BACKLINK_TEXT` | `boolean` | Remove backlink text from right-side navigation |
-| `DOCS_URL` | `string` | URL to navigate from question mark |
-| `SEARCH_TABLE_CUSTOMIZATION_PREFIX` | `string` | Search tables Customization id prefix |
+| Variable | Type | Description |
+| --------------------------------------------- | --------- | --------------------------------------------------------------------------------------- |
+| `BASEPREFIX` | `string` | Base URL for the app. `/openapi-ui` |
+| `KUBE_API_URL` | `string` | URL for the Kubernetes API. `http://api.incloud-web.svc.default.in-cloud.internal:8081` |
+| `BFF_URL` | `string` | URL for the BFF |
+| `TITLE_TEXT` | `string` | Page title |
+| `LOGO_TEXT` | `string` | Logo text near icon |
+| `ICON_SVG` | `string` | Favicon base64 encoded |
+| `FOOTER_TEXT` | `string` | Footer text |
+| `CUSTOM_LOGO_SVG` | `string` | Base64 encoded svg |
+| `CUSTOM_TENANT_TEXT` | `string` | Custom tenant text override |
+| `LOGIN_URL` | `string` | Login endpoint. `/oauth/token` |
+| `LOGOUT_URL` | `string` | Logout endpoint. `/oauth/logout` |
+| `LOGIN_USERNAME_FIELD` | `string` | Field from login endpoint response. `name` |
+| `CUSTOMIZATION_API_GROUP` | `string` | API group for customization resources. `front.in-cloud.io` |
+| `CUSTOMIZATION_API_VERSION` | `string` | API version for customization resources. `v1alpha1` |
+| `CUSTOMIZATION_NAVIGATION_RESOURCE_NAME` | `string` | Resource plural name for navigation settings. `navigations` |
+| `CUSTOMIZATION_NAVIGATION_RESOURCE` | `string` | Resource name for navigation settings. `navigation` |
+| `USE_NAMESPACE_NAV` | `boolean` | Use namespaces instead of project/instances. `true` |
+| `HIDE_INSIDE` | `boolean` | Use namespaces instead of project/instances. `true` |
+| `NAVIGATE_FROM_CLUSTERLIST` | `string` | Location to be navigated after selecting cluster. `/openapi-ui/clusters/~recordValue~` |
+| `PROJECTS_API_GROUP` | `string` | API group for projects resources. If not using namespace nav. |
+| `PROJECTS_VERSION` | `string` | API version for projects resources. If not using namespace nav. |
+| `PROJECTS_RESOURCE_NAME` | `string` | Plural name for projects resources. If not using namespace nav. |
+| `INSTANCES_API_GROUP` | `string` | API group for instances resources. If not using namespace nav. |
+| `INSTANCES_VERSION` | `string` | API version for instances resources. If not using namespace nav. |
+| `INSTANCES_RESOURCE_NAME` | `string` | Plural name for instances resources. If not using namespace nav. |
+| `MARKETPLACE_RESOURCE_NAME` | `string` | Plural name for marketplace resources for related factory component. |
+| `MARKETPLACE_KIND` | `string` | Kind name for marketplace resources for related factory component. |
+| `NODE_TERMINAL_DEFAULT_PROFILE` | `string` | Default profile for node terminal component. `baseline` |
+| `REMOVE_BACKLINK` | `boolean` | Remove backlink arrow from right-side navigation |
+| `REMOVE_BACKLINK_TEXT` | `boolean` | Remove backlink text from right-side navigation |
+| `DOCS_URL` | `string` | URL to navigate from question mark |
+| `SEARCH_TABLE_CUSTOMIZATION_PREFIX` | `string` | Search tables Customization id prefix |
+| `BASE_FACTORY_NAMESPACED_API_KEY` | `string` | Base factory key for namespaced API resources |
+| `BASE_FACTORY_CLUSTERSCOPED_API_KEY` | `string` | Base factory key for clusterscoped API resources |
+| `BASE_FACTORY_NAMESPACED_BUILTIN_KEY` | `string` | Base factory key for namespaced builtin (v1) resources |
+| `BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY` | `string` | Base factory key for clusterscoped builtin (v1) resources |
+| `BASE_NAMESPACE_FACTORY_KEY` | `string` | Base factory key for namespaces |
+| `CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP` | `string` | Custom namespace resource: api group |
+| `CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION` | `string` | Custom namespace resource: api version |
+| `CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME` | `string` | Custom namespace resource: resource name |
diff --git a/package-lock.json b/package-lock.json
index 7bcdf8c..b502cbe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
"@ant-design/icons": "5.6.0",
"@monaco-editor/react": "4.6.0",
"@originjs/vite-plugin-federation": "1.3.6",
- "@prorobotech/openapi-k8s-toolkit": "^0.0.1-alpha.137",
+ "@prorobotech/openapi-k8s-toolkit": "^1.0.3",
"@readme/openapi-parser": "4.0.0",
"@reduxjs/toolkit": "2.2.5",
"@tanstack/react-query": "5.62.2",
@@ -2804,9 +2804,9 @@
}
},
"node_modules/@prorobotech/openapi-k8s-toolkit": {
- "version": "0.0.1-alpha.137",
- "resolved": "https://registry.npmjs.org/@prorobotech/openapi-k8s-toolkit/-/openapi-k8s-toolkit-0.0.1-alpha.137.tgz",
- "integrity": "sha512-40wpiC8aRBmXZiNnkqYjqo15UEjF9mowoSGqe1/xzxFdjjeZtSBoDoGhLMFy/j6QBCvgaYnNH7mYqxZg5MS7rg==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@prorobotech/openapi-k8s-toolkit/-/openapi-k8s-toolkit-1.0.3.tgz",
+ "integrity": "sha512-A8RFEd8CYdvYwnGHSOFEGZ+hotMWvtRRnXsZAcFycBF+oy6GNxCOCSfHv8IWlXfD7Rr4i7Xhp8MtDijAty+80g==",
"license": "MIT",
"dependencies": {
"@monaco-editor/react": "4.6.0",
diff --git a/package.json b/package.json
index bff473d..9ee35fd 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
"@ant-design/icons": "5.6.0",
"@monaco-editor/react": "4.6.0",
"@originjs/vite-plugin-federation": "1.3.6",
- "@prorobotech/openapi-k8s-toolkit": "0.0.1-alpha.137",
+ "@prorobotech/openapi-k8s-toolkit": "1.0.3",
"@readme/openapi-parser": "4.0.0",
"@reduxjs/toolkit": "2.2.5",
"@tanstack/react-query": "5.62.2",
diff --git a/server/getDynamicIndex.ts b/server/getDynamicIndex.ts
index 3be5d00..5990242 100644
--- a/server/getDynamicIndex.ts
+++ b/server/getDynamicIndex.ts
@@ -2,6 +2,27 @@ export const getDynamicIndex = (baseprefix: string): string => {
try {
const mainJs = 'index-react.js'
const mainCss = 'style.css'
+ const titleText = process.env.TITLE_TEXT || 'OpenAPI UI'
+ const iconSvg = process.env.ICON_SVG || ''
+
+ // Generate favicon from SVG if provided
+ const generateFavicon = (): string => {
+ if (!iconSvg) return ''
+ try {
+ // If iconSvg is base64-of-base64, unwrap once
+ const maybeInner = Buffer.from(iconSvg, 'base64').toString('utf8')
+ const payload =
+ /^[A-Za-z0-9+/=\n\r]+$/.test(maybeInner) && !maybeInner.trim().startsWith('<')
+ ? maybeInner // double-encoded → use inner base64
+ : iconSvg // single-encoded → already fine
+
+ const dataUri = `data:image/svg+xml;base64,${payload}`
+ return ``
+ } catch (e) {
+ console.error('Error processing icon SVG:', e)
+ return ''
+ }
+ }
return `
@@ -14,7 +35,8 @@ export const getDynamicIndex = (baseprefix: string): string => {
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap"
rel="stylesheet"
/>
- OpenAPI UI
+ ${titleText}
+ ${generateFavicon()}
diff --git a/server/index.ts b/server/index.ts
index f95b1ec..f14aca6 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -18,6 +18,12 @@ if (process.env.LOCAL === 'true') {
const KUBE_API_URL = process.env.LOCAL === 'true' ? options?.KUBE_API_URL : process.env.KUBE_API_URL
+const TITLE_TEXT = process.env.LOCAL === 'true' ? options?.TITLE_TEXT : process.env.TITLE_TEXT
+const LOGO_TEXT = process.env.LOCAL === 'true' ? options?.LOGO_TEXT : process.env.LOGO_TEXT
+const FOOTER_TEXT = process.env.LOCAL === 'true' ? options?.FOOTER_TEXT : process.env.FOOTER_TEXT
+const CUSTOM_LOGO_SVG = process.env.LOCAL === 'true' ? options?.CUSTOM_LOGO_SVG : process.env.CUSTOM_LOGO_SVG
+const CUSTOM_TENANT_TEXT = process.env.LOCAL === 'true' ? options?.CUSTOM_TENANT_TEXT : process.env.CUSTOM_TENANT_TEXT
+
const CUSTOMIZATION_API_GROUP =
process.env.LOCAL === 'true' ? options?.CUSTOMIZATION_API_GROUP : process.env.CUSTOMIZATION_API_GROUP
const CUSTOMIZATION_API_VERSION =
@@ -33,6 +39,7 @@ const 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 HIDE_INSIDE = process.env.LOCAL === 'true' ? options?.HIDE_INSIDE : process.env.HIDE_INSIDE
const NAVIGATE_FROM_CLUSTERLIST =
process.env.LOCAL === 'true' ? options?.NAVIGATE_FROM_CLUSTERLIST : process.env.NAVIGATE_FROM_CLUSTERLIST
@@ -73,6 +80,36 @@ const SEARCH_TABLE_CUSTOMIZATION_PREFIX =
? options?.SEARCH_TABLE_CUSTOMIZATION_PREFIX
: process.env.SEARCH_TABLE_CUSTOMIZATION_PREFIX
+const BASE_FACTORY_NAMESPACED_API_KEY =
+ process.env.LOCAL === 'true' ? options?.BASE_FACTORY_NAMESPACED_API_KEY : process.env.BASE_FACTORY_NAMESPACED_API_KEY
+const BASE_FACTORY_CLUSTERSCOPED_API_KEY =
+ process.env.LOCAL === 'true'
+ ? options?.BASE_FACTORY_CLUSTERSCOPED_API_KEY
+ : process.env.BASE_FACTORY_CLUSTERSCOPED_API_KEY
+const BASE_FACTORY_NAMESPACED_BUILTIN_KEY =
+ process.env.LOCAL === 'true'
+ ? options?.BASE_FACTORY_NAMESPACED_BUILTIN_KEY
+ : process.env.BASE_FACTORY_NAMESPACED_BUILTIN_KEY
+const BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY =
+ process.env.LOCAL === 'true'
+ ? options?.BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY
+ : process.env.BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY
+const BASE_NAMESPACE_FACTORY_KEY =
+ process.env.LOCAL === 'true' ? options?.BASE_NAMESPACE_FACTORY_KEY : process.env.BASE_NAMESPACE_FACTORY_KEY
+
+const CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP =
+ process.env.LOCAL === 'true'
+ ? options?.CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP
+ : process.env.CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP
+const CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION =
+ process.env.LOCAL === 'true'
+ ? options?.CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION
+ : process.env.CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION
+const CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME =
+ process.env.LOCAL === 'true'
+ ? options?.CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME
+ : process.env.CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME
+
const healthcheck = require('express-healthcheck')
const promBundle = require('express-prom-bundle')
@@ -176,13 +213,34 @@ app.get(`${basePrefix ? basePrefix : ''}/env.js`, (_, res) => {
`
window._env_ = {
${basePrefix ? ` BASEPREFIX: "${basePrefix}",` : ''}
+ TITLE_TEXT: ${JSON.stringify(TITLE_TEXT) || '"check envs"'},
+ LOGO_TEXT: ${LOGO_TEXT !== undefined ? JSON.stringify(LOGO_TEXT) : '"check envs"'},
+ FOOTER_TEXT: ${JSON.stringify(FOOTER_TEXT) || '"check envs"'},
+ ${CUSTOM_LOGO_SVG ? ` CUSTOM_LOGO_SVG: "${CUSTOM_LOGO_SVG}",` : ''}
+ ${CUSTOM_TENANT_TEXT ? ` CUSTOM_TENANT_TEXT: "${CUSTOM_TENANT_TEXT}",` : ''}
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"'
},
+ ${
+ CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP
+ ? ` CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP: "${CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP}",`
+ : ''
+ }
+ ${
+ CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION
+ ? ` CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION: "${CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION}",`
+ : ''
+ }
+ ${
+ CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME
+ ? ` CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME: "${CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME}",`
+ : ''
+ }
CUSTOMIZATION_NAVIGATION_RESOURCE: ${JSON.stringify(CUSTOMIZATION_NAVIGATION_RESOURCE) || '"check envs"'},
USE_NAMESPACE_NAV: ${USE_NAMESPACE_NAV ? JSON.stringify(USE_NAMESPACE_NAV).toLowerCase() : '"false"'},
+ HIDE_INSIDE: ${HIDE_INSIDE ? JSON.stringify(HIDE_INSIDE).toLowerCase() : '"false"'},
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"'},
@@ -199,7 +257,14 @@ app.get(`${basePrefix ? basePrefix : ''}/env.js`, (_, res) => {
DOCS_URL: ${JSON.stringify(DOCS_URL) || '"/docs"'},
SEARCH_TABLE_CUSTOMIZATION_PREFIX: ${JSON.stringify(SEARCH_TABLE_CUSTOMIZATION_PREFIX) || '"search-"'},
REMOVE_BACKLINK: ${!!REMOVE_BACKLINK ? JSON.stringify(REMOVE_BACKLINK).toLowerCase() : '"false"'},
- REMOVE_BACKLINK_TEXT: ${!!REMOVE_BACKLINK_TEXT ? JSON.stringify(REMOVE_BACKLINK_TEXT).toLowerCase() : '"false"'}
+ REMOVE_BACKLINK_TEXT: ${!!REMOVE_BACKLINK_TEXT ? JSON.stringify(REMOVE_BACKLINK_TEXT).toLowerCase() : '"false"'},
+ BASE_FACTORY_NAMESPACED_API_KEY: ${JSON.stringify(BASE_FACTORY_NAMESPACED_API_KEY) || '"check envs"'},
+ BASE_FACTORY_CLUSTERSCOPED_API_KEY: ${JSON.stringify(BASE_FACTORY_CLUSTERSCOPED_API_KEY) || '"check envs"'},
+ BASE_FACTORY_NAMESPACED_BUILTIN_KEY: ${JSON.stringify(BASE_FACTORY_NAMESPACED_BUILTIN_KEY) || '"check envs"'},
+ BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY: ${
+ JSON.stringify(BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY) || '"check envs"'
+ },
+ BASE_NAMESPACE_FACTORY_KEY: ${JSON.stringify(BASE_NAMESPACE_FACTORY_KEY) || '"check envs"'}
}
`,
)
diff --git a/src/components/molecules/ManageableSidebar/styled.ts b/src/components/molecules/ManageableSidebar/styled.ts
index 8be95f6..e80e757 100644
--- a/src/components/molecules/ManageableSidebar/styled.ts
+++ b/src/components/molecules/ManageableSidebar/styled.ts
@@ -18,6 +18,7 @@ const Container = styled.div`
direction: rtl;
max-height: ${({ $maxHeight }) => $maxHeight || 'initial'};
user-select: none;
+ border-top-right-radius: 7px;
& ul {
direction: ltr;
@@ -50,9 +51,9 @@ const Container = styled.div`
/* corner radius */
- && .ant-menu li:first-child div:first-child {
- border-top-right-radius: 8px;
- }
+ /* && .ant-menu li:first-child div:first-child {
+ border-top-right-radius: 4px;
+ } */
/* selected header bgcolor */
@@ -71,7 +72,7 @@ const Container = styled.div`
}
&& .ant-menu-sub .ant-menu-item.ant-menu-item-selected {
- width: 225px;
+ width: 214px;
margin-left: 25px !important;
padding-left: 23px !important;
transition: padding 0s;
diff --git a/src/components/organisms/Footer/Footer.tsx b/src/components/organisms/Footer/Footer.tsx
index c302c01..f36d824 100644
--- a/src/components/organisms/Footer/Footer.tsx
+++ b/src/components/organisms/Footer/Footer.tsx
@@ -1,11 +1,14 @@
import React, { FC } from 'react'
import { Typography } from 'antd'
+import { FOOTER_TEXT } from 'constants/customizationApiGroupAndVersion'
import { Styled } from './styled'
export const Footer: FC = () => {
return (
- PRO Robotech © {new Date().getFullYear()}
+
+ {FOOTER_TEXT} © {new Date().getFullYear()}
+
)
}
diff --git a/src/components/organisms/Header/organisms/Logo/Logo.tsx b/src/components/organisms/Header/organisms/Logo/Logo.tsx
index 7823147..1adf2a0 100644
--- a/src/components/organisms/Header/organisms/Logo/Logo.tsx
+++ b/src/components/organisms/Header/organisms/Logo/Logo.tsx
@@ -3,6 +3,8 @@ import { Flex, theme as antdtheme } from 'antd'
import { useNavigate } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { RootState } from 'store/store'
+import { LOGO_TEXT, CUSTOM_LOGO_SVG, CUSTOM_TENANT_TEXT } from 'constants/customizationApiGroupAndVersion'
+import { renderLogo } from './utils'
import { Styled } from './styled'
export const Logo: FC = () => {
@@ -17,14 +19,23 @@ export const Logo: FC = () => {
return (
-
- navigate(`${baseprefix}`)}>In-Cloud
- {tenant}
+ {CUSTOM_LOGO_SVG && typeof CUSTOM_LOGO_SVG === 'string' && CUSTOM_LOGO_SVG.length > 0 ? (
+ renderLogo(CUSTOM_LOGO_SVG, token.colorText)
+ ) : (
+
+ )}
+
+ navigate(`${baseprefix}`)}>{LOGO_TEXT}
+
+ {CUSTOM_TENANT_TEXT && typeof CUSTOM_TENANT_TEXT === 'string' && CUSTOM_TENANT_TEXT.length > 0
+ ? CUSTOM_TENANT_TEXT
+ : tenant}
+
)
diff --git a/src/components/organisms/Header/organisms/Logo/utils.tsx b/src/components/organisms/Header/organisms/Logo/utils.tsx
new file mode 100644
index 0000000..1d04d21
--- /dev/null
+++ b/src/components/organisms/Header/organisms/Logo/utils.tsx
@@ -0,0 +1,17 @@
+export const renderLogo = (customLogo: string, colorText: string): JSX.Element | null => {
+ if (customLogo) {
+ // Decode base64 SVG and replace all fill placeholders
+ try {
+ const decodedSvg = atob(customLogo)
+ // Replace all instances of {token.colorText} with actual color
+ const svgWithFill = decodedSvg.replace(/\{token\.colorText\}/g, `"${colorText}"`)
+ // eslint-disable-next-line react/no-danger
+ return
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error('Error decoding custom logo:', error)
+ return null
+ }
+ }
+ return null
+}
diff --git a/src/components/organisms/Header/organisms/User/User.tsx b/src/components/organisms/Header/organisms/User/User.tsx
index efe7ac3..69f89ce 100644
--- a/src/components/organisms/Header/organisms/User/User.tsx
+++ b/src/components/organisms/Header/organisms/User/User.tsx
@@ -6,6 +6,7 @@ import { useSelector } from 'react-redux'
import type { RootState } from 'store/store'
import { useAuth } from 'hooks/useAuth'
import { logout } from 'api/auth'
+import { BASE_HIDE_INSIDE } from 'constants/customizationApiGroupAndVersion'
import { Styled } from './styled'
export const User: FC = () => {
@@ -23,10 +24,14 @@ export const User: FC = () => {
// key: '1',
// label: ,
// },
- {
- key: '2',
- label: navigate(`${baseprefix}/inside/clusters`)}>Inside
,
- },
+ ...(BASE_HIDE_INSIDE === 'true'
+ ? []
+ : [
+ {
+ key: '2',
+ label: navigate(`${baseprefix}/inside/clusters`)}>Inside
,
+ },
+ ]),
{
key: '3',
label: (
diff --git a/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx b/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx
index b6fb99f..4f52a36 100644
--- a/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx
+++ b/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx
@@ -1,10 +1,15 @@
import React, { FC, useState } from 'react'
import { Button, Alert, Spin, Typography } from 'antd'
-import { filterSelectOptions, Spacer, useBuiltinResources } from '@prorobotech/openapi-k8s-toolkit'
+import { filterSelectOptions, Spacer, useBuiltinResources, useApiResources } from '@prorobotech/openapi-k8s-toolkit'
import { useNavigate } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux'
import { RootState } from 'store/store'
import { setCluster } from 'store/cluster/cluster/cluster'
+import {
+ CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP,
+ CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION,
+ CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME,
+} from 'constants/customizationApiGroupAndVersion'
import { Styled } from './styled'
export const ListInsideClusterAndNs: FC = () => {
@@ -17,11 +22,31 @@ export const ListInsideClusterAndNs: FC = () => {
const [selectedCluster, setSelectedCluster] = useState()
const [selectedNamespace, setSelectedNamespace] = useState()
+ const isCustomNamespaceResource =
+ CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP &&
+ typeof CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP === 'string' &&
+ CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP.length > 0 &&
+ CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION &&
+ typeof CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION === 'string' &&
+ CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION.length > 0 &&
+ CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME &&
+ typeof CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME === 'string' &&
+ CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME.length > 0
+
const namespacesData = useBuiltinResources({
clusterName: selectedCluster || '',
typeName: 'namespaces',
limit: null,
- isEnabled: selectedCluster !== undefined,
+ isEnabled: Boolean(selectedCluster !== undefined && !isCustomNamespaceResource),
+ })
+
+ const namespacesDataCustom = useApiResources({
+ clusterName: selectedCluster || '',
+ apiGroup: CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP,
+ apiVersion: CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION,
+ typeName: CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME,
+ limit: null,
+ isEnabled: Boolean(selectedCluster !== undefined && isCustomNamespaceResource),
})
return (
@@ -55,33 +80,62 @@ export const ListInsideClusterAndNs: FC = () => {
/>
)}
- {selectedCluster && namespacesData.isPending && }
- {selectedCluster && namespacesData.error && (
-
+ {selectedCluster && (isCustomNamespaceResource ? namespacesDataCustom.isPending : namespacesData.isPending) && (
+
)}
- {selectedCluster && selectedCluster.length > 0 && namespacesData.data && namespacesData.data.items.length > 0 && (
- <>
- Namespace
-
- ({
- label: ns.metadata.name,
- value: ns.metadata.name,
- }))}
- filterOption={filterSelectOptions}
- allowClear
- showSearch
- onSelect={value => {
- if (typeof value === 'string') {
- setSelectedNamespace(value)
- }
- }}
- onClear={() => setSelectedNamespace(undefined)}
- />
-
- >
+ {selectedCluster && (isCustomNamespaceResource ? namespacesDataCustom.error : namespacesData.error) && (
+
)}
+ {selectedCluster &&
+ selectedCluster.length > 0 &&
+ ((!isCustomNamespaceResource && namespacesData.data && namespacesData.data.items.length > 0) ||
+ (isCustomNamespaceResource && namespacesDataCustom.data && namespacesDataCustom.data.items.length > 0)) && (
+ <>
+ Namespace
+
+ {isCustomNamespaceResource ? (
+ ({
+ label: ns.metadata.name,
+ value: ns.metadata.name,
+ }))}
+ filterOption={filterSelectOptions}
+ allowClear
+ showSearch
+ onSelect={value => {
+ if (typeof value === 'string') {
+ setSelectedNamespace(value)
+ }
+ }}
+ onClear={() => setSelectedNamespace(undefined)}
+ />
+ ) : (
+ ({
+ label: ns.metadata.name,
+ value: ns.metadata.name,
+ }))}
+ filterOption={filterSelectOptions}
+ allowClear
+ showSearch
+ onSelect={value => {
+ if (typeof value === 'string') {
+ setSelectedNamespace(value)
+ }
+ }}
+ onClear={() => setSelectedNamespace(undefined)}
+ />
+ )}
+
+ >
+ )}