Problem
Two separate ClustersTable components exist with duplicated table rendering logic:
| File |
Columns |
Data Source |
Features |
console/src/app/AccountDetails/components/ClustersTable.tsx |
5 (ID, Name, Status, Provider, Instance Count) |
clusters prop |
Local sort only |
console/src/app/Clusters/components/ClustersTable.tsx |
8 (ID, Name, Status, Account, Provider, Region, Cost 15d, Console) |
useClusters() hook |
Filter, sort, pagination, loading/empty states |
Both share:
- Same
getSortParams pattern with activeSortIndex / activeSortDirection state
- Same
sortItems() utility call
- Same
<Table> / <Thead> / <Tbody> / <Tr> / <Td> structure
- Same
renderStatusLabel() for status column
- Same
<Link to={/clusters/${id}}> for cluster ID
Proposed Solution
Step 1: Define a column configuration type
interface ColumnConfig<T> {
key: string;
label: string;
sortField?: keyof T; // omit to disable sort on this column
render: (item: T) => React.ReactNode;
}
Step 2: Create a unified ClustersTable with a columns prop
interface UnifiedClustersTableProps {
clusters: ClusterResponseApi[];
columns: ColumnConfig<ClusterResponseApi>[];
loading?: boolean;
emptyState?: React.ReactNode;
pagination?: boolean;
}
Step 3: Define column presets
// Full columns for /clusters page
export const FULL_CLUSTER_COLUMNS: ColumnConfig<ClusterResponseApi>[] = [
{ key: 'id', label: 'ID', sortField: 'clusterId', render: c => <Link ...>{c.clusterId}</Link> },
{ key: 'name', label: 'Name', sortField: 'clusterName', render: c => c.clusterName },
// ... 6 more
];
// Compact columns for account details
export const COMPACT_CLUSTER_COLUMNS = FULL_CLUSTER_COLUMNS.filter(
c => ['id', 'name', 'status', 'provider', 'instanceCount'].includes(c.key)
);
Considerations
The two tables currently differ in how they get data:
- AccountDetails version: receives
clusters as a prop (parent fetches via api.accounts.clustersList)
- Clusters page version: calls
useClusters() internally and handles filtering/pagination
The unified component should receive data as a prop (like the simpler version). The Clusters page would keep its filtering/pagination logic in the parent and pass the paginated slice down. This separates concerns: parent handles data, child handles rendering.
Expected Impact
AccountDetails/ClustersTable.tsx (75 lines) → deleted, replaced by shared component
Clusters/ClustersTable.tsx (181 lines) → split into data logic (stays in parent or hook) + shared table component
- One place to add new columns or change table behavior
Files to Modify
- New:
console/src/app/components/common/ClustersTable.tsx (or keep in Clusters/components/)
- Modify:
console/src/app/Clusters/components/ClustersTable.tsx → refactor to use shared component
- Delete:
console/src/app/AccountDetails/components/ClustersTable.tsx
- Modify:
console/src/app/AccountDetails/components/AccountClusters.tsx → update import
- Modify:
console/src/app/AccountDetails/components/types.ts → remove ClustersTableProps if unused
Problem
Two separate
ClustersTablecomponents exist with duplicated table rendering logic:console/src/app/AccountDetails/components/ClustersTable.tsxclusterspropconsole/src/app/Clusters/components/ClustersTable.tsxuseClusters()hookBoth share:
getSortParamspattern withactiveSortIndex/activeSortDirectionstatesortItems()utility call<Table>/<Thead>/<Tbody>/<Tr>/<Td>structurerenderStatusLabel()for status column<Link to={/clusters/${id}}>for cluster IDProposed Solution
Step 1: Define a column configuration type
Step 2: Create a unified
ClustersTablewith acolumnspropStep 3: Define column presets
Considerations
The two tables currently differ in how they get data:
clustersas a prop (parent fetches viaapi.accounts.clustersList)useClusters()internally and handles filtering/paginationThe unified component should receive data as a prop (like the simpler version). The Clusters page would keep its filtering/pagination logic in the parent and pass the paginated slice down. This separates concerns: parent handles data, child handles rendering.
Expected Impact
AccountDetails/ClustersTable.tsx(75 lines) → deleted, replaced by shared componentClusters/ClustersTable.tsx(181 lines) → split into data logic (stays in parent or hook) + shared table componentFiles to Modify
console/src/app/components/common/ClustersTable.tsx(or keep inClusters/components/)console/src/app/Clusters/components/ClustersTable.tsx→ refactor to use shared componentconsole/src/app/AccountDetails/components/ClustersTable.tsxconsole/src/app/AccountDetails/components/AccountClusters.tsx→ update importconsole/src/app/AccountDetails/components/types.ts→ removeClustersTablePropsif unused