fix: register AppBarClusterBadge, fix CSI label mismatch, improve accessibility and theme support

- Register AppBarClusterBadge via registerAppBarAction (was dead code)
- Add Rook 1.12+ CSI pod labels to CephPodDetailSection alongside legacy labels
- Add sidebar entries for Storage Classes and Volumes pages
- Add role="dialog", aria-modal, aria-labelledby, and Escape key to all detail drawers
- Replace hardcoded hex colors with CSS custom properties for dark/light theme compat
- Remove duplicate parseStorageToBytes from OverviewPage (import from k8s.ts)
- Add endpoints field to CephObjectStoreStatus interface (remove unsafe cast)
- Use ROOK_CEPH_API_GROUP/VERSION constants in API URL construction
- Hoist extractJsonData to module level
- Remove dead extractPoolFromVolumeHandle function
- Fix redundant storageClasses.length guard in OverviewPage
- Fix lint indent warnings
- Update CLAUDE.md and CHANGELOG.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DevContainer User
2026-03-04 12:55:37 +00:00
parent fea6df6719
commit 62c24e3857
14 changed files with 185 additions and 100 deletions
+20 -13
View File
@@ -17,6 +17,8 @@ import {
filterRookCephPVCs,
filterRookCephStorageClasses,
isKubeList,
ROOK_CEPH_API_GROUP,
ROOK_CEPH_API_VERSION,
ROOK_CEPH_NAMESPACE,
ROOK_CSI_CEPHFS_SELECTOR,
ROOK_CSI_RBD_SELECTOR,
@@ -79,6 +81,19 @@ export function useRookCephContext(): RookCephContextValue {
return ctx;
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Unwrap Headlamp KubeObject class instances to their raw `.jsonData`. */
function extractJsonData(items: unknown[]): unknown[] {
return items.map(item =>
item && typeof item === 'object' && 'jsonData' in item
? (item as { jsonData: unknown }).jsonData
: item
);
}
// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------
@@ -118,7 +133,7 @@ export function RookCephDataProvider({ children }: { children: React.ReactNode }
// CephCluster CRDs
try {
const clusterList = await ApiProxy.request(
`/apis/ceph.rook.io/v1/namespaces/${ROOK_CEPH_NAMESPACE}/cephclusters`
`/apis/${ROOK_CEPH_API_GROUP}/${ROOK_CEPH_API_VERSION}/namespaces/${ROOK_CEPH_NAMESPACE}/cephclusters`
);
if (!cancelled && isKubeList(clusterList)) {
setCephClusters(clusterList.items as CephCluster[]);
@@ -130,7 +145,7 @@ export function RookCephDataProvider({ children }: { children: React.ReactNode }
// CephBlockPool CRDs
try {
const poolList = await ApiProxy.request(
`/apis/ceph.rook.io/v1/namespaces/${ROOK_CEPH_NAMESPACE}/cephblockpools`
`/apis/${ROOK_CEPH_API_GROUP}/${ROOK_CEPH_API_VERSION}/namespaces/${ROOK_CEPH_NAMESPACE}/cephblockpools`
);
if (!cancelled && isKubeList(poolList)) {
setBlockPools(poolList.items as CephBlockPool[]);
@@ -142,7 +157,7 @@ export function RookCephDataProvider({ children }: { children: React.ReactNode }
// CephFilesystem CRDs
try {
const fsList = await ApiProxy.request(
`/apis/ceph.rook.io/v1/namespaces/${ROOK_CEPH_NAMESPACE}/cephfilesystems`
`/apis/${ROOK_CEPH_API_GROUP}/${ROOK_CEPH_API_VERSION}/namespaces/${ROOK_CEPH_NAMESPACE}/cephfilesystems`
);
if (!cancelled && isKubeList(fsList)) {
setFilesystems(fsList.items as CephFilesystem[]);
@@ -154,7 +169,7 @@ export function RookCephDataProvider({ children }: { children: React.ReactNode }
// CephObjectStore CRDs
try {
const osList = await ApiProxy.request(
`/apis/ceph.rook.io/v1/namespaces/${ROOK_CEPH_NAMESPACE}/cephobjectstores`
`/apis/${ROOK_CEPH_API_GROUP}/${ROOK_CEPH_API_VERSION}/namespaces/${ROOK_CEPH_NAMESPACE}/cephobjectstores`
);
if (!cancelled && isKubeList(osList)) {
setObjectStores(osList.items as CephObjectStore[]);
@@ -255,15 +270,7 @@ export function RookCephDataProvider({ children }: { children: React.ReactNode }
// Derived / filtered values — memoized to avoid recomputation on every render
// ---------------------------------------------------------------------------
// Headlamp useList() returns KubeObject class instances that store raw
// Kubernetes JSON under `.jsonData`. Extract it so our plain-object helpers
// work correctly.
const extractJsonData = (items: unknown[]): unknown[] =>
items.map(item =>
item && typeof item === 'object' && 'jsonData' in item
? (item as { jsonData: unknown }).jsonData
: item
);
// Uses module-level extractJsonData below
const storageClasses = useMemo(() => {
if (!allStorageClasses) return [];
+6 -8
View File
@@ -209,10 +209,16 @@ export interface CephObjectStoreSpec {
gateway?: { port?: number; securePort?: number; instances?: number };
}
export interface CephObjectStoreEndpoints {
insecure?: string[];
secure?: string[];
}
export interface CephObjectStoreStatus {
phase?: string;
conditions?: CephClusterCondition[];
info?: Record<string, string>;
endpoints?: CephObjectStoreEndpoints;
}
export interface CephObjectStore extends KubeObject {
@@ -463,11 +469,3 @@ export function formatStorageType(type: 'rbd' | 'cephfs' | 'unknown'): string {
return 'Unknown';
}
}
/** Extracts pool/subvolume group name from a Rook-Ceph PV volumeHandle. */
export function extractPoolFromVolumeHandle(handle: string | undefined): string {
if (!handle) return '—';
// RBD format: "<csi-vol-id>-<pool>-..." — pool is in volumeAttributes
// We rely on volumeAttributes.pool instead; this just provides a fallback.
return handle;
}