Skip to content

refactor(console): unify duplicated ClustersTable components #274

Description

@r2dedios

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions