mirror of
https://github.com/outbackdingo/openapi-ui.git
synced 2026-01-27 18:19:50 +00:00
Merge pull request #160 from PRO-Robotech/feature/dev
White Labels + Border fix
This commit is contained in:
10
.env
10
.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
|
||||
|
||||
@@ -39,3 +45,7 @@ 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=
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
TITLE_TEXT=
|
||||
LOGO_TEXT=
|
||||
FOOTER_TEXT=
|
||||
CUSTOM_LOGO_SVG=
|
||||
CUSTOM_TENANT_TEXT=
|
||||
|
||||
KUBE_API_URL=
|
||||
|
||||
CUSTOMIZATION_API_GROUP=
|
||||
@@ -41,3 +47,7 @@ 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=
|
||||
|
||||
67
README.md
67
README.md
@@ -9,30 +9,43 @@ 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 |
|
||||
| `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` |
|
||||
| `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 |
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -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.154",
|
||||
"@prorobotech/openapi-k8s-toolkit": "^0.0.1-alpha.156",
|
||||
"@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.154",
|
||||
"resolved": "https://registry.npmjs.org/@prorobotech/openapi-k8s-toolkit/-/openapi-k8s-toolkit-0.0.1-alpha.154.tgz",
|
||||
"integrity": "sha512-m/xi/HWTiDj8nNRoym0GQpMbxtIdoq32/z/r7lYGrYxQmq15VvF5FhRbB2utY1J1tK6WnzMqXnFOM0J19qJ/Ag==",
|
||||
"version": "0.0.1-alpha.156",
|
||||
"resolved": "https://registry.npmjs.org/@prorobotech/openapi-k8s-toolkit/-/openapi-k8s-toolkit-0.0.1-alpha.156.tgz",
|
||||
"integrity": "sha512-uMKzfdcEAbzKAFPWNIjcQMUtjhLmuUETs+1p+4K752HLB+31t9W1Fl5GOiySbpIWhoM7R9Y1mw5efe7/f04aRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "4.6.0",
|
||||
|
||||
@@ -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.154",
|
||||
"@prorobotech/openapi-k8s-toolkit": "0.0.1-alpha.156",
|
||||
"@readme/openapi-parser": "4.0.0",
|
||||
"@reduxjs/toolkit": "2.2.5",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
|
||||
@@ -2,6 +2,23 @@ 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 {
|
||||
const decodedSvg = Buffer.from(iconSvg, 'base64').toString('utf-8')
|
||||
const dataUri = `data:image/svg+xml;base64,${decodedSvg}`
|
||||
return `<link rel="icon" type="image/svg+xml" href="${dataUri}">`
|
||||
} catch (error) {
|
||||
console.error('Error processing icon SVG:', error)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
return `<html>
|
||||
<head>
|
||||
@@ -14,7 +31,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"
|
||||
/>
|
||||
<title>OpenAPI UI</title>
|
||||
<title>${titleText}</title>
|
||||
${generateFavicon()}
|
||||
<script src="${baseprefix}/env.js"></script>
|
||||
<script type="module" crossorigin src="${baseprefix}/${mainJs}"></script>
|
||||
<link rel="stylesheet" crossorigin href="${baseprefix}/${mainCss}">
|
||||
|
||||
@@ -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 =
|
||||
@@ -90,6 +96,19 @@ const 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')
|
||||
|
||||
@@ -193,11 +212,31 @@ app.get(`${basePrefix ? basePrefix : ''}/env.js`, (_, res) => {
|
||||
`
|
||||
window._env_ = {
|
||||
${basePrefix ? ` BASEPREFIX: "${basePrefix}",` : ''}
|
||||
TITLE_TEXT: ${JSON.stringify(TITLE_TEXT) || '"check envs"'},
|
||||
LOGO_TEXT: ${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"'},
|
||||
NAVIGATE_FROM_CLUSTERLIST: ${JSON.stringify(NAVIGATE_FROM_CLUSTERLIST) || '"check envs"'},
|
||||
|
||||
@@ -18,6 +18,7 @@ const Container = styled.div<TContainerProps>`
|
||||
direction: rtl;
|
||||
max-height: ${({ $maxHeight }) => $maxHeight || 'initial'};
|
||||
user-select: none;
|
||||
border-top-right-radius: 12px;
|
||||
|
||||
& ul {
|
||||
direction: ltr;
|
||||
|
||||
@@ -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 (
|
||||
<Styled.Container>
|
||||
<Typography.Text type="secondary">PRO Robotech © {new Date().getFullYear()}</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
{FOOTER_TEXT} © {new Date().getFullYear()}
|
||||
</Typography.Text>
|
||||
</Styled.Container>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<Styled.CursorPointer $svgHoverFill={token.colorInfoActive}>
|
||||
<Flex gap={8} align="center">
|
||||
<svg width="26" height="31" viewBox="0 0 26 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M18.7467 19.3641C18.7467 20.808 17.6069 21.9784 16.2007 21.9784H8.82597C7.71912 21.9784 6.8217 21.057 6.8217 19.9204C6.8217 18.7837 7.71912 17.8623 8.82597 17.8623C8.83507 17.8623 8.84412 17.8626 8.85317 17.8627C9.01052 16.8873 9.83572 16.1435 10.8302 16.1435C10.9381 16.1435 11.0441 16.1524 11.1474 16.1692C11.6261 15.1372 12.6512 14.4239 13.8386 14.4239C15.2587 14.4239 16.4467 15.4443 16.7453 16.81C17.8898 17.0662 18.7466 18.1123 18.7467 19.3641ZM14.768 13.9194C15.8071 14.1367 16.8944 13.078 17.1963 11.5548C17.4984 10.0315 16.9008 8.62062 15.8616 8.40337C14.8225 8.18613 13.7353 9.24488 13.4333 10.7681C13.1313 12.2913 13.7288 13.7022 14.768 13.9194ZM11.271 13.9194C12.3102 13.7022 12.9078 12.2913 12.6057 10.768C12.3037 9.24479 11.2165 8.18608 10.1774 8.40332C9.13825 8.62057 8.54068 10.0315 8.8427 11.5547C9.14468 13.0779 10.2319 14.1366 11.271 13.9194ZM8.45417 16.4552C9.25412 16.0652 9.46307 14.7984 8.92087 13.6258C8.37866 12.4531 7.29057 11.8187 6.49057 12.2087C5.69062 12.5988 5.48167 13.8656 6.02387 15.0382C6.56612 16.2108 7.65417 16.8453 8.45417 16.4552ZM19.296 12.1539C18.456 11.8658 17.4504 12.6304 17.0499 13.8617C16.6494 15.0929 17.0057 16.3246 17.8457 16.6128C18.6857 16.9008 19.6913 16.1362 20.0918 14.905C20.4922 13.6737 20.136 12.442 19.296 12.1539ZM26 21.6578C26.0001 22.6152 25.5027 23.4999 24.6954 23.9789L14.3072 30.1406C13.4999 30.6197 12.5048 30.6198 11.697 30.141L1.30716 23.9827C0.499661 23.5039 0.00204077 22.6193 0.00181896 21.6619L7.41513e-08 9.34205C-0.000221741 8.38456 0.497221 7.49995 1.30454 7.02082L11.6925 0.859252C12.5002 0.380394 13.4952 0.380257 14.3027 0.858797L24.6928 7.01709C25.5003 7.49585 25.9979 8.38041 25.9982 9.33786L26 21.6578ZM24.6065 20.9976L24.6048 9.99838C24.6047 9.14357 24.1603 8.35408 23.4394 7.92647L14.1631 2.42843C13.4422 2.0011 12.5537 2.00123 11.8328 2.42884L2.55819 7.92989C1.8376 8.35768 1.3934 9.1473 1.39357 10.0021L1.39522 21.0014C1.39531 21.8562 1.83978 22.6457 2.56054 23.0733L11.8367 28.5714C12.5578 28.9988 13.4464 28.9987 14.1671 28.571L23.4417 23.0698C24.1625 22.6421 24.6066 21.8524 24.6065 20.9976Z"
|
||||
fill={token.colorText}
|
||||
/>
|
||||
</svg>
|
||||
<Styled.LogoText onClick={() => navigate(`${baseprefix}`)}>In-Cloud</Styled.LogoText>
|
||||
<Styled.TenantText $color={token.colorTextDescription}>{tenant}</Styled.TenantText>
|
||||
{CUSTOM_LOGO_SVG && typeof CUSTOM_LOGO_SVG === 'string' && CUSTOM_LOGO_SVG.length > 0 ? (
|
||||
renderLogo(CUSTOM_LOGO_SVG, token.colorText)
|
||||
) : (
|
||||
<svg width="26" height="31" viewBox="0 0 26 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M18.7467 19.3641C18.7467 20.808 17.6069 21.9784 16.2007 21.9784H8.82597C7.71912 21.9784 6.8217 21.057 6.8217 19.9204C6.8217 18.7837 7.71912 17.8623 8.82597 17.8623C8.83507 17.8623 8.84412 17.8626 8.85317 17.8627C9.01052 16.8873 9.83572 16.1435 10.8302 16.1435C10.9381 16.1435 11.0441 16.1524 11.1474 16.1692C11.6261 15.1372 12.6512 14.4239 13.8386 14.4239C15.2587 14.4239 16.4467 15.4443 16.7453 16.81C17.8898 17.0662 18.7466 18.1123 18.7467 19.3641ZM14.768 13.9194C15.8071 14.1367 16.8944 13.078 17.1963 11.5548C17.4984 10.0315 16.9008 8.62062 15.8616 8.40337C14.8225 8.18613 13.7353 9.24488 13.4333 10.7681C13.1313 12.2913 13.7288 13.7022 14.768 13.9194ZM11.271 13.9194C12.3102 13.7022 12.9078 12.2913 12.6057 10.768C12.3037 9.24479 11.2165 8.18608 10.1774 8.40332C9.13825 8.62057 8.54068 10.0315 8.8427 11.5547C9.14468 13.0779 10.2319 14.1366 11.271 13.9194ZM8.45417 16.4552C9.25412 16.0652 9.46307 14.7984 8.92087 13.6258C8.37866 12.4531 7.29057 11.8187 6.49057 12.2087C5.69062 12.5988 5.48167 13.8656 6.02387 15.0382C6.56612 16.2108 7.65417 16.8453 8.45417 16.4552ZM19.296 12.1539C18.456 11.8658 17.4504 12.6304 17.0499 13.8617C16.6494 15.0929 17.0057 16.3246 17.8457 16.6128C18.6857 16.9008 19.6913 16.1362 20.0918 14.905C20.4922 13.6737 20.136 12.442 19.296 12.1539ZM26 21.6578C26.0001 22.6152 25.5027 23.4999 24.6954 23.9789L14.3072 30.1406C13.4999 30.6197 12.5048 30.6198 11.697 30.141L1.30716 23.9827C0.499661 23.5039 0.00204077 22.6193 0.00181896 21.6619L7.41513e-08 9.34205C-0.000221741 8.38456 0.497221 7.49995 1.30454 7.02082L11.6925 0.859252C12.5002 0.380394 13.4952 0.380257 14.3027 0.858797L24.6928 7.01709C25.5003 7.49585 25.9979 8.38041 25.9982 9.33786L26 21.6578ZM24.6065 20.9976L24.6048 9.99838C24.6047 9.14357 24.1603 8.35408 23.4394 7.92647L14.1631 2.42843C13.4422 2.0011 12.5537 2.00123 11.8328 2.42884L2.55819 7.92989C1.8376 8.35768 1.3934 9.1473 1.39357 10.0021L1.39522 21.0014C1.39531 21.8562 1.83978 22.6457 2.56054 23.0733L11.8367 28.5714C12.5578 28.9988 13.4464 28.9987 14.1671 28.571L23.4417 23.0698C24.1625 22.6421 24.6066 21.8524 24.6065 20.9976Z"
|
||||
fill={token.colorText}
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
|
||||
<Styled.LogoText onClick={() => navigate(`${baseprefix}`)}>{LOGO_TEXT}</Styled.LogoText>
|
||||
<Styled.TenantText $color={token.colorTextDescription}>
|
||||
{CUSTOM_TENANT_TEXT && typeof CUSTOM_TENANT_TEXT === 'string' && CUSTOM_TENANT_TEXT.length > 0
|
||||
? CUSTOM_TENANT_TEXT
|
||||
: tenant}
|
||||
</Styled.TenantText>
|
||||
</Flex>
|
||||
</Styled.CursorPointer>
|
||||
)
|
||||
|
||||
17
src/components/organisms/Header/organisms/Logo/utils.tsx
Normal file
17
src/components/organisms/Header/organisms/Logo/utils.tsx
Normal file
@@ -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 <div dangerouslySetInnerHTML={{ __html: svgWithFill }} />
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error decoding custom logo:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -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<string>()
|
||||
const [selectedNamespace, setSelectedNamespace] = useState<string>()
|
||||
|
||||
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: 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: selectedCluster !== undefined && isCustomNamespaceResource,
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -55,33 +80,62 @@ export const ListInsideClusterAndNs: FC = () => {
|
||||
/>
|
||||
)}
|
||||
<Spacer $space={8} $samespace />
|
||||
{selectedCluster && namespacesData.isPending && <Spin />}
|
||||
{selectedCluster && namespacesData.error && (
|
||||
<Alert message={`An error has occurred: ${namespacesData.error?.message} `} type="error" />
|
||||
{selectedCluster && (isCustomNamespaceResource ? namespacesDataCustom.isPending : namespacesData.isPending) && (
|
||||
<Spin />
|
||||
)}
|
||||
{selectedCluster && selectedCluster.length > 0 && namespacesData.data && namespacesData.data.items.length > 0 && (
|
||||
<>
|
||||
<Typography.Text>Namespace</Typography.Text>
|
||||
<Spacer $space={8} $samespace />
|
||||
<Styled.FullWidthSelect
|
||||
placeholder="Choose namespace"
|
||||
options={namespacesData.data.items.map(ns => ({
|
||||
label: ns.metadata.name,
|
||||
value: ns.metadata.name,
|
||||
}))}
|
||||
filterOption={filterSelectOptions}
|
||||
allowClear
|
||||
showSearch
|
||||
onSelect={value => {
|
||||
if (typeof value === 'string') {
|
||||
setSelectedNamespace(value)
|
||||
}
|
||||
}}
|
||||
onClear={() => setSelectedNamespace(undefined)}
|
||||
/>
|
||||
<Spacer $space={8} $samespace />
|
||||
</>
|
||||
{selectedCluster && (isCustomNamespaceResource ? namespacesDataCustom.error : namespacesData.error) && (
|
||||
<Alert
|
||||
message={`An error has occurred: ${
|
||||
isCustomNamespaceResource ? namespacesDataCustom.error?.message : namespacesData.error?.message
|
||||
} `}
|
||||
type="error"
|
||||
/>
|
||||
)}
|
||||
{selectedCluster &&
|
||||
selectedCluster.length > 0 &&
|
||||
((!isCustomNamespaceResource && namespacesData.data && namespacesData.data.items.length > 0) ||
|
||||
(isCustomNamespaceResource && namespacesDataCustom.data && namespacesDataCustom.data.items.length > 0)) && (
|
||||
<>
|
||||
<Typography.Text>Namespace</Typography.Text>
|
||||
<Spacer $space={8} $samespace />
|
||||
{isCustomNamespaceResource ? (
|
||||
<Styled.FullWidthSelect
|
||||
placeholder="Choose namespace"
|
||||
options={namespacesDataCustom.data?.items.map(ns => ({
|
||||
label: ns.metadata.name,
|
||||
value: ns.metadata.name,
|
||||
}))}
|
||||
filterOption={filterSelectOptions}
|
||||
allowClear
|
||||
showSearch
|
||||
onSelect={value => {
|
||||
if (typeof value === 'string') {
|
||||
setSelectedNamespace(value)
|
||||
}
|
||||
}}
|
||||
onClear={() => setSelectedNamespace(undefined)}
|
||||
/>
|
||||
) : (
|
||||
<Styled.FullWidthSelect
|
||||
placeholder="Choose namespace"
|
||||
options={namespacesData.data?.items.map(ns => ({
|
||||
label: ns.metadata.name,
|
||||
value: ns.metadata.name,
|
||||
}))}
|
||||
filterOption={filterSelectOptions}
|
||||
allowClear
|
||||
showSearch
|
||||
onSelect={value => {
|
||||
if (typeof value === 'string') {
|
||||
setSelectedNamespace(value)
|
||||
}
|
||||
}}
|
||||
onClear={() => setSelectedNamespace(undefined)}
|
||||
/>
|
||||
)}
|
||||
<Spacer $space={8} $samespace />
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
onClick={() =>
|
||||
navigate(`${baseprefix}/inside/${cluster}${selectedNamespace ? `/${selectedNamespace}` : ''}/apis`)
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
export const TITLE_TEXT = import.meta.env.DEV
|
||||
? window._env_.TITLE_TEXT || import.meta.env.VITE_TITLE_TEXT
|
||||
: window._env_.TITLE_TEXT
|
||||
|
||||
export const LOGO_TEXT = import.meta.env.DEV
|
||||
? window._env_.LOGO_TEXT || import.meta.env.VITE_LOGO_TEXT
|
||||
: window._env_.LOGO_TEXT
|
||||
|
||||
export const FOOTER_TEXT = import.meta.env.DEV
|
||||
? window._env_.FOOTER_TEXT || import.meta.env.VITE_FOOTER_TEXT
|
||||
: window._env_.FOOTER_TEXT
|
||||
|
||||
export const CUSTOM_LOGO_SVG = import.meta.env.DEV
|
||||
? window._env_.CUSTOM_LOGO_SVG || import.meta.env.VITE_CUSTOM_LOGO_SVG
|
||||
: window._env_.CUSTOM_LOGO_SVG
|
||||
|
||||
export const CUSTOM_TENANT_TEXT = import.meta.env.DEV
|
||||
? window._env_.CUSTOM_TENANT_TEXT || import.meta.env.VITE_CUSTOM_TENANT_TEXT
|
||||
: window._env_.CUSTOM_TENANT_TEXT
|
||||
|
||||
export const BASE_API_GROUP = import.meta.env.DEV
|
||||
? window._env_.CUSTOMIZATION_API_GROUP || import.meta.env.VITE_CUSTOMIZATION_API_GROUP
|
||||
: window._env_.CUSTOMIZATION_API_GROUP
|
||||
@@ -93,3 +114,15 @@ export const BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY = import.meta.env.DEV
|
||||
export const BASE_NAMESPACE_FACTORY_KEY = import.meta.env.DEV
|
||||
? window._env_.BASE_NAMESPACE_FACTORY_KEY || import.meta.env.VITE_BASE_NAMESPACE_FACTORY_KEY
|
||||
: window._env_.BASE_NAMESPACE_FACTORY_KEY
|
||||
|
||||
export const CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP = import.meta.env.DEV
|
||||
? window._env_.CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP || import.meta.env.VITE_CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP
|
||||
: window._env_.CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP
|
||||
export const CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION = import.meta.env.DEV
|
||||
? window._env_.CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION ||
|
||||
import.meta.env.VITE_CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION
|
||||
: window._env_.CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION
|
||||
export const CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME = import.meta.env.DEV
|
||||
? window._env_.CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME ||
|
||||
import.meta.env.VITE_CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME
|
||||
: window._env_.CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME
|
||||
|
||||
Reference in New Issue
Block a user