package dashboard import ( "encoding/json" "strings" dashboardv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) // ---------------- Complete refactored static resources ---------------- // CreateAllBreadcrumbs creates all breadcrumb resources using helper functions 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/{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/{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/{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/{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/{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/{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/{cluster}/{namespace}/builtin-table/ingresses"), createBreadcrumbItem("ingress", "{6}"), }), // Stock cluster api table createBreadcrumb("stock-cluster-api-table", []map[string]any{ createBreadcrumbItem("api", "{apiGroup}/{apiVersion}/{plural}"), }), // Stock cluster api form createBreadcrumb("stock-cluster-api-form", []map[string]any{ createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{plural}", "/openapi-ui/{cluster}/api-table/{apiGroup}/{apiVersion}/{plural}"), 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}/{plural}", "/openapi-ui/{cluster}/api-table/{apiGroup}/{apiVersion}/{plural}"), createBreadcrumbItem("create-api-res-namespaced-typename", "Update"), }), // Stock cluster builtin table createBreadcrumb("stock-cluster-builtin-table", []map[string]any{ createBreadcrumbItem("api", "v1/{plural}"), }), // Stock cluster builtin form createBreadcrumb("stock-cluster-builtin-form", []map[string]any{ createBreadcrumbItem("create-api-res-namespaced-table", "v1/{plural}", "/openapi-ui/{cluster}/builtin-table/{plural}"), 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/{plural}", "/openapi-ui/{cluster}/builtin-table/{plural}"), createBreadcrumbItem("create-api-res-namespaced-typename", "Update"), }), // Stock project api table createBreadcrumb("stock-project-api-table", []map[string]any{ createBreadcrumbItem("api", "{apiGroup}/{apiVersion}/{plural}"), }), // Stock project api form createBreadcrumb("stock-project-api-form", []map[string]any{ createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{plural}", "/openapi-ui/{cluster}/{namespace}/api-table/{apiGroup}/{apiVersion}/{plural}"), 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}/{plural}", "/openapi-ui/{cluster}/{namespace}/api-table/{apiGroup}/{apiVersion}/{plural}"), createBreadcrumbItem("create-api-res-namespaced-typename", "Update"), }), // Stock project builtin table createBreadcrumb("stock-project-builtin-table", []map[string]any{ createBreadcrumbItem("api", "v1/{plural}"), }), // Stock project builtin form createBreadcrumb("stock-project-builtin-form", []map[string]any{ createBreadcrumbItem("create-api-res-namespaced-table", "v1/{plural}", "/openapi-ui/{cluster}/{namespace}/builtin-table/{plural}"), 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/{plural}", "/openapi-ui/{cluster}/{namespace}/builtin-table/{plural}"), createBreadcrumbItem("create-api-res-namespaced-typename", "Update"), }), } } // CreateAllCustomColumnsOverrides creates all custom column override resources using helper functions func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverride { return []*dashboardv1alpha1.CustomColumnsOverride{ // Factory details v1 services createCustomColumnsOverride("factory-details-v1.services", []any{ createCustomColumnWithSpecificColor("Name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("ClusterIP", ".spec.clusterIP"), createStringColumn("LoadbalancerIP", ".status.loadBalancer.ingress[0].ip"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace v1 services createCustomColumnsOverride("stock-namespace-/v1/services", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("ClusterIP", ".spec.clusterIP"), createStringColumn("LoadbalancerIP", ".status.loadBalancer.ingress[0].ip"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace core cozystack io v1alpha1 tenantmodules createCustomColumnsOverride("stock-namespace-/core.cozystack.io/v1alpha1/tenantmodules", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Module", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/{reqsJsonPath[0]['.metadata.name']['-']}-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createReadyColumn(), createTimestampColumn("Created", ".metadata.creationTimestamp"), createStringColumn("Version", ".status.version"), }), // Factory service details endpointslice (Pod serving table) createCustomColumnsOverride("factory-kube-service-details-endpointslice", []any{ createStringColumn("Pod", ".targetRef.name"), createArrayColumn("Addresses", ".addresses"), createBoolColumn("Ready", ".conditions.ready"), createStringColumn("Node", ".nodeName"), }), // Factory service details port mapping createCustomColumnsOverride("factory-kube-service-details-port-mapping", []any{ createStringColumn("Name", ".name"), createStringColumn("Port", ".port"), createStringColumn("Protocol", ".protocol"), createStringColumn("Pod port or name", ".targetPort"), }), // Factory details v1alpha1 cozystack io workloadmonitors createCustomColumnsOverride("factory-details-v1alpha1.cozystack.io.workloadmonitors", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "WorkloadMonitor", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/workloadmonitor-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("TYPE", ".spec.type"), createStringColumn("VERSION", ".spec.version"), createStringColumn("REPLICAS", ".spec.replicas"), createStringColumn("MINREPLICAS", ".spec.minReplicas"), createStringColumn("AVAILABLE", ".status.availableReplicas"), createStringColumn("OBSERVED", ".status.observedReplicas"), }), // Factory details v1alpha1 core cozystack io tenantsecrets createCustomColumnsOverride("factory-details-v1alpha1.core.cozystack.io.tenantsecrets", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createFlatMapColumn("Data", ".data"), createStringColumn("Key", "_flatMapData_Key"), createSecretBase64Column("Value", "._flatMapData_Value"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Virtual private cloud subnets createCustomColumnsOverride("virtualprivatecloud-subnets", []any{ createFlatMapColumn("Data", ".data"), createStringColumn("Subnet Parameters", "_flatMapData_Key"), createStringColumn("Values", "_flatMapData_Value"), }), // Factory resource quotas createCustomColumnsOverride("factory-resource-quotas", []any{ createFlatMapColumn("Data", ".spec.hard"), createStringColumn("Resource", "_flatMapData_Key"), createStringColumn("Hard", "_flatMapData_Value"), createStringColumn("Used", ".status.used[_flatMapData_Key]"), }), // Factory ingress details rules createCustomColumnsOverride("factory-kube-ingress-details-rules", []any{ createStringColumn("Host", ".host"), createCustomColumnWithJsonPath("Service", ".http.paths[0].backend.service.name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.http.paths[0].backend.service.name']['-']}"), createStringColumn("Port", ".http.paths[0].backend.service.port.number"), createStringColumn("Path", ".http.paths[0].path"), }), // Factory node images createCustomColumnsOverride("factory-node-images", []any{ createStringColumn("ImageID", ".names[0]"), createConverterBytesColumn("Size", ".sizeBytes"), }), // Factory pod details volume list createCustomColumnsOverride("factory-pod-details-volume-list", []any{ createStringColumn("Name", ".name"), }), // Factory details events. Event Time falls back to `.firstTimestamp` // because core/v1 Events (Helm, controller-runtime) populate only the // legacy timestamps while events.k8s.io/v1 Events populate only // `.eventTime`; taking the first non-null of the two covers both. createCustomColumnsOverride("factory-details-events", []any{ createTimestampColumn("Last Seen", ".lastTimestamp", ".eventTime"), createTimestampColumn("Event Time", ".eventTime", ".firstTimestamp"), createStringColumn("Type", ".type"), createStringColumn("Reason", ".reason"), createStringColumn("Object", ".involvedObject.kind"), createStringColumn("Name", ".involvedObject.name"), createStringColumn("Message", ".message"), }), // Factory status conditions createCustomColumnsOverride("factory-status-conditions", []any{ createStringColumn("Type", ".type"), createBoolColumn("Status", ".status"), createTimestampColumn("Updated", ".lastTransitionTime"), createStringColumn("Reason", ".reason"), createStringColumn("Message", ".message"), }), // Container status init containers list createCustomColumnsOverride("container-status-init-containers-list", []any{ createStringColumn("Name", ".name"), createStringColumn("Image", ".imageID"), createBoolColumn("Started", ".started"), createBoolColumn("Ready", ".ready"), createStringColumn("RestartCount", ".restartCount"), createStringColumn("WaitingReason", ".state.waiting.reason"), createStringColumn("TerminatedReason", ".state.terminated.reason"), }), // Container status containers list createCustomColumnsOverride("container-status-containers-list", []any{ createStringColumn("Name", ".name"), createStringColumn("Image", ".imageID"), createBoolColumn("Started", ".started"), createBoolColumn("Ready", ".ready"), createStringColumn("RestartCount", ".restartCount"), createStringColumn("WaitingReason", ".state.waiting.reason"), createStringColumn("TerminatedReason", ".state.terminated.reason"), }), // Container spec init containers list createCustomColumnsOverride("container-spec-init-containers-list", []any{ createStringColumn("Name", ".name"), createStringColumn("Image", ".image"), createArrayColumn("Resources requests", ".resources.requests"), createArrayColumn("Resources limits", ".resources.limits"), }), // Container spec containers list createCustomColumnsOverride("container-spec-containers-list", []any{ createStringColumn("Name", ".name"), createStringColumn("Image", ".image"), createArrayColumn("Resources requests", ".resources.requests"), createArrayColumn("Resources limits", ".resources.limits"), createArrayColumn("Ports", ".ports[*].containerPort"), }), // Factory details networking k8s io v1 ingresses createCustomColumnsOverride("factory-details-networking.k8s.io.v1.ingresses", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Ingress", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-ingress-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("Hosts", ".spec.rules[*].host"), createStringColumn("Address", ".status.loadBalancer.ingress[0].ip"), createStringColumn("Port", ".spec.defaultBackend.service.port.number"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace networking k8s io v1 ingresses createCustomColumnsOverride("stock-namespace-/networking.k8s.io/v1/ingresses", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Ingress", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-ingress-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("Hosts", ".spec.rules[*].host"), createStringColumn("Address", ".status.loadBalancer.ingress[0].ip"), createStringColumn("Port", ".spec.defaultBackend.service.port.number"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock cluster v1 configmaps createCustomColumnsOverride("stock-cluster-/v1/configmaps", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "ConfigMap", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace v1 configmaps createCustomColumnsOverride("stock-namespace-/v1/configmaps", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "ConfigMap", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Cluster v1 configmaps createCustomColumnsOverride("cluster-/v1/configmaps", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "ConfigMap", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock cluster v1 nodes createCustomColumnsOverride("stock-cluster-/v1/nodes", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Node", "", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createSimpleStatusColumn("Status", "node-status"), }), // Factory node details v1 pods createCustomColumnsOverride("factory-node-details-v1.pods", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"), createStringColumn("Restart Policy", ".spec.restartPolicy"), createStringColumn("Pod IP", ".status.podIP"), createStringColumn("QOS", ".status.qosClass"), createSimpleStatusColumn("Status", "pod-status"), }), // Factory v1 pods createCustomColumnsOverride("factory-v1.pods", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithoutJsonPath("Node", "Node", "", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"), createStringColumn("Restart Policy", ".spec.restartPolicy"), createStringColumn("Pod IP", ".status.podIP"), createStringColumn("QOS", ".status.qosClass"), createSimpleStatusColumn("Status", "pod-status"), }), // Stock cluster v1 pods createCustomColumnsOverride("stock-cluster-/v1/pods", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "#009596", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "#a25792ff", "/openapi-ui/{2}/factory/tenantnamespace/{reqsJsonPath[0]['.metadata.namespace']['-']}"), createCustomColumnWithJsonPath("Node", ".spec.nodeName", "Node", "#8476d1", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"), createStringColumn("Restart Policy", ".spec.restartPolicy"), createStringColumn("Pod IP", ".status.podIP"), createStringColumn("QOS", ".status.qosClass"), createSimpleStatusColumn("Status", "pod-status"), }), // Stock namespace v1 pods createCustomColumnsOverride("stock-namespace-/v1/pods", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "#009596", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithoutJsonPath("Node", "Node", "#8476d1", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"), createStringColumn("Restart Policy", ".spec.restartPolicy"), createStringColumn("Pod IP", ".status.podIP"), createStringColumn("QOS", ".status.qosClass"), createSimpleStatusColumn("Status", "pod-status"), }), // Stock cluster v1 secrets createCustomColumnsOverride("stock-cluster-/v1/secrets", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "#c46100", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "#a25792ff", "/openapi-ui/{2}/factory/tenantnamespace/{reqsJsonPath[0]['.metadata.namespace']['-']}"), createStringColumn("Type", ".type"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace v1 secrets createCustomColumnsOverride("stock-namespace-/v1/secrets", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "#c46100", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("Type", ".type"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Factory details v1alpha1 cozystack io workloads createCustomColumnsOverride("factory-details-v1alpha1.cozystack.io.workloads", []any{ createStringColumn("Name", ".metadata.name"), createStringColumn("Kind", ".status.kind"), createStringColumn("Type", ".status.type"), createStringColumn("CPU", ".status.resources.cpu"), createStringColumn("Memory", ".status.resources.memory"), createStringColumn("Operational", ".status.operational"), }), // Stock cluster core cozystack io v1alpha1 tenantnamespaces createCustomColumnsOverride("stock-cluster-/core.cozystack.io/v1alpha1/tenantnamespaces", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "TenantNamespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.name']['-']}/factory/marketplace"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace backups cozystack io v1alpha1 plans createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/plans", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Plan", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/plan-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createApplicationRefColumn("Application"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace backups cozystack io v1alpha1 backupjobs createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/backupjobs", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "BackupJob", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/backupjob-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("Phase", ".status.phase"), createApplicationRefColumn("Application"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace backups cozystack io v1alpha1 backups createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/backups", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "Backup", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/backup-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("Phase", ".status.phase"), createApplicationRefColumn("Application"), createTimestampColumn("Taken At", ".spec.takenAt"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), // Stock namespace backups cozystack io v1alpha1 restorejobs createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/restorejobs", []any{ createCustomColumnWithJsonPath("Name", ".metadata.name", "RestoreJob", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/restorejob-details/{reqsJsonPath[0]['.metadata.name']['-']}"), createStringColumn("Phase", ".status.phase"), createStringColumn("Backup", ".spec.backupRef.name"), createTimestampColumn("Created", ".metadata.creationTimestamp"), }), } } // CreateAllCustomFormsOverrides creates all custom forms override resources using helper functions func CreateAllCustomFormsOverrides() []*dashboardv1alpha1.CustomFormsOverride { return []*dashboardv1alpha1.CustomFormsOverride{ // Default networking k8s io v1 ingresses createCustomFormsOverride("default-/networking.k8s.io/v1/ingresses", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.rules", "Rules", "array"), }, }), // Default storage k8s io v1 storageclasses createCustomFormsOverride("default-/storage.k8s.io/v1/storageclasses", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("provisioner", "Provisioner", "text"), createFormItem("reclaimPolicy", "Reclaim Policy", "select"), }, }), // Default v1 configmaps createCustomFormsOverride("default-/v1/configmaps", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("data", "Data", "object"), }, }), // Default v1 namespaces createCustomFormsOverride("default-/v1/namespaces", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.labels", "Labels", "object"), }, }), // Default v1 nodes createCustomFormsOverride("default-/v1/nodes", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("spec.podCIDR", "Pod CIDR", "text"), }, }), // Default v1 persistentvolumeclaims createCustomFormsOverride("default-/v1/persistentvolumeclaims", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.accessModes", "Access Modes", "array"), }, }), // Default v1 persistentvolumes createCustomFormsOverride("default-/v1/persistentvolumes", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("spec.capacity", "Capacity", "object"), }, }), // Default v1 pods createCustomFormsOverride("default-/v1/pods", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.containers", "Containers", "array"), }, }), // Default v1 secrets createCustomFormsOverride("default-/v1/secrets", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("type", "Type", "text"), }, }), // Default v1 services createCustomFormsOverride("default-/v1/services", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.ports", "Ports", "array"), }, }), // Plans form override - backups.cozystack.io/v1alpha1 createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/plans", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.applicationRef.kind", "Application Kind", "text"), createFormItem("spec.applicationRef.name", "Application Name", "text"), createFormItem("spec.schedule.type", "Schedule Type", "text"), createFormItem("spec.schedule.cron", "Schedule Cron", "text"), }, "schema": createSchema(map[string]any{ "backupClassName": listInputScemaItemBackupClass(), }), }), // BackupJobs form override - backups.cozystack.io/v1alpha1 createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/backupjobs", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.planRef.name", "Plan Name (optional)", "text"), createFormItem("spec.applicationRef.apiGroup", "Application API Group", "text"), createFormItem("spec.applicationRef.kind", "Application Kind", "text"), createFormItem("spec.applicationRef.name", "Application Name", "text"), }, "schema": createSchema(map[string]any{ "backupClassName": listInputScemaItemBackupClass(), }), }), // RestoreJobs form override - backups.cozystack.io/v1alpha1 createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/restorejobs", map[string]any{ "formItems": []any{ createFormItem("metadata.name", "Name", "text"), createFormItem("metadata.namespace", "Namespace", "text"), createFormItem("spec.backupRef.name", "Backup", "text"), // Target application: leave empty to restore into the same application // as referenced by the selected Backup. Fill all three to restore into // a different application (e.g. rename, or restore into a new target). createFormItem("spec.targetApplicationRef.apiGroup", "Target Application API Group (optional, used to restore into a different application)", "text"), createFormItem("spec.targetApplicationRef.kind", "Target Application Kind (optional, used to restore into a different application)", "text"), createFormItem("spec.targetApplicationRef.name", "Target Application Name (optional, used to restore into a different application)", "text"), // Driver-specific options (key-value editor). Refer to the backup driver // documentation for supported keys. createFormItem("spec.options", "Options (driver-specific key/value pairs)", "object"), }, "schema": createSchema(map[string]any{ "backupRef": map[string]any{ "properties": map[string]any{ "name": listInputSchemaItemBackup(), }, }, }), }), } } // CreateAllFactories creates all factory resources using helper functions func CreateAllFactories() []*dashboardv1alpha1.Factory { // Marketplace factory marketplaceSpec := map[string]any{ "key": "marketplace", "sidebarTags": []any{ "marketplace-sidebar", }, "urlsToFetch": []any{}, "withScrollableMainContentCard": true, "data": []any{ contentCardWithTitle(31, "Marketplace", map[string]any{ "flexGrow": 1, }, []any{ map[string]any{ "data": map[string]any{ "baseApiVersion": "v1alpha1", "baseprefix": "openapi-ui", "cluster": "{2}", "id": 311, "marketplaceKind": "MarketplacePanel", "marketplacePlural": "marketplacepanels", "namespace": "{3}", "baseApiGroup": "dashboard.cozystack.io", }, "type": "MarketplaceCard", }, }), }, } // Namespace details factory using unified approach namespaceConfig := UnifiedResourceConfig{ Name: "namespace-details", ResourceType: "factory", Kind: "Namespace", Plural: "namespaces", Title: "namespace", } namespaceSpec := createUnifiedFactory(namespaceConfig, nil, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{5}"}) // Node details factory nodeHeader := createNodeHeader() // Create node spec with tabs containing items nodeTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ map[string]any{ "type": "ContentCard", "data": map[string]any{ "id": "details-card", "style": map[string]any{ "marginBottom": "24px", }, }, "children": []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "details-title", "text": "Node details", "strong": true, "style": map[string]any{ "fontSize": float64(20), "marginBottom": "12px", }, }, }, }, }, }, }, } nodeSpec := map[string]any{ "key": "node-details", "sidebarTags": []any{"node-sidebar"}, "withScrollableMainContentCard": true, "urlsToFetch": []any{"/api/clusters/{2}/k8s/api/v1/nodes/{5}"}, "data": []any{ nodeHeader, map[string]any{ "type": "antdTabs", "data": map[string]any{ "id": "tabs-root", "defaultActiveKey": "details", "items": nodeTabs, }, }, }, } // Pod details factory podHeader := createPodHeader() // Create pod spec with empty tabs (items: nil) podSpec := map[string]any{ "key": "pod-details", "sidebarTags": []any{"pods-sidebar"}, "withScrollableMainContentCard": true, "urlsToFetch": []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"}, "data": []any{ podHeader, map[string]any{ "type": "antdTabs", "data": map[string]any{ "id": "tabs-root", "defaultActiveKey": "details", "items": nil, }, }, }, } // Secret details factory secretHeader := map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": float64(6), "style": map[string]any{ "marginBottom": "24px", }, }, "children": []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "badge-secret", "text": "S", "title": "secret", "style": map[string]any{ "backgroundColor": "#c46100", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "id": "header-secret-name", "text": "{reqsJsonPath[0]['.metadata.name']['-']}", "style": map[string]any{ "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "lineHeight": "24px", }, }, }, }, } secretTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": "24px", }, []any{ antdText("details-title", true, "Secret details", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("details-spacer", 16), antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-name-label", true, "Namespace", nil), antdFlex("header-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "15px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-labels-block", 8, []any{ antdText("labels-title", true, "Labels", map[string]any{ "fontSize": 14, }), map[string]any{ "type": "Labels", "data": map[string]any{ "id": "labels-editor", "endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}", "jsonPathToLabels": ".metadata.labels", "pathToValue": "/metadata/labels", "reqIndex": 0, "modalTitle": "Edit labels", "modalDescriptionText": "", "inputLabel": "", "notificationSuccessMessage": "Updated successfully", "notificationSuccessMessageDescription": "Labels have been updated", "editModalWidth": 650, "maxEditTagTextLength": 35, "paddingContainerEnd": "24px", "containerStyle": map[string]any{ "marginTop": "-30px", }, "selectProps": map[string]any{ "maxTagTextLength": 35, }, }, }, }), antdFlexVertical("ds-annotations", 4, []any{ antdText("annotations", true, "Annotations", nil), map[string]any{ "type": "Annotations", "data": map[string]any{ "id": "annotations", "endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}", "jsonPathToObj": ".metadata.annotations", "pathToValue": "/metadata/annotations", "reqIndex": 0, "modalTitle": "Edit annotations", "modalDescriptionText": "", "inputLabel": "", "notificationSuccessMessage": "Updated successfully", "notificationSuccessMessageDescription": "Annotations have been updated", "editModalWidth": "800px", "errorText": "0 Annotations", "text": "~counter~ Annotations", "cols": []any{11, 11, 2}, }, }, }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", }, }, }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdFlexVertical("secret-type-block", 4, []any{ antdText("secret-type-label", true, "Type", nil), parsedText("secret-type-value", "{reqsJsonPath[0]['.type']['-']}", nil), }), antdFlexVertical("secret-sa-block", 4, []any{ map[string]any{ "type": "parsedText", "data": map[string]any{ "id": "serviceaccount-title", "text": "ServiceAccount", "strong": true, "style": map[string]any{ "fontWeight": "bold", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "serviceaccount-link", "text": "{reqsJsonPath[0]['.metadata.annotations[\"kubernetes.io/service-account.name\"]']['-']}", "href": "/openapi-ui/{2}/{3}/factory/serviceaccount-details/{reqsJsonPath[0]['.metadata.annotations[\"kubernetes.io/service-account.name\"]']['-']}", }, }, }), }), }), }), }), }, }, map[string]any{ "key": "yaml", "label": "YAML", "children": []any{ map[string]any{ "type": "YamlEditorSingleton", "data": map[string]any{ "id": "yaml-editor", "cluster": "{2}", "isNameSpaced": true, "prefillValuesRequestIndex": 0, "substractHeight": float64(400), "type": "builtin", "plural": "secrets", "readOnly": true, }, }, }, }, } secretSpec := createFactorySpec("kube-secret-details", []any{"secret-sidebar"}, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}"}, secretHeader, secretTabs) // Service details factory serviceHeader := map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": float64(6), "style": map[string]any{ "marginBottom": "24px", }, }, "children": []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "badge-service", "text": "S", "title": "services", "style": map[string]any{ "backgroundColor": "#6ca100", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "id": "service-name", "text": "{reqsJsonPath[0]['.metadata.name']['-']}", "style": map[string]any{ "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "lineHeight": "24px", }, }, }, }, } serviceTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": "24px", }, []any{ antdText("details-title", true, "Service details", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("details-spacer", 16), antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-name-label", true, "Namespace", nil), antdFlex("header-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "15px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-labels-block", 8, []any{ antdText("labels-title", true, "Labels", map[string]any{ "fontSize": 14, }), map[string]any{ "type": "Labels", "data": map[string]any{ "id": "labels-editor", "endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}", "jsonPathToLabels": ".metadata.labels", "pathToValue": "/metadata/labels", "reqIndex": 0, "modalTitle": "Edit labels", "modalDescriptionText": "", "inputLabel": "", "notificationSuccessMessage": "Updated successfully", "notificationSuccessMessageDescription": "Labels have been updated", "editModalWidth": 650, "maxEditTagTextLength": 35, "paddingContainerEnd": "24px", "containerStyle": map[string]any{ "marginTop": "-30px", }, "selectProps": map[string]any{ "maxTagTextLength": 35, }, }, }, }), antdFlexVertical("meta-pod-selector-block", 4, []any{ antdText("pod-selector", true, "Pod selector", map[string]any{ "fontSize": 14, }), map[string]any{ "type": "LabelsToSearchParams", "data": map[string]any{ "id": "pod-to-search-params", "jsonPathToLabels": ".spec.selector", "linkPrefix": "/openapi-ui/{2}/search", "reqIndex": 0, "errorText": "-", }, }, }), antdFlexVertical("ds-annotations", 4, []any{ antdText("annotations", true, "Annotations", nil), map[string]any{ "type": "Annotations", "data": map[string]any{ "id": "annotations", "endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}", "jsonPathToObj": ".metadata.annotations", "pathToValue": "/metadata/annotations", "reqIndex": 0, "modalTitle": "Edit annotations", "modalDescriptionText": "", "inputLabel": "", "notificationSuccessMessage": "Updated successfully", "notificationSuccessMessageDescription": "Annotations have been updated", "editModalWidth": "800px", "errorText": "0 Annotations", "text": "~counter~ Annotations", "cols": []any{11, 11, 2}, }, }, }), antdFlexVertical("meta-session-affinity-block", 4, []any{ antdText("meta-session-affinity-label", true, "Session affinity", nil), parsedText("meta-session-affinity-value", "{reqsJsonPath[0]['.spec.sessionAffinity']['Not configured']}", nil), }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", }, }, }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdText("routing-title", true, "Service routing", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("routing-spacer", 16), antdFlexVertical("service-hostname-block", 4, []any{ antdText("service-hostname-label", true, "Hostname", nil), parsedText("service-hostname-value", "{reqsJsonPath[0]['.metadata.name']['-']}.{reqsJsonPath[0]['.metadata.namespace']['-']}.svc.cluster.local", nil), }), antdFlexVertical("service-ip-block", 12, []any{ antdFlexVertical("clusterip-block", 4, []any{ antdText("clusterip-label", true, "ClusterIP address", nil), parsedText("clusterip-value", "{reqsJsonPath[0]['.spec.clusterIP']['-']}", nil), }), antdFlexVertical("loadbalancerip-block", 4, []any{ antdText("loadbalancerip-label", true, "LoadBalancerIP address", nil), parsedText("loadbalancerip-value", "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].ip']['Not Configured']}", nil), }), }), antdFlexVertical("service-port-mapping-block", 4, []any{ antdText("service-port-mapping-label", true, "Service port mapping", nil), map[string]any{ "type": "EnrichedTable", "data": map[string]any{ "id": "service-port-mapping-table", "baseprefix": "/openapi-ui", "cluster": "{2}", "customizationId": "factory-kube-service-details-port-mapping", "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}", "pathToItems": ".spec.ports", "withoutControls": true, }, }, }), map[string]any{ "type": "VisibilityContainer", "data": map[string]any{ "id": "service-pod-serving-vis", "value": "{reqsJsonPath[0]['.spec.selector']['-']}", "style": map[string]any{ "margin": 0, "padding": 0, }, }, "children": []any{ antdFlexVertical("service-pod-serving-block", 4, []any{ antdText("service-pod-serving-label", true, "Pod serving", nil), map[string]any{ "type": "EnrichedTable", "data": map[string]any{ "id": "service-pod-serving-table", "baseprefix": "/openapi-ui", "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{ "kubernetes.io/service-name": "{reqsJsonPath[0]['.metadata.name']['-']}", }, "pathToItems": ".items[*].endpoints", "withoutControls": true, }, }, }), }, }, }), }), }), }), }, }, map[string]any{ "key": "yaml", "label": "YAML", "children": []any{ map[string]any{ "type": "YamlEditorSingleton", "data": map[string]any{ "id": "yaml-editor", "cluster": "{2}", "isNameSpaced": true, "prefillValuesRequestIndex": 0, "substractHeight": float64(400), "type": "builtin", "plural": "services", }, }, }, }, map[string]any{ "key": "pods", "label": "Pods", "children": []any{ map[string]any{ "type": "VisibilityContainer", "data": map[string]any{ "id": "service-pod-serving-vis", "value": "{reqsJsonPath[0]['.spec.selector']['-']}", "style": map[string]any{ "margin": 0, "padding": 0, }, }, "children": []any{ map[string]any{ "type": "EnrichedTable", "data": map[string]any{ "id": "pods-table", "baseprefix": "/openapi-ui", "cluster": "{2}", "customizationId": "factory-node-details-/v1/pods", "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods", "labelSelectorFull": map[string]any{ "pathToLabels": ".spec.selector", "reqIndex": 0, }, "pathToItems": ".items", "withoutControls": false, }, }, }, }, }, }, } serviceSpec := createFactorySpec("kube-service-details", []any{"service-sidebar"}, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}"}, serviceHeader, serviceTabs) // Ingress details factory ingressHeader := map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": 6, "style": map[string]any{ "marginBottom": float64(24), }, }, "children": []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "badge-ingress", "text": "I", "title": "ingresses", "style": map[string]any{ "backgroundColor": "#2e7dff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": float64(20), "fontWeight": float64(400), "lineHeight": "24px", "minWidth": float64(24), "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "id": "ingress-name", "text": "{reqsJsonPath[0]['.metadata.name']['-']}", "style": map[string]any{ "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": float64(20), "lineHeight": "24px", }, }, }, }, } ingressTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": float64(24), }, []any{ antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-namespace-label", true, "Namespace", nil), map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "namespace-row", "align": "center", "gap": 6, }, "children": []any{ createUnifiedBadgeFromKind("ns-badge", "Namespace"), antdLink("namespace-link", "{reqsJsonPath[0]['.metadata.namespace']['-']}", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", ), }, }, }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ antdText("time-icon", false, "🌐", nil), parsedTextWithFormatter("time-value", "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", "timestamp"), }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdFlexVertical("status-ingress-ip", 4, []any{ antdText("status-ingress-ip-label", true, "LoadBalancer IP", nil), parsedText("status-ingress-ip-value", "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].ip']['-']}", nil), }), antdFlexVertical("status-ingress-hostname", 4, []any{ antdText("status-ingress-hostname-label", true, "LoadBalancer Hostname", nil), parsedText("status-ingress-hostname-value", "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].hostname']['-']}", nil), }), }), }), }), spacer("rules-title-spacer", float64(16)), antdText("rules-title", true, "Rules", map[string]any{ "fontSize": float64(20), }), spacer("rules-spacer", float64(8)), map[string]any{ "type": "EnrichedTable", "data": map[string]any{ "id": "rules-table", "fetchUrl": "/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses/{6}", "cluster": "{2}", "customizationId": "factory-kube-ingress-details-rules", "baseprefix": "/openapi-ui", "withoutControls": true, "pathToItems": []any{"spec", "rules"}, }, }, }), }, }, map[string]any{ "key": "yaml", "label": "YAML", "children": []any{ map[string]any{ "type": "YamlEditorSingleton", "data": map[string]any{ "id": "yaml-editor", "cluster": "{2}", "isNameSpaced": true, "type": "apis", "apiGroup": "networking.k8s.io", "apiVersion": "v1", "plural": "ingresses", "prefillValuesRequestIndex": float64(0), "substractHeight": float64(400), }, }, }, }, } ingressSpec := createFactorySpec("kube-ingress-details", []any{"ingress-sidebar"}, []any{"/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses/{6}"}, ingressHeader, ingressTabs) // Workloadmonitor details factory workloadmonitorHeader := createWorkloadmonitorHeader() workloadmonitorTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": float64(24), }, []any{ antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-namespace-label", true, "Namespace", nil), antdFlex("namespace-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "ns-badge", "text": "NS", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": 15, "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, parsedTextWithFormatter("time-value", "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", "timestamp"), }), }), antdFlexVertical("meta-kind-block", 4, []any{ antdText("kind-label", true, "Kind", nil), parsedText("kind-value", "{reqsJsonPath[0]['.spec.kind']['-']}", nil), }), antdFlexVertical("meta-type-block", 4, []any{ antdText("type-label", true, "Type", nil), parsedText("type-value", "{reqsJsonPath[0]['.spec.type']['-']}", nil), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdText("params-title", true, "Parameters", map[string]any{ "fontSize": float64(20), "marginBottom": float64(12), }), antdFlexVertical("params-list", 24, []any{ antdFlexVertical("param-version", 4, []any{ antdText("param-version-label", true, "Version", nil), parsedText("param-version-value", "{reqsJsonPath[0]['.spec.version']['-']}", nil), }), antdFlexVertical("param-replicas", 4, []any{ antdText("param-replicas-label", true, "Replicas", nil), parsedText("param-replicas-value", "{reqsJsonPath[0]['.spec.replicas']['-']}", nil), }), antdFlexVertical("param-minreplicas", 4, []any{ antdText("param-minreplicas-label", true, "MinReplicas", nil), parsedText("param-minreplicas-value", "{reqsJsonPath[0]['.spec.minReplicas']['-']}", nil), }), antdFlexVertical("param-available", 4, []any{ antdText("param-available-label", true, "AvailableReplicas", nil), parsedText("param-available-value", "{reqsJsonPath[0]['.status.availableReplicas']['-']}", nil), }), antdFlexVertical("param-observed", 4, []any{ antdText("param-observed-label", true, "ObservedReplicas", nil), parsedText("param-observed-value", "{reqsJsonPath[0]['.status.observedReplicas']['-']}", nil), }), antdFlexVertical("param-operational", 4, []any{ antdText("param-operational-label", true, "Operational", nil), parsedText("param-operational-value", "{reqsJsonPath[0]['.status.operational']['-']}", nil), }), }), }), }), }), }), }, }, map[string]any{ "key": "workloads", "label": "Workloads", "children": []any{ map[string]any{ "type": "EnrichedTable", "data": map[string]any{ "id": "workloads-table", "baseprefix": "/openapi-ui", "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{ "workloads.cozystack.io/monitor": "{reqs[0]['metadata','name']}", }, "pathToItems": []any{"items"}, }, }, }, }, map[string]any{ "key": "yaml", "label": "YAML", "children": []any{ map[string]any{ "type": "YamlEditorSingleton", "data": map[string]any{ "id": "yaml-editor", "cluster": "{2}", "isNameSpaced": true, "prefillValuesRequestIndex": 0, "substractHeight": float64(400), "type": "apis", "apiGroup": "cozystack.io", "apiVersion": "v1alpha1", "plural": "workloadmonitors", }, }, }, }, } workloadmonitorSpec := createFactorySpec("workloadmonitor-details", []any{"workloadmonitor-sidebar"}, []any{"/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloadmonitors/{6}"}, workloadmonitorHeader, workloadmonitorTabs) // Plan details factory using unified approach planConfig := UnifiedResourceConfig{ Name: "plan-details", ResourceType: "factory", Kind: "Plan", Plural: "plans", Title: "plan", } planTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": "24px", }, []any{ antdText("details-title", true, "Plan details", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("details-spacer", 16), antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-namespace-label", true, "Namespace", nil), antdFlex("header-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "15px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", }, }, }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdFlexVertical("spec-application-ref-block", 4, []any{ antdText("application-ref-label", true, "Application", nil), parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil), }), antdFlexVertical("spec-backup-class-name-block", 4, []any{ antdText("backup-class-name-label", true, "Backup Class", nil), parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil), }), antdFlexVertical("spec-schedule-type-block", 4, []any{ antdText("schedule-type-label", true, "Schedule Type", nil), parsedText("schedule-type-value", "{reqsJsonPath[0]['.spec.schedule.type']['-']}", nil), }), antdFlexVertical("spec-schedule-cron-block", 4, []any{ antdText("schedule-cron-label", true, "Schedule Cron", nil), parsedText("schedule-cron-value", "{reqsJsonPath[0]['.spec.schedule.cron']['-']}", nil), }), }), }), }), }), }, }, } planSpec := createUnifiedFactory(planConfig, planTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/plans/{6}"}) // BackupJob details factory using unified approach backupJobConfig := UnifiedResourceConfig{ Name: "backupjob-details", ResourceType: "factory", Kind: "BackupJob", Plural: "backupjobs", Title: "backupjob", } backupJobTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": "24px", }, []any{ antdText("details-title", true, "BackupJob details", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("details-spacer", 16), antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-namespace-label", true, "Namespace", nil), antdFlex("header-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "15px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", }, }, }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdFlexVertical("status-phase-block", 4, []any{ antdText("phase-label", true, "Phase", nil), parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil), }), antdFlexVertical("spec-plan-ref-block", 4, []any{ antdText("plan-ref-label", true, "Plan Ref", nil), parsedText("plan-ref-value", "{reqsJsonPath[0]['.spec.planRef.name']['-']}", nil), }), antdFlexVertical("spec-application-ref-block", 4, []any{ antdText("application-ref-label", true, "Application", nil), parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil), }), antdFlexVertical("spec-backup-class-name-block", 4, []any{ antdText("backup-class-name-label", true, "Backup Class", nil), parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil), }), antdFlexVertical("status-backup-ref-block", 4, []any{ antdText("backup-ref-label", true, "Backup Ref", nil), parsedText("backup-ref-value", "{reqsJsonPath[0]['.status.backupRef.name']['-']}", nil), }), antdFlexVertical("status-started-at-block", 4, []any{ antdText("started-at-label", true, "Started At", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.status.startedAt']['-']}", }, }, }), }), antdFlexVertical("status-completed-at-block", 4, []any{ antdText("completed-at-label", true, "Completed At", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.status.completedAt']['-']}", }, }, }), }), antdFlexVertical("status-message-block", 4, []any{ antdText("message-label", true, "Message", nil), parsedText("message-value", "{reqsJsonPath[0]['.status.message']['-']}", nil), }), }), }), }), }), }, }, } backupJobSpec := createUnifiedFactory(backupJobConfig, backupJobTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/backupjobs/{6}"}) // Backup details factory using unified approach backupConfig := UnifiedResourceConfig{ Name: "backup-details", ResourceType: "factory", Kind: "Backup", Plural: "backups", Title: "backup", } backupTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": "24px", }, []any{ antdText("details-title", true, "Backup details", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("details-spacer", 16), antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-namespace-label", true, "Namespace", nil), antdFlex("header-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "15px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-created-block", 4, []any{ antdText("time-label", true, "Created", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", }, }, }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdFlexVertical("status-phase-block", 4, []any{ antdText("phase-label", true, "Phase", nil), parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil), }), antdFlexVertical("spec-taken-at-block", 4, []any{ antdText("taken-at-label", true, "Taken At", nil), antdFlex("time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "time-value", "text": "{reqsJsonPath[0]['.spec.takenAt']['-']}", }, }, }), }), antdFlexVertical("spec-plan-ref-block", 4, []any{ antdText("plan-ref-label", true, "Plan Ref", nil), parsedText("plan-ref-value", "{reqsJsonPath[0]['.spec.planRef.name']['-']}", nil), }), antdFlexVertical("spec-application-ref-block", 4, []any{ antdText("application-ref-label", true, "Application", nil), parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil), }), antdFlexVertical("spec-backup-class-name-block", 4, []any{ antdText("backup-class-name-label", true, "Backup Class", nil), parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil), }), antdFlexVertical("status-artifact-uri-block", 4, []any{ antdText("artifact-uri-label", true, "Artifact URI", nil), parsedText("artifact-uri-value", "{reqsJsonPath[0]['.status.artifact.uri']['-']}", nil), }), antdFlexVertical("status-artifact-size-block", 4, []any{ antdText("artifact-size-label", true, "Artifact Size", nil), parsedText("artifact-size-value", "{reqsJsonPath[0]['.status.artifact.sizeBytes']['-']}", nil), }), antdFlexVertical("status-artifact-checksum-block", 4, []any{ antdText("artifact-checksum-label", true, "Artifact Checksum", nil), parsedText("artifact-checksum-value", "{reqsJsonPath[0]['.status.artifact.checksum']['-']}", nil), }), }), }), }), }), }, }, } backupSpec := createUnifiedFactory(backupConfig, backupTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/backups/{6}"}) // RestoreJob details factory using unified approach restoreJobConfig := UnifiedResourceConfig{ Name: "restorejob-details", ResourceType: "factory", Kind: "RestoreJob", Plural: "restorejobs", Title: "restorejob", } restoreJobTabs := []any{ map[string]any{ "key": "details", "label": "Details", "children": []any{ contentCard("details-card", map[string]any{ "marginBottom": "24px", }, []any{ antdText("details-title", true, "RestoreJob details", map[string]any{ "fontSize": 20, "marginBottom": "12px", }), spacer("details-spacer", 16), antdRow("details-grid", []any{48, 12}, []any{ antdCol("col-left", 12, []any{ antdFlexVertical("col-left-stack", 24, []any{ antdFlexVertical("meta-name-block", 4, []any{ antdText("meta-name-label", true, "Name", nil), parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil), }), antdFlexVertical("meta-namespace-block", 8, []any{ antdText("meta-namespace-label", true, "Namespace", nil), antdFlex("header-row", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "15px", "fontWeight": 400, "lineHeight": "24px", "minWidth": 24, "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, }, map[string]any{ "type": "antdLink", "data": map[string]any{ "id": "namespace-link", "text": "{reqsJsonPath[0]['.metadata.namespace']['-']}", "href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace", }, }, }), }), antdFlexVertical("meta-created-block", 4, []any{ antdText("created-time-label", true, "Created", nil), antdFlex("created-time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "created-time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "created-time-value", "text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", }, }, }), }), }), }), antdCol("col-right", 12, []any{ antdFlexVertical("col-right-stack", 24, []any{ antdFlexVertical("status-phase-block", 4, []any{ antdText("phase-label", true, "Phase", nil), parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil), }), antdFlexVertical("spec-backup-ref-block", 4, []any{ antdText("backup-ref-label", true, "Backup Ref", nil), parsedText("backup-ref-value", "{reqsJsonPath[0]['.spec.backupRef.name']['-']}", nil), }), antdFlexVertical("spec-target-application-ref-block", 4, []any{ antdText("target-application-ref-label", true, "Target Application", nil), // targetApplicationRef is optional — when absent, the restore targets // the same application as the selected Backup. Show a single friendly // fallback in that case instead of rendering "-.-/-". parsedText("target-application-ref-value", "{reqsJsonPath[0]['.spec.targetApplicationRef.name']['Same as backup']}", nil), }), antdFlexVertical("spec-options-target-namespace-block", 4, []any{ antdText("options-target-namespace-label", true, "Target Namespace", nil), parsedText("options-target-namespace-value", "{reqsJsonPath[0]['.spec.options.targetNamespace']['-']}", nil), }), antdFlexVertical("spec-options-fail-if-target-exists-block", 4, []any{ antdText("options-fail-if-target-exists-label", true, "Fail If Target Exists", nil), parsedText("options-fail-if-target-exists-value", "{reqsJsonPath[0]['.spec.options.failIfTargetExists']['-']}", nil), }), antdFlexVertical("spec-options-keep-original-pvc-block", 4, []any{ antdText("options-keep-original-pvc-label", true, "Keep Original PVC", nil), parsedText("options-keep-original-pvc-value", "{reqsJsonPath[0]['.spec.options.keepOriginalPVC']['-']}", nil), }), antdFlexVertical("spec-options-keep-original-ip-mac-block", 4, []any{ antdText("options-keep-original-ip-mac-label", true, "Keep Original IP/MAC", nil), parsedText("options-keep-original-ip-mac-value", "{reqsJsonPath[0]['.spec.options.keepOriginalIpAndMac']['-']}", nil), }), antdFlexVertical("status-started-at-block", 4, []any{ antdText("started-at-label", true, "Started At", nil), antdFlex("started-at-time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "started-at-time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "started-at-time-value", "text": "{reqsJsonPath[0]['.status.startedAt']['-']}", }, }, }), }), antdFlexVertical("status-completed-at-block", 4, []any{ antdText("completed-at-label", true, "Completed At", nil), antdFlex("completed-at-time-block", 6, []any{ map[string]any{ "type": "antdText", "data": map[string]any{ "id": "completed-at-time-icon", "text": "🌐", }, }, map[string]any{ "type": "parsedText", "data": map[string]any{ "formatter": "timestamp", "id": "completed-at-time-value", "text": "{reqsJsonPath[0]['.status.completedAt']['-']}", }, }, }), }), antdFlexVertical("status-message-block", 4, []any{ antdText("message-label", true, "Message", nil), parsedText("message-value", "{reqsJsonPath[0]['.status.message']['-']}", nil), }), }), }), }), }), }, }, } restoreJobSpec := createUnifiedFactory(restoreJobConfig, restoreJobTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/restorejobs/{6}"}) // External IPs factory (filtered services) externalIPsTabs := []any{ map[string]any{ "key": "services", "label": "Services", "children": []any{ map[string]any{ "type": "EnrichedTable", "data": map[string]any{ "id": "external-ips-table", "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services", "cluster": "{2}", "baseprefix": "/openapi-ui", "customizationId": "factory-details-v1.services", "pathToItems": ".items", "fieldSelector": map[string]any{ "spec.type": "LoadBalancer", }, }, }, }, }, } externalIPsSpec := map[string]any{ "key": "external-ips", "sidebarTags": []any{"external-ips-sidebar"}, "withScrollableMainContentCard": true, "urlsToFetch": []any{}, "data": []any{ map[string]any{ "type": "antdTabs", "data": map[string]any{ "id": "tabs-root", "defaultActiveKey": "services", "items": externalIPsTabs, }, }, }, } return []*dashboardv1alpha1.Factory{ createFactory("marketplace", marketplaceSpec), createFactory("namespace-details", namespaceSpec), createFactory("node-details", nodeSpec), createFactory("pod-details", podSpec), createFactory("kube-secret-details", secretSpec), createFactory("kube-service-details", serviceSpec), createFactory("kube-ingress-details", ingressSpec), createFactory("workloadmonitor-details", workloadmonitorSpec), createFactory("plan-details", planSpec), createFactory("backupjob-details", backupJobSpec), createFactory("backup-details", backupSpec), createFactory("restorejob-details", restoreJobSpec), createFactory("external-ips", externalIPsSpec), } } // CreateAllNavigations creates all navigation resources using helper functions func CreateAllNavigations() []*dashboardv1alpha1.Navigation { // Build baseFactoriesMapping for static (built-in) factories baseFactoriesMapping := map[string]string{ // Cluster-scoped builtin resources "base-factory-clusterscoped-builtin-v1-namespaces": "namespace-details", "base-factory-clusterscoped-builtin-v1-nodes": "node-details", // Namespaced builtin resources "base-factory-namespaced-builtin-v1-pods": "pod-details", "base-factory-namespaced-builtin-v1-secrets": "kube-secret-details", "base-factory-namespaced-builtin-v1-services": "kube-service-details", // Namespaced API resources "base-factory-namespaced-api-networking.k8s.io-v1-ingresses": "kube-ingress-details", "base-factory-namespaced-api-cozystack.io-v1alpha1-workloadmonitors": "workloadmonitor-details", // Backup resources (not ApplicationDefinitions, so ensureNavigation doesn't cover them) "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-plans": "plan-details", "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-backupjobs": "backupjob-details", "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-backups": "backup-details", "base-factory-namespaced-api-backups.cozystack.io-v1alpha1-restorejobs": "restorejob-details", } return []*dashboardv1alpha1.Navigation{ createNavigation("navigation", map[string]any{ "namespaces": map[string]any{ "change": "/openapi-ui/{selectedCluster}/{value}/factory/marketplace", "clear": "/openapi-ui/{selectedCluster}/api-table/core.cozystack.io/v1alpha1/tenantnamespaces", }, "baseFactoriesMapping": baseFactoriesMapping, }), } } // CreateAllTableUriMappings creates all table URI mapping resources using helper functions func CreateAllTableUriMappings() []*dashboardv1alpha1.TableUriMapping { // links are now handled through CustomFormsPrefills return []*dashboardv1alpha1.TableUriMapping{} } // ---------------- Additional helper functions for missing resource types ---------------- // createCustomFormsOverride creates a CustomFormsOverride resource func createCustomFormsOverride(customizationId string, spec map[string]any) *dashboardv1alpha1.CustomFormsOverride { // Generate name from customizationId name := customizationId if strings.Contains(customizationId, "default-/") { // For default-/ resources, replace "default-/" with "default-" and slashes with dots name = strings.ReplaceAll(customizationId, "default-/", "default-") name = strings.ReplaceAll(name, "/", ".") } // Create hidden fields list hidden := []any{ []any{"metadata", "creationTimestamp"}, } // Add namespace to hidden for specific resources in the correct order if strings.Contains(name, "namespaces") || strings.Contains(name, "nodes") { hidden = append(hidden, []any{"metadata", "namespace"}) } // Add remaining hidden fields hidden = append(hidden, []any{ []any{"metadata", "deletionGracePeriodSeconds"}, []any{"metadata", "deletionTimestamp"}, []any{"metadata", "finalizers"}, []any{"metadata", "generateName"}, []any{"metadata", "generation"}, []any{"metadata", "managedFields"}, []any{"metadata", "ownerReferences"}, []any{"metadata", "resourceVersion"}, []any{"metadata", "selfLink"}, []any{"metadata", "uid"}, []any{"kind"}, []any{"apiVersion"}, []any{"status"}, }...) // Create new spec with all required fields including formItems from the original spec newSpec := map[string]any{ "customizationId": customizationId, "hidden": hidden, "schema": map[string]any{}, "strategy": "merge", } // Merge into newSpec caller-provided fields without: customizationId, hidden, strategy for key, value := range spec { if key != "customizationId" && key != "hidden" && key != "strategy" { newSpec[key] = value } } jsonData, _ := json.Marshal(newSpec) return &dashboardv1alpha1.CustomFormsOverride{ TypeMeta: metav1.TypeMeta{ APIVersion: "dashboard.cozystack.io/v1alpha1", Kind: "CustomFormsOverride", }, ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "", }, Spec: dashboardv1alpha1.ArbitrarySpec{ JSON: v1.JSON{ Raw: jsonData, }, }, } } // createNavigation creates a Navigation resource func createNavigation(name string, spec map[string]any) *dashboardv1alpha1.Navigation { jsonData, _ := json.Marshal(spec) return &dashboardv1alpha1.Navigation{ TypeMeta: metav1.TypeMeta{ APIVersion: "dashboard.cozystack.io/v1alpha1", Kind: "Navigation", }, ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "", }, Spec: dashboardv1alpha1.ArbitrarySpec{ JSON: v1.JSON{ Raw: jsonData, }, }, } } func listInputScemaItemBackupClass() map[string]any { return map[string]any{ "type": "listInput", "customProps": map[string]any{ "valueUri": "/api/clusters/{cluster}/k8s/apis/backups.cozystack.io/v1alpha1/backupclasses", "keysToValue": []any{"metadata", "name"}, "keysToLabel": []any{"metadata", "name"}, }, } } // listInputSchemaItemBackup returns a listInput schema overlay for selecting a Backup // from the current namespace (used by RestoreJob form for spec.backupRef.name). func listInputSchemaItemBackup() map[string]any { return map[string]any{ "type": "listInput", "customProps": map[string]any{ "valueUri": "/api/clusters/{cluster}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{namespace}/backups", "keysToValue": []any{"metadata", "name"}, "keysToLabel": []any{"metadata", "name"}, }, } } // backupClassSchema returns the schema for spec.backupClassName as listInput (BackupJob/Plan). func createSchema(customProps map[string]any) map[string]any { return map[string]any{ "properties": map[string]any{ "spec": map[string]any{ "properties": customProps, }, }, } } // createFormItem creates a form item for CustomFormsOverride func createFormItem(path, label, fieldType string) map[string]any { return map[string]any{ "path": path, "label": label, "type": fieldType, } } // createFormItemWithAPI creates a form item with API endpoint for resource-based selects func createFormItemWithAPI(path, label, fieldType string, apiConfig map[string]any) map[string]any { item := map[string]any{ "path": path, "label": label, "type": fieldType, } // Merge API configuration into the form item for key, value := range apiConfig { item[key] = value } return item } // ---------------- Workloadmonitor specific functions ---------------- // createNamespaceHeader creates a header specifically for namespace with correct colors and text func createNamespaceHeader() map[string]any { badge := map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "NS", "title": "Namespace", "style": map[string]any{ "backgroundColor": "#a25792ff", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "fontWeight": float64(400), "lineHeight": "24px", "minWidth": float64(24), "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, } nameText := parsedText("header-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{ "fontSize": "20px", "lineHeight": "24px", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", }) return map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": float64(6), "style": map[string]any{ "marginBottom": "24px", }, }, "children": []any{ badge, nameText, }, } } // createNodeHeader creates a header specifically for node with correct colors and text func createNodeHeader() map[string]any { badge := map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "N", "title": "nodes", "style": map[string]any{ "backgroundColor": "#8476d1", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "fontWeight": float64(400), "lineHeight": "24px", "minWidth": float64(24), "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, } nameText := parsedText("header-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{ "fontSize": "20px", "lineHeight": "24px", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", }) statusBlock := map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "status-header-block", "vertical": true, "gap": float64(4), }, "children": []any{ map[string]any{ "type": "StatusText", "data": map[string]any{ "id": "node-status", "values": []any{ "{reqsJsonPath[0]['.status.conditions[?(@.status=='True')].reason']['-']}", }, "criteriaSuccess": "equals", "strategySuccess": "every", "valueToCompareSuccess": "KubeletReady", "criteriaError": "equals", "strategyError": "every", "valueToCompareError": []any{ "KernelDeadlock", "ReadonlyFilesystem", "NetworkUnavailable", "MemoryPressure", "DiskPressure", "PIDPressure", }, "successText": "Available", "errorText": "Unavailable", "fallbackText": "Progressing", }, }, }, } return map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": float64(6), "style": map[string]any{ "marginBottom": "24px", }, }, "children": []any{ badge, nameText, statusBlock, }, } } // createPodHeader creates a header specifically for pod with correct colors and text func createPodHeader() map[string]any { badge := map[string]any{ "type": "antdText", "data": map[string]any{ "id": "header-badge", "text": "P", "title": "Pods", "style": map[string]any{ "backgroundColor": "#009596", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": "20px", "fontWeight": float64(400), "lineHeight": "24px", "minWidth": float64(24), "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, } nameText := parsedText("header-pod-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{ "fontSize": "20px", "lineHeight": "24px", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", }) statusBlock := map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "status-header-block", "vertical": true, "gap": float64(4), }, "children": []any{ map[string]any{ "type": "StatusText", "data": map[string]any{ "id": "pod-status", }, }, }, } return map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": float64(6), "style": map[string]any{ "marginBottom": "24px", }, }, "children": []any{ badge, nameText, statusBlock, }, } } // createWorkloadmonitorHeader creates a header specifically for workloadmonitor with correct colors and text func createWorkloadmonitorHeader() map[string]any { badge := map[string]any{ "type": "antdText", "data": map[string]any{ "id": "badge-workloadmonitor", "text": "W", "title": "workloadmonitors", "style": map[string]any{ "backgroundColor": "#c46100", "borderRadius": "20px", "color": "#fff", "display": "inline-block", "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": float64(20), "fontWeight": float64(400), "lineHeight": "24px", "minWidth": float64(24), "padding": "0 9px", "textAlign": "center", "whiteSpace": "nowrap", }, }, } nameText := parsedText("workloadmonitor-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{ "fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif", "fontSize": float64(20), "lineHeight": "24px", }) return map[string]any{ "type": "antdFlex", "data": map[string]any{ "id": "header-row", "align": "center", "gap": float64(6), "style": map[string]any{ "marginBottom": float64(24), }, }, "children": []any{ badge, nameText, }, } } // CreateStaticCFOMapping creates the CFOMapping resource with mappings from static CustomFormsOverrides func CreateStaticCFOMapping() *dashboardv1alpha1.CFOMapping { // Build mappings from static CustomFormsOverrides customFormsOverrides := CreateAllCustomFormsOverrides() mappings := make(map[string]string, len(customFormsOverrides)) for _, cfo := range customFormsOverrides { var spec map[string]any if err := json.Unmarshal(cfo.Spec.JSON.Raw, &spec); err != nil { continue } customizationID, ok := spec["customizationId"].(string) if !ok { continue } // Extract the resource path from customizationId (remove "default-" prefix) resourcePath := strings.TrimPrefix(customizationID, "default-") mappings[resourcePath] = customizationID } return createCFOMapping("cfomapping", mappings) } // createCFOMapping creates a CFOMapping resource func createCFOMapping(name string, mappings map[string]string) *dashboardv1alpha1.CFOMapping { spec := map[string]any{ "mappings": mappings, } jsonData, _ := json.Marshal(spec) return &dashboardv1alpha1.CFOMapping{ TypeMeta: metav1.TypeMeta{ APIVersion: "dashboard.cozystack.io/v1alpha1", Kind: "CFOMapping", }, ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: dashboardv1alpha1.ArbitrarySpec{ JSON: v1.JSON{ Raw: jsonData, }, }, } } // ---------------- Complete resource creation function ---------------- // CreateAllStaticResources creates all static dashboard resources using helper functions func CreateAllStaticResources() []client.Object { var resources []client.Object // Add all breadcrumbs for _, breadcrumb := range CreateAllBreadcrumbs() { resources = append(resources, breadcrumb) } // Add all custom column overrides for _, customColumns := range CreateAllCustomColumnsOverrides() { resources = append(resources, customColumns) } // Add all custom forms overrides for _, customForms := range CreateAllCustomFormsOverrides() { resources = append(resources, customForms) } // Add all factories for _, factory := range CreateAllFactories() { resources = append(resources, factory) } // Add all navigations for _, navigation := range CreateAllNavigations() { resources = append(resources, navigation) } // Add all table URI mappings for _, tableUriMapping := range CreateAllTableUriMappings() { resources = append(resources, tableUriMapping) } // Add CFOMapping resources = append(resources, CreateStaticCFOMapping()) return resources }