From klair-legacy
Guides developers through refactoring existing custom tables to use the UnifiedTable and UnifiedTableWithCommentsWrapper components. Provides step-by-step guidance for analysis, planning, implementation, backend integration, and testing. Includes automatic knowledge of UnifiedTable API and best practices.
npx claudepluginhub ai-builder-team/ai-builder-plugin-marketplace --plugin klair-legacyThis skill is limited to using the following tools:
**Goal**: Guide developers through refactoring existing custom tables to use the UnifiedTable and UnifiedTableWithCommentsWrapper components.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Goal: Guide developers through refactoring existing custom tables to use the UnifiedTable and UnifiedTableWithCommentsWrapper components.
This skill helps developers:
Before starting, ensure you have:
klair-client/src/components/tables/UnifiedTable/USAGE.mdStep 1.1: Identify the Target Table
klair-client/src/components/ or klair-client/src/screens/)Step 1.2: Analyze Current Implementation Identify the following:
Step 1.3: Check Backend API
Step 2.1: Determine Data Structure Type Based on analysis, classify the table:
UnifiedTable with data={{ data: [] }}UnifiedTable with data={{ sections: [] }}
level and expandable propertieschildren for nested levels, items for leaf dataStep 2.2: Select Feature Preset Choose the closest preset from:
TABLE_PRESETS.BASIC - Basic table with sorting, pagination, searchTABLE_PRESETS.ACCORDION - Expandable rowsTABLE_PRESETS.COMMENTS - Table with comments functionalityTABLE_PRESETS.CATALOG - Product catalog with hierarchyTABLE_PRESETS.CORPORATE - Corporate hierarchyTABLE_PRESETS.FINANCIAL - Financial data tableOr create custom features object combining specific features.
Step 2.3: Plan Data Adapter Layer If the backend data structure doesn't match UnifiedTable expectations:
level, expandable)Step 2.4: Plan Column Definitions
Map existing columns to ColumnDef format:
{
key: string, // Field name in data
header: string, // Column header text
sticky?: boolean, // First column sticky?
align?: 'left' | 'right' | 'center',
formatType?: 'currency' | 'number' | 'percentage' | 'auto',
currencySymbol?: string,
decimalPlaces?: number,
render?: (value, row) => ReactNode, // Custom renderer
tooltip?: string
}
Step 3.1: Create Data Adapter (if needed) Create a utility function to transform backend data:
// klair-client/src/utils/adapters/[tableName]Adapter.ts
export const adapt[TableName]Data = (backendData: BackendType): TableData<FrontendType> => {
// For simple data
return {
data: backendData.items.map(item => ({
// Map backend fields to frontend fields
id: item.itemId,
name: item.itemName,
// ... other mappings
}))
};
// For hierarchical data
return {
sections: backendData.categories.map(category => ({
id: category.id,
name: category.name,
summary: { /* summary fields */ },
level: 0,
expandable: true,
children: /* map nested children */
}))
};
};
Step 3.2: Define Column Configuration Create column definitions:
const columns: ColumnDef<YourDataType>[] = [
{
key: 'name',
header: 'Name',
sticky: true,
tooltip: 'Item name'
},
{
key: 'revenue',
header: 'Revenue',
formatType: 'currency',
align: 'right',
decimalPlaces: 2
},
// ... other columns
];
Step 3.3: Refactor Component Replace the existing table implementation with UnifiedTable:
For tables WITHOUT comments:
import { UnifiedTable, TABLE_PRESETS } from '@/components/tables/UnifiedTable';
import { adapt[TableName]Data } from '@/utils/adapters/[tableName]Adapter';
const [TableName]Component = () => {
// Fetch data (keep existing data fetching logic)
const { data: backendData, isLoading } = useYourDataHook();
// Adapt data
const tableData = useMemo(() => {
if (!backendData) return { data: [] };
return adapt[TableName]Data(backendData);
}, [backendData]);
// Column definitions
const columns = useMemo(() => [/* your columns */], []);
if (isLoading) return <LoadingSpinner />;
return (
<UnifiedTable
data={tableData}
columns={columns}
features={{
...TABLE_PRESETS.BASIC, // or other preset
// Override specific features if needed
totalRow: true,
export: true,
}}
title="Your Table Title"
description="Optional description"
searchableColumns={['name', 'email']}
pageSize={10}
initialSort={{ key: 'name', direction: 'ascending' }}
totalRowConfig={{
position: 'bottom',
calculate: true,
label: 'Total',
excludeColumns: ['name', 'status']
}}
// ... other props
/>
);
};
For tables WITH comments:
import { UnifiedTableWithCommentsWrapper } from '@/components/tables/UnifiedTable/UnifiedTableWithCommentsWrapper';
import { TABLE_PRESETS } from '@/components/tables/UnifiedTable';
import { adapt[TableName]Data } from '@/utils/adapters/[tableName]Adapter';
const [TableName]Component = () => {
// Fetch data
const { data: backendData, isLoading } = useYourDataHook();
// Adapt data
const tableData = useMemo(() => {
if (!backendData) return { data: [] };
return adapt[TableName]Data(backendData);
}, [backendData]);
// Column definitions
const columns = useMemo(() => [/* your columns */], []);
// Comment handlers
const handleCommentCreate = async (comment: Comment) => {
const response = await fetch('/api/comments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(comment)
});
return response.ok;
};
const handleCommentUpdate = async (commentId: string, updates: Partial<Comment>) => {
const response = await fetch(`/api/comments/${commentId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
});
return response.ok;
};
const handleCommentDelete = async (commentId: string) => {
const response = await fetch(`/api/comments/${commentId}`, {
method: 'DELETE'
});
return response.ok;
};
const handleCommentThreadDelete = async (documentId: string, elementId: string) => {
const response = await fetch(`/api/comments/thread`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ documentId, elementId })
});
return response.ok;
};
if (isLoading) return <LoadingSpinner />;
return (
<UnifiedTableWithCommentsWrapper
data={tableData}
columns={columns}
features={TABLE_PRESETS.COMMENTS}
title="Your Table Title"
sectionType="your-table-type"
commentsIdField="id"
documentId="your-document-id"
onCommentCreate={handleCommentCreate}
onCommentUpdate={handleCommentUpdate}
onCommentDelete={handleCommentDelete}
onCommentThreadDelete={handleCommentThreadDelete}
searchableColumns={['name', 'email']}
pageSize={10}
// ... other props
/>
);
};
Step 3.4: Handle Custom Features If the existing table has custom features not directly supported:
Custom Header Controls:
<UnifiedTable
// ... other props
customHeaderControls={
<div className="flex items-center gap-2">
<button onClick={handleCustomAction}>Custom Action</button>
<select onChange={handleFilter}>
<option>Filter Option</option>
</select>
</div>
}
/>
Custom Cell Renderers:
const columns: ColumnDef<DataType>[] = [
{
key: 'status',
header: 'Status',
render: (value, row) => (
<span className={`badge ${value === 'active' ? 'success' : 'warning'}`}>
{value}
</span>
)
}
];
Step 4.1: Review Backend Requirements Check if the backend needs modifications:
Step 4.2: Create Backend Adapter (if needed) If the backend structure is very different, create a backend endpoint adapter:
# klair-api/routers/[table_name]_adapter.py
from fastapi import APIRouter, Query
from typing import Optional
router = APIRouter()
@router.get("/api/v2/[table-name]")
async def get_table_data(
page: Optional[int] = Query(1),
page_size: Optional[int] = Query(10),
sort_by: Optional[str] = Query(None),
sort_direction: Optional[str] = Query("asc"),
search: Optional[str] = Query(None)
):
# Call existing backend logic
original_data = await get_original_data()
# Transform to UnifiedTable format
adapted_data = {
"data": [
{
"id": item.id,
"name": item.display_name,
# ... map other fields
}
for item in original_data
]
}
return adapted_data
Step 4.3: Update API Service Update the frontend API service to use the new endpoint:
// klair-client/src/services/[tableName]Service.ts
export const fetch[TableName]Data = async (
page?: number,
pageSize?: number,
sortBy?: string,
sortDirection?: string,
search?: string
) => {
const params = new URLSearchParams();
if (page) params.append('page', page.toString());
if (pageSize) params.append('page_size', pageSize.toString());
if (sortBy) params.append('sort_by', sortBy);
if (sortDirection) params.append('sort_direction', sortDirection);
if (search) params.append('search', search);
const response = await fetch(`/api/v2/[table-name]?${params}`);
return response.json();
};
Step 5.1: Functional Testing Test all features:
Step 5.2: Visual Testing Compare with the original table:
Step 5.3: Performance Testing
Step 5.4: Edge Cases Test with:
Step 6.1: Remove Old Code
Step 6.2: Update Documentation
Step 6.3: Code Review Checklist Before submitting:
Use Case: Basic table with flat data, sorting, and pagination.
Solution: Use TABLE_PRESETS.BASIC with minimal configuration.
Use Case: Multi-level nested data with different rendering at each level.
Solution: Use recursiveSubTables to define custom components for different levels.
Use Case: Financial data requiring calculated totals.
Solution: Use TABLE_PRESETS.FINANCIAL with totalRowConfig.
Use Case: Backend returns different field names or structure. Solution: Create a data adapter function to transform the data.
Use Case: Multiple filters that aren't just search.
Solution: Use customHeaderControls and manage filtered data in parent component with useMemo.
Use Case: Need comments functionality on nested/hierarchical tables.
Solution: Use UnifiedTableWithCommentsWrapper with proper commentsIdField. The component handles path-based IDs automatically.
Use Case: Existing table has row actions (edit, delete, etc.).
Solution: Add action column with custom render function:
{
key: 'actions',
header: 'Actions',
render: (_, row) => (
<div className="flex gap-2">
<button onClick={() => handleEdit(row)}>Edit</button>
<button onClick={() => handleDelete(row)}>Delete</button>
</div>
)
}
Check:
{ data: [] } or { sections: [] })Check:
disableSorting set to false?Check:
commentsIdField pointing to the correct field?Check:
level and expandable properties?children and items correctly?Solutions:
pageSizerowHeight: 'compact' for dense dataFor complete API documentation and examples, refer to:
klair-client/src/components/tables/UnifiedTable/USAGE.md - Complete usage guideklair-client/src/components/tables/UnifiedTable/INTERNAL.md - Internal implementation details/components-demo - Live examples and demosgraph TD
A[Start: Identify Table] --> B[Analyze Current Implementation]
B --> C[Plan Data Structure]
C --> D[Create Data Adapter if needed]
D --> E[Define Column Configuration]
E --> F[Refactor Component]
F --> G{Comments Needed?}
G -->|Yes| H[Use UnifiedTableWithCommentsWrapper]
G -->|No| I[Use UnifiedTable]
H --> J[Implement Comment Handlers]
I --> K[Test Implementation]
J --> K
K --> L{Backend Changes Needed?}
L -->|Yes| M[Create Backend Adapter]
L -->|No| N[Final Testing]
M --> N
N --> O[Cleanup & Documentation]
O --> P[Code Review]
P --> Q[Complete]
useMemo for data transformations and column definitionsWhen this skill is invoked:
klair-client/src/components/tables/UnifiedTable/USAGE.mdAlways refer to the USAGE.md file for the most up-to-date API documentation and examples.