---
name: dashboard-templates
description: Reference components for generating Kubernetes operator CRD dashboards
---
# Dashboard Template Components
Read this before generating any dashboard component. It explains which template file to use for each component type and how to adapt it to a new operator's CRDs.
## When to Use This Skill
Use this skill when:
- Generating a list view for a CRD kind (use ResourceTable.tsx)
- Generating a detail/inspect view for a single CR (use ResourceInspect.tsx)
- Defining the TypeScript model for a specific CRD kind (use the matching .ts file)
- Adapting any reference component to a new operator's CRD schema
## CRD Model Files (`.ts` files)
These files define the TypeScript types and K8sGroupVersionKind models for each CRD kind. Use them as the starting point when modelling a new CRD's API group, version, kind, and optional spec/status interfaces.
### `Certificate.ts`
**Purpose**: Exports the K8sGroupVersionKind model for cert-manager's Certificate CRD. Used for operator detection and for useK8sWatchResource / useK8sModel.
**Key interfaces**: None (model only).
**Model**:
```ts
export const CertificateModel: K8sGroupVersionKind = {
group: 'cert-manager.io',
version: 'v1',
kind: 'Certificate',
};
```
**Fields to adapt**: Replace `group`, `version`, and `kind` with the target operator's values from `oc api-resources` (APIVERSION and KIND columns).
### `Events.ts`
**Purpose**: Core v1 Event model and a map from resource type (plural) to Kind for event involvedObject lookups; provides `getInvolvedObjectKind(resourceType)` for filtering events by resource.
**Key interfaces**:
```ts
export interface K8sEvent {
apiVersion?: string;
kind?: string;
metadata?: {
name?: string;
namespace?: string;
creationTimestamp?: string;
[key: string]: unknown;
};
type?: string;
reason?: string;
message?: string;
count?: number;
firstTimestamp?: string;
lastTimestamp?: string;
involvedObject?: {
kind?: string;
name?: string;
namespace?: string;
[key: string]: unknown;
};
}
```
**Fields to adapt**: Extend `RESOURCE_TYPE_TO_KIND` with `plural: 'Kind'` for each new resource type so events are filtered by the matching involvedObject kind on the inspect page.
### `ExternalSecret.ts`
**Purpose**: K8sGroupVersionKind models for ExternalSecret and ClusterExternalSecret (external-secrets operator).
**Key interfaces**: None (models only).
**Models**: ExternalSecretModel, ClusterExternalSecretModel — group `external-secrets.io`, version `v1beta1`.
**Fields to adapt**: Replace group/version/kind with target operator's CRD. Use two models when the operator has both namespaced and cluster-scoped variants of the same kind.
### `Issuer.ts`
**Purpose**: K8sGroupVersionKind models for Issuer and ClusterIssuer (cert-manager).
**Key interfaces**: None (models only).
**Models**: IssuerModel, ClusterIssuerModel — group `cert-manager.io`, version `v1`.
**Fields to adapt**: Same as Certificate.ts; use for any operator that exposes namespaced and cluster-scoped kinds.
### `PushSecret.ts`
**Purpose**: K8sGroupVersionKind models for PushSecret and ClusterPushSecret (external-secrets operator).
**Key interfaces**: None (models only).
**Fields to adapt**: Replace group/version/kind with the target operator's API from cluster verification.
### `SecretProviderClass.ts`
**Purpose**: Models for SecretProviderClass and SecretProviderClassPodStatus (CSI secrets store); includes an interface for the pod status subresource used on the inspect page.
**Key interfaces**:
```ts
export interface SecretProviderClassPodStatus {
apiVersion?: string;
kind?: string;
metadata?: {
name?: string;
namespace?: string;
creationTimestamp?: string;
[key: string]: unknown;
};
status?: {
secretProviderClassName?: string;
podName?: string;
mounted?: boolean;
[key: string]: unknown;
};
}
```
**Fields to adapt**: Group/version/kind for both models. If the target operator has a similar “status” or pod-binding resource, add a parallel interface and model.
### `SecretStore.ts`
**Purpose**: K8sGroupVersionKind models for SecretStore and ClusterSecretStore (external-secrets operator).
**Key interfaces**: None (models only).
**Fields to adapt**: Replace group/version/kind with values from `oc api-resources`.
## UI Component Files (`.tsx` files)
### `ResourceTable.tsx`
**Purpose**: Shared table for listing CRs of a given kind. Renders loading (three-dot loader), error Alert, empty EmptyState, or a plain table with thead/tbody. Accepts columns and rows (cells as React nodes).
**Use when**: Displaying a list of CRs of a given kind in a table on the operator dashboard.
**Key patterns**:
- Columns: array of `{ title, width? }`; last column is typically Actions.
- Rows: array of `{ cells: React.ReactNode[] }`; build from `useK8sWatchResource` list; Name cell uses ``, Actions cell uses ResourceTableRowActions (so useDeleteModal is per row).
- Loading: show three-dot loader when `loading` is true; error: show Alert; empty: show EmptyState with titleText and EmptyStateBody; selectedProject used for project-aware empty message.
**Props interface**:
```ts
interface Column {
title: string;
width?: number;
}
interface Row {
cells: React.ReactNode[];
}
interface ResourceTableProps {
columns: Column[];
rows: Row[];
loading?: boolean;
error?: string;
emptyStateTitle?: string;
emptyStateBody?: string;
selectedProject?: string;
'data-test'?: string;
}
```
**How to adapt**:
1. Replace CRD kind and API group/version in the table’s useK8sWatchResource (use the corresponding .ts model).
2. Build columns from the target CRD: Name, Namespace (if namespaced), then additionalPrinterColumns or fallback (Status, Age), then Actions.
3. Build rows from the list: name link (Link to inspect href), namespace, status Label, timestamp, ResourceTableRowActions. Use plugin-prefixed CSS classes (e.g. `console-plugin-template__table`) and PatternFly variables; do not use co-m-* or inline styles in the consuming plugin.
### `ResourceInspect.tsx`
**Purpose**: Shared resource detail (inspect) page: Card + Grid layout with back button, Metadata, Labels, Annotations, Specification, Status, Events (and optional Pod Statuses for SecretProviderClass). Parses URL for resourceType, namespace, name; uses getResourceModel(resourceType) and getPagePath(resourceType); supports optional “Show/Hide sensitive data” for spec/status.
**Use when**: Displaying the full detail view for a single CR instance at `//inspect//[namespace/]`.
**Key patterns**:
- Parse path: find segment after `inspect` for resourceType; then either `namespace` + `name` (namespaced) or `name` only (cluster-scoped).
- getResourceModel(resourceType): switch returning the K8sGroupVersionKind for the resource (from crds/*.ts).
- useK8sWatchResource for the single resource; for Events, use EventModel and fieldSelector by involvedObject name/kind/namespace; getInvolvedObjectKind(resourceType) from Events.ts.
- Render: Metadata (DescriptionList), Labels/Annotations (Cards), Spec/Status (YAML dump with optional sensitive-data toggle), Events table, and optional kind-specific block (e.g. SecretProviderClassPodStatus).
**Props interface**: None (component uses URL and hooks only).
**How to adapt**:
1. Add DISPLAY_NAMES entries for each new plural (plural → display name).
2. Add cases in getResourceModel(resourceType) returning the new kind’s K8sModel.
3. Add case in getPagePath(resourceType) returning the operator page path (e.g. `'/cert-manager'`).
4. Add plural → Kind in Events.ts RESOURCE_TYPE_TO_KIND so events are filtered by the matching involvedObject kind.
5. Do not change the overall Card + Grid layout or back button; extend only the maps and model lookups.
## Shared Patterns
### Data fetching
- **List view**: `useK8sWatchResource({ groupVersionKind, namespace?, isList: true })`; use `loaded` and `loadError` for loading/error; build rows from the list.
- **Detail view**: `useK8sWatchResource({ groupVersionKind, name, namespace?, isList: false })`; same loading/error handling.
- **Operator detection**: `useK8sModel({ group, version, kind })`; returns `[model, inFlight]`; check `if (inFlight) return 'loading'`.
### Status display
- Use PatternFly `