Compare commits
3 commits
main
...
dashboard/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d33630ad1e | ||
|
|
e62a6d2a8e | ||
|
|
af0c54d2b0 |
13 changed files with 349 additions and 162 deletions
|
|
@ -156,7 +156,7 @@ menuItems = append(menuItems, map[string]any{
|
|||
map[string]any{
|
||||
"key": "{plural}",
|
||||
"label": "{ResourceLabel}",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/{group}/{version}/{plural}",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/api-table/{group}/{version}/{plural}",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
|
@ -174,7 +174,7 @@ menuItems = append(menuItems, map[string]any{
|
|||
|
||||
**Important Notes**:
|
||||
- The sidebar tag (`{lowercase-kind}-sidebar`) must match what the Factory uses
|
||||
- The link format: `/openapi-ui/{clusterName}/{namespace}/api-table/{group}/{version}/{plural}`
|
||||
- The link format: `/openapi-ui/{cluster}/{namespace}/api-table/{group}/{version}/{plural}`
|
||||
- All sidebars share the same `keysAndTags` and `menuItems`, so changes affect all sidebar instances
|
||||
|
||||
### Step 4: Verify Integration
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.Applic
|
|||
|
||||
key := plural // e.g., "virtualmachines"
|
||||
label := labelPlural
|
||||
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", strings.ToLower(group), strings.ToLower(version), plural)
|
||||
link := fmt.Sprintf("/openapi-ui/{cluster}/{namespace}/api-table/%s/%s/%s", strings.ToLower(group), strings.ToLower(version), plural)
|
||||
// If this is a module, change the first breadcrumb item to "Tenant Modules"
|
||||
if crd.Spec.Dashboard != nil && crd.Spec.Dashboard.Module {
|
||||
key = "tenantmodules"
|
||||
label = "Tenant Modules"
|
||||
link = "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
|
||||
link = "/openapi-ui/{cluster}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
|
||||
}
|
||||
|
||||
items := []any{
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str
|
|||
"data": map[string]any{
|
||||
"id": "vpc-subnets-table",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "virtualprivatecloud-subnets",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/configmaps",
|
||||
"fieldSelector": map[string]any{
|
||||
|
|
@ -190,7 +190,7 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str
|
|||
"data": map[string]any{
|
||||
"id": "resource-quotas-table",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-resource-quotas",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{reqsJsonPath[0]['.status.namespace']}/resourcequotas",
|
||||
"pathToItems": []any{`items`},
|
||||
|
|
@ -244,7 +244,7 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str
|
|||
"data": map[string]any{
|
||||
"id": "conditions-table",
|
||||
"fetchUrl": endpoint,
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-status-conditions",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"withoutControls": true,
|
||||
|
|
@ -266,7 +266,7 @@ func workloadsTab(kind string) map[string]any {
|
|||
"data": map[string]any{
|
||||
"id": "workloads-table",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloadmonitors",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"customizationId": "factory-details-v1alpha1.cozystack.io.workloadmonitors",
|
||||
"pathToItems": []any{"items"},
|
||||
|
|
@ -291,7 +291,7 @@ func servicesTab(kind string) map[string]any {
|
|||
"data": map[string]any{
|
||||
"id": "services-table",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"customizationId": "factory-details-v1.services",
|
||||
"pathToItems": []any{"items"},
|
||||
|
|
@ -317,7 +317,7 @@ func ingressesTab(kind string) map[string]any {
|
|||
"data": map[string]any{
|
||||
"id": "ingresses-table",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"customizationId": "factory-details-networking.k8s.io.v1.ingresses",
|
||||
"pathToItems": []any{"items"},
|
||||
|
|
@ -343,7 +343,7 @@ func secretsTab(kind string) map[string]any {
|
|||
"data": map[string]any{
|
||||
"id": "secrets-table",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/apis/core.cozystack.io/v1alpha1/namespaces/{3}/tenantsecrets",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"customizationId": "factory-details-v1alpha1.core.cozystack.io.tenantsecrets",
|
||||
"pathToItems": []any{"items"},
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
//
|
||||
// Menu rules:
|
||||
// - The first section is "Marketplace" with two hardcoded entries:
|
||||
// - Marketplace (/openapi-ui/{clusterName}/{namespace}/factory/marketplace)
|
||||
// - Tenant Info (/openapi-ui/{clusterName}/{namespace}/factory/info-details/info)
|
||||
// - Marketplace (/openapi-ui/{cluster}/{namespace}/factory/marketplace)
|
||||
// - Tenant Info (/openapi-ui/{cluster}/{namespace}/factory/info-details/info)
|
||||
// - All other sections are built from CRDs where spec.dashboard != nil.
|
||||
// - Categories are ordered strictly as:
|
||||
// Marketplace, IaaS, PaaS, NaaS, <others A→Z>, Resources, Backups, Administration
|
||||
|
|
@ -91,7 +91,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati
|
|||
// Weight (default 0)
|
||||
weight := def.Spec.Dashboard.Weight
|
||||
|
||||
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", g, v, plural)
|
||||
link := fmt.Sprintf("/openapi-ui/{cluster}/{namespace}/api-table/%s/%s/%s", g, v, plural)
|
||||
|
||||
categories[cat] = append(categories[cat], item{
|
||||
Key: plural,
|
||||
|
|
@ -146,7 +146,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati
|
|||
map[string]any{
|
||||
"key": "marketplace",
|
||||
"label": "Marketplace",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/factory/marketplace",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/factory/marketplace",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -182,17 +182,17 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati
|
|||
map[string]any{
|
||||
"key": "plans",
|
||||
"label": "Plans",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/backups.cozystack.io/v1alpha1/plans",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/plans",
|
||||
},
|
||||
map[string]any{
|
||||
"key": "backupjobs",
|
||||
"label": "BackupJobs",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backupjobs",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backupjobs",
|
||||
},
|
||||
map[string]any{
|
||||
"key": "backups",
|
||||
"label": "Backups",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backups",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/api-table/backups.cozystack.io/v1alpha1/backups",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -205,22 +205,22 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati
|
|||
map[string]any{
|
||||
"key": "info",
|
||||
"label": "Info",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/factory/info-details/info",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/factory/info-details/info",
|
||||
},
|
||||
map[string]any{
|
||||
"key": "modules",
|
||||
"label": "Modules",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules",
|
||||
},
|
||||
map[string]any{
|
||||
"key": "loadbalancer-services",
|
||||
"label": "External IPs",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/factory/external-ips",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/factory/external-ips",
|
||||
},
|
||||
map[string]any{
|
||||
"key": "tenants",
|
||||
"label": "Tenants",
|
||||
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/apps.cozystack.io/v1alpha1/tenants",
|
||||
"link": "/openapi-ui/{cluster}/{namespace}/api-table/apps.cozystack.io/v1alpha1/tenants",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,43 +17,43 @@ func CreateAllBreadcrumbs() []*dashboardv1alpha1.Breadcrumb {
|
|||
return []*dashboardv1alpha1.Breadcrumb{
|
||||
// Stock project factory configmap details
|
||||
createBreadcrumb("stock-project-factory-configmap-details", []map[string]any{
|
||||
createBreadcrumbItem("configmaps", "v1/configmaps", "/openapi-ui/{clusterName}/{namespace}/builtin-table/configmaps"),
|
||||
createBreadcrumbItem("configmaps", "v1/configmaps", "/openapi-ui/{cluster}/{namespace}/builtin-table/configmaps"),
|
||||
createBreadcrumbItem("configmap", "{6}"),
|
||||
}),
|
||||
|
||||
// Stock cluster factory namespace details
|
||||
createBreadcrumb("stock-cluster-factory-namespace-details", []map[string]any{
|
||||
createBreadcrumbItem("namespaces", "v1/namespaces", "/openapi-ui/{clusterName}/builtin-table/namespaces"),
|
||||
createBreadcrumbItem("namespaces", "v1/namespaces", "/openapi-ui/{cluster}/builtin-table/namespaces"),
|
||||
createBreadcrumbItem("namespace", "{5}"),
|
||||
}),
|
||||
|
||||
// Stock cluster factory node details
|
||||
createBreadcrumb("stock-cluster-factory-node-details", []map[string]any{
|
||||
createBreadcrumbItem("node", "v1/nodes", "/openapi-ui/{clusterName}/builtin-table/nodes"),
|
||||
createBreadcrumbItem("node", "v1/nodes", "/openapi-ui/{cluster}/builtin-table/nodes"),
|
||||
createBreadcrumbItem("node", "{5}"),
|
||||
}),
|
||||
|
||||
// Stock project factory pod details
|
||||
createBreadcrumb("stock-project-factory-pod-details", []map[string]any{
|
||||
createBreadcrumbItem("pods", "v1/pods", "/openapi-ui/{clusterName}/{namespace}/builtin-table/pods"),
|
||||
createBreadcrumbItem("pods", "v1/pods", "/openapi-ui/{cluster}/{namespace}/builtin-table/pods"),
|
||||
createBreadcrumbItem("pod", "{6}"),
|
||||
}),
|
||||
|
||||
// Stock project factory secret details
|
||||
createBreadcrumb("stock-project-factory-kube-secret-details", []map[string]any{
|
||||
createBreadcrumbItem("secrets", "v1/secrets", "/openapi-ui/{clusterName}/{namespace}/builtin-table/secrets"),
|
||||
createBreadcrumbItem("secrets", "v1/secrets", "/openapi-ui/{cluster}/{namespace}/builtin-table/secrets"),
|
||||
createBreadcrumbItem("secret", "{6}"),
|
||||
}),
|
||||
|
||||
// Stock project factory service details
|
||||
createBreadcrumb("stock-project-factory-kube-service-details", []map[string]any{
|
||||
createBreadcrumbItem("services", "v1/services", "/openapi-ui/{clusterName}/{namespace}/builtin-table/services"),
|
||||
createBreadcrumbItem("services", "v1/services", "/openapi-ui/{cluster}/{namespace}/builtin-table/services"),
|
||||
createBreadcrumbItem("service", "{6}"),
|
||||
}),
|
||||
|
||||
// Stock project factory ingress details
|
||||
createBreadcrumb("stock-project-factory-kube-ingress-details", []map[string]any{
|
||||
createBreadcrumbItem("ingresses", "networking.k8s.io/v1/ingresses", "/openapi-ui/{clusterName}/{namespace}/builtin-table/ingresses"),
|
||||
createBreadcrumbItem("ingresses", "networking.k8s.io/v1/ingresses", "/openapi-ui/{cluster}/{namespace}/builtin-table/ingresses"),
|
||||
createBreadcrumbItem("ingress", "{6}"),
|
||||
}),
|
||||
|
||||
|
|
@ -64,13 +64,13 @@ func CreateAllBreadcrumbs() []*dashboardv1alpha1.Breadcrumb {
|
|||
|
||||
// Stock cluster api form
|
||||
createBreadcrumb("stock-cluster-api-form", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{clusterName}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{cluster}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
||||
}),
|
||||
|
||||
// Stock cluster api form edit
|
||||
createBreadcrumb("stock-cluster-api-form-edit", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{clusterName}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{cluster}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
||||
}),
|
||||
|
||||
|
|
@ -81,13 +81,13 @@ func CreateAllBreadcrumbs() []*dashboardv1alpha1.Breadcrumb {
|
|||
|
||||
// Stock cluster builtin form
|
||||
createBreadcrumb("stock-cluster-builtin-form", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{clusterName}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{cluster}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
||||
}),
|
||||
|
||||
// Stock cluster builtin form edit
|
||||
createBreadcrumb("stock-cluster-builtin-form-edit", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{clusterName}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{cluster}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
||||
}),
|
||||
|
||||
|
|
@ -98,13 +98,13 @@ func CreateAllBreadcrumbs() []*dashboardv1alpha1.Breadcrumb {
|
|||
|
||||
// Stock project api form
|
||||
createBreadcrumb("stock-project-api-form", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{clusterName}/{namespace}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{cluster}/{namespace}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
||||
}),
|
||||
|
||||
// Stock project api form edit
|
||||
createBreadcrumb("stock-project-api-form-edit", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{clusterName}/{namespace}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{typeName}", "/openapi-ui/{cluster}/{namespace}/api-table/{apiGroup}/{apiVersion}/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
||||
}),
|
||||
|
||||
|
|
@ -115,13 +115,13 @@ func CreateAllBreadcrumbs() []*dashboardv1alpha1.Breadcrumb {
|
|||
|
||||
// Stock project builtin form
|
||||
createBreadcrumb("stock-project-builtin-form", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{clusterName}/{namespace}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{cluster}/{namespace}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
||||
}),
|
||||
|
||||
// Stock project builtin form edit
|
||||
createBreadcrumb("stock-project-builtin-form-edit", []map[string]any{
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{clusterName}/{namespace}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{typeName}", "/openapi-ui/{cluster}/{namespace}/builtin-table/{typeName}"),
|
||||
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
||||
}),
|
||||
}
|
||||
|
|
@ -516,11 +516,11 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"baseApiVersion": "v1alpha1",
|
||||
"baseprefix": "openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"id": 311,
|
||||
"mpResourceKind": "MarketplacePanel",
|
||||
"mpResourceName": "marketplacepanels",
|
||||
"namespacePartOfUrl": "{3}",
|
||||
"marketplaceKind": "MarketplacePanel",
|
||||
"marketplacePlural": "marketplacepanels",
|
||||
"namespace": "{3}",
|
||||
"baseApiGroup": "dashboard.cozystack.io",
|
||||
},
|
||||
"type": "MarketplaceCard",
|
||||
|
|
@ -1066,7 +1066,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"id": "service-port-mapping-table",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-kube-service-details-port-mapping",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}",
|
||||
"pathToItems": ".spec.ports",
|
||||
|
|
@ -1092,7 +1092,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"id": "service-pod-serving-table",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-kube-service-details-endpointslice",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/apis/discovery.k8s.io/v1/namespaces/{3}/endpointslices",
|
||||
"labelSelector": map[string]any{
|
||||
|
|
@ -1149,7 +1149,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"id": "pods-table",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-node-details-/v1/pods",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods",
|
||||
"labelSelectorFull": map[string]any{
|
||||
|
|
@ -1281,7 +1281,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"id": "rules-table",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses/{6}",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-kube-ingress-details-rules",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"withoutControls": true,
|
||||
|
|
@ -1433,7 +1433,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"id": "workloads-table",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"customizationId": "factory-details-v1alpha1.cozystack.io.workloads",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloads",
|
||||
"labelSelector": map[string]any{
|
||||
|
|
@ -1904,7 +1904,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|||
"data": map[string]any{
|
||||
"id": "external-ips-table",
|
||||
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services",
|
||||
"clusterNamePartOfUrl": "{2}",
|
||||
"cluster": "{2}",
|
||||
"baseprefix": "/openapi-ui",
|
||||
"customizationId": "factory-details-v1.services",
|
||||
"pathToItems": []any{"items"},
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ include ../../../hack/package.mk
|
|||
update: update-crd update-dockerfiles
|
||||
image: image-openapi-ui image-openapi-ui-k8s-bff image-token-proxy update-tenant-text
|
||||
|
||||
frontend-dev:
|
||||
hack/frontend-dev.sh
|
||||
|
||||
|
||||
update-dockerfiles:
|
||||
@echo Update dockerfiles manually
|
||||
|
|
|
|||
5
packages/system/dashboard/hack/.gitignore
vendored
Normal file
5
packages/system/dashboard/hack/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Cloned repos (created by frontend-dev.sh)
|
||||
openapi-k8s-toolkit/
|
||||
openapi-ui/
|
||||
openapi-ui-k8s-bff/
|
||||
.toolkit-patched
|
||||
90
packages/system/dashboard/hack/dev-nginx.conf
Normal file
90
packages/system/dashboard/hack/dev-nginx.conf
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
worker_processes 1;
|
||||
daemon off;
|
||||
error_log /dev/stderr warn;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
access_log /dev/stderr;
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080 default_server;
|
||||
|
||||
# Static cluster list
|
||||
location ~ ^(/clusterlist|/api/clusters)$ {
|
||||
add_header Content-Type application/json;
|
||||
return 200 '[{"api":"localhost","baseDomain":"localhost","description":"local","externalDomain":"localhost","name":"default","tenant":"dev"}]';
|
||||
}
|
||||
|
||||
# Strip /api/clusters/default prefix and self-proxy (same as production)
|
||||
location /api/clusters/default {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
|
||||
rewrite /api/clusters/default/(.*) /$1 break;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
||||
# K8s API via kubectl proxy
|
||||
location /k8s {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
proxy_buffering off;
|
||||
|
||||
rewrite /k8s/(.*) /$1 break;
|
||||
proxy_pass http://127.0.0.1:8081;
|
||||
}
|
||||
|
||||
# BFF HTTP
|
||||
location /openapi-bff {
|
||||
proxy_pass http://127.0.0.1:4002;
|
||||
}
|
||||
|
||||
# BFF WebSocket
|
||||
location /openapi-bff-ws/ {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
|
||||
proxy_pass http://127.0.0.1:4002;
|
||||
}
|
||||
|
||||
# Redirect / to /openapi-ui
|
||||
location = / {
|
||||
return 301 /openapi-ui;
|
||||
}
|
||||
|
||||
# Frontend (Vite dev server) — catch-all with WebSocket support for HMR
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
proxy_pass http://127.0.0.1:4001;
|
||||
}
|
||||
|
||||
location /healthcheck {
|
||||
access_log off;
|
||||
return 200 "Healthy\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
189
packages/system/dashboard/hack/frontend-dev.sh
Executable file
189
packages/system/dashboard/hack/frontend-dev.sh
Executable file
|
|
@ -0,0 +1,189 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
DASHBOARD_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${CYAN}==>${NC} $*"; }
|
||||
|
||||
# Verify kubectl access
|
||||
log "Using kubectl context: $(kubectl config current-context)"
|
||||
if ! kubectl api-resources --api-group=core.cozystack.io &>/dev/null; then
|
||||
echo "ERROR: Cluster does not have core.cozystack.io API group." >&2
|
||||
echo "Set KUBECONFIG to a cluster with Cozystack installed, e.g.:" >&2
|
||||
echo " KUBECONFIG=/path/to/kubeconfig $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup on exit
|
||||
cleanup() {
|
||||
echo ""
|
||||
log "Stopping all services..."
|
||||
kill 0 2>/dev/null
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# --- Clone repositories ---
|
||||
clone_repo() {
|
||||
local url="$1" dir="$2" branch="${3:-}"
|
||||
if [ ! -d "$dir/.git" ]; then
|
||||
log "Cloning $dir..."
|
||||
git clone "$url" "$dir"
|
||||
fi
|
||||
if [ -n "$branch" ]; then
|
||||
git -C "$dir" checkout "$branch" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
log "Preparing repositories..."
|
||||
clone_repo git@github.com:PRO-Robotech/openapi-ui.git openapi-ui release/1.3.0
|
||||
clone_repo git@github.com:PRO-Robotech/openapi-ui-k8s-bff.git openapi-ui-k8s-bff release/1.3.0
|
||||
clone_repo git@github.com:PRO-Robotech/openapi-k8s-toolkit.git openapi-k8s-toolkit release/1.3.0
|
||||
|
||||
# --- Install dependencies ---
|
||||
log "Installing dependencies..."
|
||||
for dir in openapi-ui openapi-ui-k8s-bff openapi-k8s-toolkit; do
|
||||
if [ ! -d "$dir/node_modules" ]; then
|
||||
log " npm install in $dir"
|
||||
(cd "$dir" && npm install --silent)
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Build patched toolkit ---
|
||||
PATCHES_DIR="$DASHBOARD_DIR/images/openapi-ui/openapi-k8s-toolkit/patches"
|
||||
UI_TOOLKIT_DIST="openapi-ui/node_modules/@prorobotech/openapi-k8s-toolkit/dist"
|
||||
|
||||
if [ ! -f .toolkit-patched ]; then
|
||||
log "Building patched openapi-k8s-toolkit..."
|
||||
(
|
||||
cd openapi-k8s-toolkit
|
||||
git checkout -- . 2>/dev/null || true
|
||||
for patch in "$PATCHES_DIR"/*.diff; do
|
||||
[ -f "$patch" ] && git apply "$patch" && log " Applied $(basename "$patch")"
|
||||
done
|
||||
npm run build --silent
|
||||
)
|
||||
log "Replacing toolkit in openapi-ui node_modules..."
|
||||
rm -rf "$UI_TOOLKIT_DIST"
|
||||
cp -r openapi-k8s-toolkit/dist "$UI_TOOLKIT_DIST"
|
||||
touch .toolkit-patched
|
||||
fi
|
||||
|
||||
# --- Prepare UI ---
|
||||
mkdir -p openapi-ui/public
|
||||
|
||||
if [ ! -f openapi-ui/.env.options ]; then
|
||||
log "Creating openapi-ui/.env.options..."
|
||||
sed 's|^KUBE_API_URL=.*|KUBE_API_URL=http://localhost:8080|;s|^BFF_URL=.*|BFF_URL=http://localhost:4002|' \
|
||||
openapi-ui/.env.options.dist > openapi-ui/.env.options
|
||||
fi
|
||||
|
||||
if [ ! -f openapi-ui/public/env.js ]; then
|
||||
log "Generating openapi-ui/public/env.js..."
|
||||
cat > openapi-ui/public/env.js <<'ENVJS'
|
||||
window._env_ = {
|
||||
BASEPREFIX: "/openapi-ui",
|
||||
HIDE_INSIDE: "true",
|
||||
CUSTOMIZATION_API_GROUP: "dashboard.cozystack.io",
|
||||
CUSTOMIZATION_API_VERSION: "v1alpha1",
|
||||
CUSTOMIZATION_NAVIGATION_RESOURCE: "navigation",
|
||||
CUSTOMIZATION_NAVIGATION_RESOURCE_NAME: "navigations",
|
||||
INSTANCES_API_GROUP: "dashboard.cozystack.io",
|
||||
INSTANCES_RESOURCE_NAME: "instances",
|
||||
INSTANCES_VERSION: "v1alpha1",
|
||||
MARKETPLACE_GROUP: "dashboard.cozystack.io",
|
||||
MARKETPLACE_KIND: "MarketplacePanel",
|
||||
MARKETPLACE_RESOURCE_NAME: "marketplacepanels",
|
||||
MARKETPLACE_VERSION: "v1alpha1",
|
||||
NAVIGATE_FROM_CLUSTERLIST: "/openapi-ui/~recordValue~/api-table/core.cozystack.io/v1alpha1/tenantnamespaces",
|
||||
PROJECTS_API_GROUP: "core.cozystack.io",
|
||||
PROJECTS_RESOURCE_NAME: "tenantnamespaces",
|
||||
PROJECTS_VERSION: "v1alpha1",
|
||||
CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP: "core.cozystack.io",
|
||||
CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION: "v1alpha1",
|
||||
CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME: "tenantnamespaces",
|
||||
USE_NAMESPACE_NAV: "true",
|
||||
TITLE_TEXT: "Cozystack Dashboard",
|
||||
FOOTER_TEXT: "Cozystack",
|
||||
CUSTOM_TENANT_TEXT: "dev",
|
||||
THEME_TOKENS_COLORS_DARK: {
|
||||
"colorText": "rgba(232, 236, 244, 1)",
|
||||
"colorTextSecondary": "rgba(167, 177, 196, 1)",
|
||||
"colorTextDisabled": "rgba(109, 119, 136, 1)",
|
||||
"colorTextTertiary": "rgba(15, 17, 21, 1)",
|
||||
"colorBgLayout": "rgba(15, 17, 21, 1)",
|
||||
"colorBgContainer": "rgba(23, 26, 32, 1)",
|
||||
"colorBgSpotlight": "rgba(30, 34, 43, 1)",
|
||||
"colorBgElevated": "rgba(35, 40, 50, 1)",
|
||||
"colorBorder": "rgba(42, 49, 60, 1)",
|
||||
"colorBgMask": "rgba(0, 0, 0, 0.55)",
|
||||
"colorPrimaryHover": "rgba(37, 108, 219, 1)",
|
||||
"colorPrimary": "rgba(58, 134, 255, 1)",
|
||||
"colorPrimaryActive": "rgba(27, 86, 178, 1)",
|
||||
"colorPrimaryBg": "rgba(54, 134, 255, 0.13)",
|
||||
"colorSuccess": "rgba(61, 209, 138, 1)",
|
||||
"colorWarning": "rgba(245, 165, 36, 1)",
|
||||
"colorError": "rgba(240, 81, 77, 1)",
|
||||
"colorInfo": "rgba(108, 166, 255, 1)"
|
||||
},
|
||||
THEME_TOKENS_SIZES: {"borderRadius": 6},
|
||||
THEME_TOKENS_COMPONENTS_LIGHT: {"Layout": {}},
|
||||
THEME_TOKENS_COMPONENTS_DARK: {"Layout": {}},
|
||||
THEME_TOKENS_USE_MERGE_STRATEGY: 'false'
|
||||
}
|
||||
ENVJS
|
||||
fi
|
||||
|
||||
# --- Prepare BFF ---
|
||||
if [ ! -f openapi-ui-k8s-bff/.env ]; then
|
||||
log "Generating openapi-ui-k8s-bff/.env..."
|
||||
cat > openapi-ui-k8s-bff/.env <<'BFFENV'
|
||||
DEV_KUBE_API_URL=http://localhost:8080/k8s
|
||||
BASE_API_GROUP=dashboard.cozystack.io
|
||||
BASE_API_VERSION=v1alpha1
|
||||
BASE_NAVIGATION_RESOURCE_PLURAL=navigations
|
||||
BASE_NAVIGATION_RESOURCE_NAME=navigation
|
||||
BASE_FRONTEND_PREFIX=/openapi-ui
|
||||
BASE_NAMESPACE_FULL_PATH=/apis/core.cozystack.io/v1alpha1/tenantnamespaces
|
||||
BASE_FACTORY_NAMESPACED_API_KEY=base-factory-namespaced-api
|
||||
BASE_FACTORY_CLUSTERSCOPED_API_KEY=base-factory-clusterscoped-api
|
||||
BASE_FACTORY_NAMESPACED_BUILTIN_KEY=base-factory-namespaced-builtin
|
||||
BASE_FACTORY_CLUSTERSCOPED_BUILTIN_KEY=base-factory-clusterscoped-builtin
|
||||
BASE_NAMESPACE_FACTORY_KEY=base-factory-clusterscoped-builtin
|
||||
BASE_ALLOWED_AUTH_HEADERS=user-agent,accept,content-type,application/json,origin,referer,accept-encoding,cookie
|
||||
BFFENV
|
||||
fi
|
||||
|
||||
# --- Start kubectl proxy + nginx reverse proxy ---
|
||||
# nginx on :8080 mirrors the production self-proxy architecture:
|
||||
# /api/clusters/default/* → rewrite → self-proxy back to :8080
|
||||
# /k8s/* → kubectl proxy on :8081
|
||||
# /openapi-bff* → BFF on :4002
|
||||
# /* → Vite dev server on :4001
|
||||
log "Starting kubectl proxy on :8081..."
|
||||
kubectl proxy --port=8081 &
|
||||
|
||||
log "Starting nginx reverse proxy on :8080..."
|
||||
nginx -c "$SCRIPT_DIR/dev-nginx.conf" -p "$SCRIPT_DIR" &
|
||||
|
||||
# --- Start BFF ---
|
||||
log "Starting BFF on :4002..."
|
||||
(cd openapi-ui-k8s-bff && npm run dev) &
|
||||
|
||||
# --- Start UI ---
|
||||
log "Starting UI on :4001..."
|
||||
(cd openapi-ui && npm run dev) &
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}==========================================${NC}"
|
||||
echo -e "${GREEN} Cozystack Dashboard is running${NC}"
|
||||
echo -e "${GREEN} http://localhost:8080/openapi-ui${NC}"
|
||||
echo -e "${GREEN}==========================================${NC}"
|
||||
echo ""
|
||||
|
||||
wait
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# imported from https://github.com/cozystack/openapi-ui-k8s-bff
|
||||
# imported from https://github.com/PRO-Robotech/openapi-ui-k8s-bff (release/1.3.0)
|
||||
ARG NODE_VERSION=20.18.1
|
||||
FROM node:${NODE_VERSION}-alpine AS builder
|
||||
WORKDIR /src
|
||||
|
||||
ARG COMMIT_REF=183dc9dcbb0f8a1833dad642c35faa385c71e58d
|
||||
ARG COMMIT_REF=ff359166163b263b51f51c57f416a9feb1a7e02a
|
||||
RUN wget -O- https://github.com/PRO-Robotech/openapi-ui-k8s-bff/archive/${COMMIT_REF}.tar.gz | tar xzf - --strip-components=1
|
||||
|
||||
ENV PATH=/src/node_modules/.bin:$PATH
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
ARG NODE_VERSION=20.18.1
|
||||
|
||||
# openapi-k8s-toolkit
|
||||
# imported from https://github.com/cozystack/openapi-k8s-toolkit
|
||||
# imported from https://github.com/PRO-Robotech/openapi-k8s-toolkit (release/1.3.0)
|
||||
FROM node:${NODE_VERSION}-alpine AS openapi-k8s-toolkit-builder
|
||||
RUN apk add git
|
||||
WORKDIR /src
|
||||
ARG COMMIT=cb2f122caafaa2fd5455750213d9e633017ec555
|
||||
RUN wget -O- https://github.com/cozystack/openapi-k8s-toolkit/archive/${COMMIT}.tar.gz | tar -xzvf- --strip-components=1
|
||||
ARG COMMIT=9f2b163e657e932dee6c70b9ff5969e98779c5b1
|
||||
RUN wget -O- https://github.com/PRO-Robotech/openapi-k8s-toolkit/archive/${COMMIT}.tar.gz | tar -xzvf- --strip-components=1
|
||||
|
||||
COPY openapi-k8s-toolkit/patches /patches
|
||||
RUN git apply /patches/*.diff
|
||||
|
|
@ -17,12 +17,12 @@ RUN npm run build
|
|||
|
||||
|
||||
# openapi-ui
|
||||
# imported from https://github.com/cozystack/openapi-ui
|
||||
# imported from https://github.com/PRO-Robotech/openapi-ui (release/1.3.0)
|
||||
FROM node:${NODE_VERSION}-alpine AS builder
|
||||
#RUN apk add git
|
||||
WORKDIR /src
|
||||
|
||||
ARG COMMIT_REF=3cfbbf2156b6a5e4a1f283a032019530c0c2d37d
|
||||
ARG COMMIT_REF=94cdf528ff5674c34cb856ac808b99bd393268c9
|
||||
RUN wget -O- https://github.com/PRO-Robotech/openapi-ui/archive/${COMMIT_REF}.tar.gz | tar xzf - --strip-components=1
|
||||
|
||||
#COPY openapi-ui/patches /patches
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
diff --git a/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts b/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts
|
||||
index 87a0f12..fb2e1cc 100644
|
||||
--- a/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts
|
||||
+++ b/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts
|
||||
@@ -134,22 +134,6 @@ export const prepare = ({
|
||||
// impossible in k8s
|
||||
return {}
|
||||
})
|
||||
- if (customFields.length > 0) {
|
||||
- dataSource = dataSource.map((el: TJSON) => {
|
||||
- const newFieldsForComplexJsonPath: Record<string, TJSON> = {}
|
||||
- customFields.forEach(({ dataIndex, jsonPath }) => {
|
||||
- const jpQueryResult = jp.query(el, `$${jsonPath}`)
|
||||
- newFieldsForComplexJsonPath[dataIndex] =
|
||||
- Array.isArray(jpQueryResult) && jpQueryResult.length === 1 ? jpQueryResult[0] : jpQueryResult
|
||||
- })
|
||||
- if (typeof el === 'object') {
|
||||
- return { ...el, ...newFieldsForComplexJsonPath }
|
||||
- }
|
||||
- // impossible in k8s
|
||||
- return { ...newFieldsForComplexJsonPath }
|
||||
- })
|
||||
- }
|
||||
-
|
||||
// Handle flatMap: expand rows for map objects
|
||||
// Process all flatMap columns sequentially
|
||||
if (flatMapColumns.length > 0 && dataSource) {
|
||||
@@ -204,6 +188,62 @@ export const prepare = ({
|
||||
currentDataSource = expandedDataSource
|
||||
})
|
||||
dataSource = currentDataSource
|
||||
+ }
|
||||
+
|
||||
+ if (customFields.length > 0) {
|
||||
+ dataSource = dataSource.map((el: TJSON) => {
|
||||
+ const newFieldsForComplexJsonPath: Record<string, TJSON> = {}
|
||||
+ customFields.forEach(({ dataIndex, jsonPath }) => {
|
||||
+ let fieldValue: TJSON = null
|
||||
+ let handled = false
|
||||
+
|
||||
+ const flatMapMatch = jsonPath.match(/^(.*)\[(_flatMap[^\]]+_Key)\](.*)$/)
|
||||
+ if (flatMapMatch && el && typeof el === 'object' && !Array.isArray(el)) {
|
||||
+ const basePath = flatMapMatch[1]
|
||||
+ const keyField = flatMapMatch[2]
|
||||
+ const tailPath = flatMapMatch[3]
|
||||
+ const keyValue = (el as Record<string, unknown>)[keyField]
|
||||
+ if (keyValue !== null && keyValue !== undefined) {
|
||||
+ const baseResult = jp.query(el, `$${basePath}`)[0]
|
||||
+ if (baseResult && typeof baseResult === 'object' && !Array.isArray(baseResult)) {
|
||||
+ const baseValue = (baseResult as Record<string, unknown>)[String(keyValue)]
|
||||
+ if (tailPath) {
|
||||
+ const normalizedTailPath =
|
||||
+ tailPath.startsWith('.') || tailPath.startsWith('[') ? tailPath : `.${tailPath}`
|
||||
+ const tailResult = jp.query(baseValue, `$${normalizedTailPath}`)
|
||||
+ fieldValue = Array.isArray(tailResult) && tailResult.length === 1 ? tailResult[0] : tailResult
|
||||
+ } else {
|
||||
+ fieldValue = baseValue as TJSON
|
||||
+ }
|
||||
+ handled = true
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!handled) {
|
||||
+ let resolvedJsonPath = jsonPath
|
||||
+ if (el && typeof el === 'object' && !Array.isArray(el)) {
|
||||
+ resolvedJsonPath = jsonPath.replace(/\[(_flatMap[^\]]+_Key)\]/g, (match, keyField) => {
|
||||
+ const keyValue = (el as Record<string, unknown>)[keyField]
|
||||
+ if (keyValue === null || keyValue === undefined) {
|
||||
+ return match
|
||||
+ }
|
||||
+ const escaped = String(keyValue).replace(/'/g, "\\'")
|
||||
+ return `['${escaped}']`
|
||||
+ })
|
||||
+ }
|
||||
+ const jpQueryResult = jp.query(el, `$${resolvedJsonPath}`)
|
||||
+ fieldValue = Array.isArray(jpQueryResult) && jpQueryResult.length === 1 ? jpQueryResult[0] : jpQueryResult
|
||||
+ }
|
||||
+
|
||||
+ newFieldsForComplexJsonPath[dataIndex] = fieldValue
|
||||
+ })
|
||||
+ if (typeof el === 'object') {
|
||||
+ return { ...el, ...newFieldsForComplexJsonPath }
|
||||
+ }
|
||||
+ // impossible in k8s
|
||||
+ return { ...newFieldsForComplexJsonPath }
|
||||
+ })
|
||||
}
|
||||
} else {
|
||||
dataSource = dataItems.map((el: TJSON) => {
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
diff --git a/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx b/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx
|
||||
index 8bcef4d..2551e92 100644
|
||||
--- a/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx
|
||||
+++ b/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx
|
||||
@@ -22,6 +22,15 @@ import { TableFactory } from '../../molecules'
|
||||
@@ -20,6 +20,15 @@ import { TableFactory } from '../../molecules'
|
||||
import { ShortenedTextWithTooltip, FilterDropdown, TrimmedTags, TextAlignContainer, TinyButton } from './atoms'
|
||||
import { TInternalDataForControls } from './types'
|
||||
|
||||
|
||||
+const getPluralForm = (singular: string): string => {
|
||||
+ // If already ends with 's', add 'es'
|
||||
+ if (singular.endsWith('s')) {
|
||||
|
|
@ -18,33 +17,24 @@ index 8bcef4d..2551e92 100644
|
|||
export const getCellRender = ({
|
||||
value,
|
||||
record,
|
||||
@@ -255,7 +264,7 @@ export const getEnrichedColumnsWithControls = ({
|
||||
key: 'controls',
|
||||
className: 'controls',
|
||||
width: 60,
|
||||
- render: (value: TInternalDataForControls) => {
|
||||
+ render: (value: TInternalDataForControls, record: unknown) => {
|
||||
return (
|
||||
// <TextAlignContainer $align="right" className="hideable">
|
||||
<TextAlignContainer $align="center">
|
||||
@@ -279,10 +288,19 @@ export const getEnrichedColumnsWithControls = ({
|
||||
domEvent.stopPropagation()
|
||||
domEvent.preventDefault()
|
||||
if (key === 'edit') {
|
||||
+ // Special case: redirect tenantmodules from core.cozystack.io to apps.cozystack.io with plural form
|
||||
+ let apiGroupAndVersion = value.apiGroupAndVersion
|
||||
+ let typeName = value.typeName
|
||||
+ if (apiGroupAndVersion?.startsWith('core.cozystack.io/') && typeName === 'tenantmodules') {
|
||||
+ let plural = value.plural
|
||||
+ if (apiGroupAndVersion?.startsWith('core.cozystack.io/') && plural === 'tenantmodules') {
|
||||
+ const appsApiVersion = apiGroupAndVersion.replace('core.cozystack.io/', 'apps.cozystack.io/')
|
||||
+ const pluralTypeName = getPluralForm(value.entryName)
|
||||
+ const pluralTypeName = getPluralForm(value.name)
|
||||
+ apiGroupAndVersion = appsApiVersion
|
||||
+ typeName = pluralTypeName
|
||||
+ plural = pluralTypeName
|
||||
+ }
|
||||
navigate(
|
||||
`${baseprefix}/${value.cluster}${value.namespace ? `/${value.namespace}` : ''}${
|
||||
value.syntheticProject ? `/${value.syntheticProject}` : ''
|
||||
- }/${value.pathPrefix}/${value.apiGroupAndVersion}/${value.typeName}/${value.entryName}?backlink=${
|
||||
+ }/${value.pathPrefix}/${apiGroupAndVersion}/${typeName}/${value.entryName}?backlink=${
|
||||
- }/${value.pathPrefix}/${value.apiGroupAndVersion}/${value.plural}/${value.name}?backlink=${
|
||||
+ }/${value.pathPrefix}/${apiGroupAndVersion}/${plural}/${value.name}?backlink=${
|
||||
value.backlink
|
||||
}`,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue