Simple Table
Specialized agent for implementing complete features with both listing and detail pages using the data-table-simple pattern. Creates comprehensive feature structures with:
Core Capabilities
- Listing Page: Full table implementation with CRUD operations, filters, search, and navigation arrows
- Detail Pages: Complete detail page with sidebar navigation, about page, and sub-pages
- Routing: Nested routing structure for listing and detail pages
- Hooks: Data fetching, mutations, and context providers
- Translations: Complete translation setup in all languages (en, ja, de)
- GraphQL Integration: Proper query/mutation setup with generated types
- Navigation: Sidebar integration and breadcrumb navigation
- Type Safety: Full TypeScript implementation with proper schemas
Documentation Reference
For comprehensive implementation patterns, architectural best practices, and detailed guidelines, see the Table Feature Patterns and Detail Page Patterns sections in .github/copilot-instructions.md. This includes:
- Badge system architecture and styling solutions
- Data transformation patterns for GraphQL integration
- Translation architecture and file organization standards
- Schema consolidation best practices
- Column configuration patterns with navigation arrows
- Type safety rules and validation workflows
- Complete file structure standards for both listing and detail pages
- Routing patterns for nested detail pages
- Debugging and validation procedures
Key Architectural Principles
- Complete Feature Structure: Both listing and detail pages with full navigation
- Schema Consolidation: All schema, enums, and table config in single
schema.tsfile - Translation Architecture: Centralized in
src/lib/i18n/translations/with re-export pattern - Badge System: Direct Tailwind classes for reliable styling (not shadcn variants)
- Navigation Arrows: Include clickable names AND navigation arrows for detail page access
- Context Providers: Feature-specific providers for detail page data sharing
- Route-Based Fetching: Detail pages fetch data at route level, handle errors with redirects
Complete Feature Implementation Checklist
1. Full Feature Structure Setup
Create the complete folder structure following applicants/teams patterns:
src/features/[feature]/
├── index.tsx # Main listing component using DataTableSimplePage
├── components/
│ ├── actions/
│ │ ├── [feature]-empty-state.tsx # Empty state component
│ │ ├── [feature]-dialog.tsx # Add/Edit dialog
│ │ └── [feature]-delete-dialog.tsx # Delete confirmation dialog
│ └── list/
│ ├── [feature]-actions.tsx # Action configurations
│ ├── [feature]-columns.tsx # Column definitions with navigation arrows
│ ├── [feature]-primary-buttons.tsx # Primary action buttons
│ ├── [feature]-provider.tsx # Context provider for listing
│ ├── [feature]-table.tsx # Table wrapper component
│ ├── [feature]-table-bulk-actions.tsx # Bulk actions (optional)
│ └── [feature]-table-row-actions.tsx # Row-level actions
├── detail/ # Detail page structure
│ ├── index.tsx # Main detail layout with breadcrumb + sidebar
│ ├── components/
│ │ ├── [feature]-about.tsx # About tab content
│ │ └── [feature]-[subpage].tsx # Additional sub-page contents
│ └── [subpage]/ # Sub-page folders
│ └── index.tsx # Sub-page route components
├── data/
│ ├── schema.ts # ALL schemas, enums, filters, config
│ ├── use-[feature].ts # Data fetching hooks for listing
│ ├── use-[feature]-detail.ts # Data fetching hooks for detail pages
│ └── mutations.ts # CRUD mutation hooks
└── translations/
└── index.ts # Re-export from lib/i18n
2. Schema Configuration (data/schema.ts)
Following applicants exact pattern:
Status Enums with Icons and Badges
export const statuses = [
{
label: '[feature].status.active',
value: GraphQLEnum.ACTIVE,
icon: CheckCircle,
badgeVariant: 'default',
},
// ... more statuses
] as const
Zod Schemas for Forms and Data
const [feature]SummarySchema = z.object({
id: z.string(),
name: z.string(),
status: z.custom<SelectOption>(),
createdAt: z.string(),
// ... other fields
})
const [feature]DetailSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().optional(),
status: z.custom<SelectOption>(),
// ... detailed fields
})
export type [Feature]Summary = z.infer<typeof [feature]SummarySchema>
export type [Feature]Detail = z.infer<typeof [feature]DetailSchema>
Filter and Table Configurations
export const dataTableToolbarFilters: FilterOption[] = [
{
columnId: 'status',
title: '[feature].filters.status',
options: statuses.map(status => ({ label: status.label, value: status.value })),
},
]
export const columnFilters: ColumnFilter[] = [
{ columnId: 'name', searchKey: 'name', type: 'string' },
{ columnId: 'status', searchKey: 'status', type: 'array' },
]
export const [feature]TableConfig = {
id: '[feature]',
defaultSorting: [{ id: 'createdAt', desc: true }],
defaultPageSize: 10,
}
3. Column Definitions with Navigation ([feature]-columns.tsx)
Following applicants exact pattern with navigation arrows:
import { BadgeList } from '@/components/custom/badge-list'
import { NavigationArrow } from '@/components/custom/navigation-arrow'
import { useTableColumns, type ColumnConfig } from '@/components/data-table-simple'
import { type ColumnDef } from '@tanstack/react-table'
import { Link } from '@tanstack/react-router'
import { type [Feature]Summary } from '../data/schema'
import { [Feature]TableRowActions } from './[feature]-table-row-actions'
const [feature]ColumnConfigs = (orgSlug?: string): ColumnConfig[] => [
{
key: 'name',
sortable: true,
cell: ({ row }: any) => {
const item = row.original
return orgSlug ? (
<Link
to={`/o/${orgSlug}/[feature]/${item.id}`}
className="text-primary hover:underline font-medium"
>
{item.name}
</Link>
) : (
<span>{item.name}</span>
)
},
},
{
key: 'status',
sortable: true,
cell: ({ row }: any) => <BadgeList items={[{ label: row.original.status.label, value: row.original.status.value, badgeVariant: row.original.status.badgeVariant, icon: row.original.status.icon }]} />,
filterFn: (row, _id, value) => value.includes(row.original.status.value),
},
{
id: 'actions',
cell: ({ row }: any) => <[Feature]TableRowActions row={row} />,
},
{
id: 'navigate',
header: '',
cell: ({ row }: any) => {
const item = row.original
return orgSlug ? (
<NavigationArrow
to={`/o/${orgSlug}/[feature]/${item.id}`}
titleKey="[feature].navigation.goToDetails"
titleParams={{ name: item.name }}
/>
) : null
},
enableSorting: false,
enableHiding: false,
size: 50,
meta: { className: 'w-12 text-center' } as any,
},
]
export function use[Feature]Columns(orgSlug?: string): ColumnDef<[Feature]Summary>[] {
return useTableColumns('[feature]', [feature]ColumnConfigs(orgSlug))
}
4. Data Fetching Hooks
Following applicants exact pattern:
Listing Hook (data/use-[feature].ts)
import { [Feature]QueryDocument } from "@/graphql/generated/graphql";
export const use[Feature] = () => {
const { data, isLoading, isError } = useGraphQLQueryNew<[Feature]Query, [Feature]Summary[], [Feature]QueryVariables>(
[queryKeys.[feature]()],
[Feature]QueryDocument,
(data) => {
const items = data?.[feature]?.edges?.map(edge => edge?.node) || [];
return items.map(item => ({
id: item.id,
name: item.name,
status: statuses?.find(s => s.value === item.status) || statuses[0],
createdAt: item.createdAt,
// ... transform other fields
}));
}
);
return { data: data || [], isLoading, isError };
};
Detail Hook (data/use-[feature]-detail.ts)
import { [Feature]DetailQueryDocument } from "@/graphql/generated/graphql";
export const use[Feature]Detail = (id: string) => {
const { data, isLoading, isError } = useGraphQLQueryNew<[Feature]DetailQuery, [Feature]Detail, [Feature]DetailQueryVariables>(
[queryKeys.[feature]Detail(id)],
[Feature]DetailQueryDocument,
(data) => {
const item = data?.[feature];
return {
id: item.id,
name: item.name,
description: item.description,
status: statuses?.find(s => s.value === item.status) || statuses[0],
// ... transform detailed fields
};
},
{ id }
);
return { data, isLoading, isError };
};
Mutation Hooks (data/mutations.ts)
export function useCreate[Feature]() {
const { t } = useTranslation();
const { handleServerSuccess, handleServerError } = useServerHandlers();
const { mutateAsync, isPending } = useGraphQLMutationNew<Create[Feature]Mutation, any, Create[Feature]MutationVariables>(
create[Feature]Document,
(data) => data.create[Feature],
{
onSuccess: (data, variables) => {
handleServerSuccess(variables.input, data, t('[feature].create.success'), [
queryKeys.[feature](),
])
},
onError: (error, variables) => handleServerError(error, variables.input, t('[feature].create.error')),
}
);
return { mutateAsync, isPending };
}
// Similar patterns for useUpdate[Feature] and useDelete[Feature]
5. Detail Page Implementation
Following applicants exact pattern:
Main Detail Layout (detail/index.tsx)
import { BreadcrumbBar } from '@/components/custom/breadcrumb-bar'
import { Separator } from '@/components/ui/separator'
import { SidebarNav } from '@/components/layout/sidebar-nav'
import { Outlet } from '@tanstack/react-router'
import { useTranslation } from '@/features/language/hooks/use-translation'
import { getRouteApi } from '@tanstack/react-router'
import { [Feature]Provider } from '../components/list/[feature]-provider'
import { use[Feature]Detail } from '../data/use-[feature]-detail'
import { get[Feature]NavItems } from './nav-items'
const route = getRouteApi('/_authenticated/o/$orgSlug/[feature]/$itemId/')
export function [Feature]Detail() {
const { t } = useTranslation()
const { orgSlug, itemId } = route.useParams()
const { data: item, isLoading, isError } = use[Feature]Detail(itemId)
// Handle loading/error states
if (isLoading) return <div>{t('[feature].loading')}</div>
if (isError || !item) return <div>{t('[feature].error')}</div>
const navItems = get[Feature]NavItems(itemId)
return (
<div className="flex flex-1 flex-col space-y-2 overflow-hidden md:space-y-2 lg:flex-row lg:space-y-0 lg:space-x-12">
<div className="flex w-full flex-col space-y-2 lg:w-2/3">
<BreadcrumbBar
items={[
{ label: t('[feature].title'), href: `/o/${orgSlug}/[feature]` },
{ label: item.name },
]}
/>
<div className="flex items-end justify-between">
<div>
<h2 className="text-2xl font-bold tracking-tight">{item.name}</h2>
<p className="text-muted-foreground">{t('[feature].detail.description')}</p>
</div>
</div>
<Separator className="my-4 lg:my-6" />
<div className="flex flex-1 flex-col space-y-2 overflow-hidden">
<Outlet />
</div>
</div>
<div className="w-full lg:w-1/3">
<div className="sticky top-6">
<SidebarNav items={navItems} />
</div>
</div>
</div>
)
}
About Page (detail/components/[feature]-about.tsx)
import { ContentSection } from '@/components/layout/content-section'
import { BadgeList } from '@/components/custom/badge-list'
import { useTranslation } from '@/features/language/hooks/use-translation'
import { use[Feature]Context } from '../../components/list/[feature]-provider'
export function [Feature]About() {
const { t } = useTranslation()
const { item } = use[Feature]Context()
return (
<ContentSection
title={t('[feature].about.title')}
description={t('[feature].about.description')}
>
<div className="grid gap-6">
<div>
<label className="text-sm font-medium">{t('[feature].columns.name')}</label>
<p className="mt-1">{item.name}</p>
</div>
<div>
<label className="text-sm font-medium">{t('[feature].columns.status')}</label>
<div className="mt-1">
<BadgeList items={[{
label: item.status.label,
value: item.status.value,
badgeVariant: item.status.badgeVariant,
icon: item.status.icon
}]} />
</div>
</div>
{/* Additional fields */}
</div>
</ContentSection>
)
}
6. Routing Structure
Following applicants exact pattern:
Main Listing Route
// src/routes/_authenticated/o/$orgSlug/[feature]/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { [Feature] } from '@/features/[feature]'
export const Route = createFileRoute('/_authenticated/o/$orgSlug/[feature]/')({
component: [Feature],
})
Detail Layout Route
// src/routes/_authenticated/o/$orgSlug/[feature]/$itemId/index.tsx
import { createFileRoute, redirect } from '@tanstack/react-router'
import { [Feature]Detail } from '@/features/[feature]/detail'
import { [Feature]Provider } from '@/features/[feature]/components/list/[feature]-provider'
import { use[Feature]Detail } from '@/features/[feature]/data/use-[feature]-detail'
export const Route = createFileRoute('/_authenticated/o/$orgSlug/[feature]/$itemId/')({
component: [Feature]Detail,
loader: async ({ params }) => {
// Optional: preload data or validate access
},
})
About Sub-route
// src/routes/_authenticated/o/$orgSlug/[feature]/$itemId/about/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { [Feature]About } from '@/features/[feature]/detail/components/[feature]-about'
export const Route = createFileRoute('/_authenticated/o/$orgSlug/[feature]/$itemId/about/')({
component: [Feature]About,
})
7. Complete Translation Setup
Following applicants exact pattern, add to all translation files:
English (src/lib/i18n/translations/en.ts)
[feature]: {
title: '[Feature]',
description: 'Manage your [feature] here.',
loading: 'Loading [feature]...',
error: 'Failed to load [feature].',
searchPlaceholder: 'Filter [feature]...',
empty: {
title: 'No [feature] yet',
description: 'Get started by creating your first [feature].',
},
actions: {
add: 'Add [Feature]',
edit: 'Edit [Feature]',
delete: 'Delete [Feature]',
},
columns: {
name: 'Name',
status: 'Status',
createdAt: 'Created At',
},
filters: {
status: 'Status',
},
status: {
active: 'Active',
inactive: 'Inactive',
},
createDialog: {
title: 'Create [Feature]',
description: 'Add a new [feature] to your workspace.',
},
editDialog: {
title: 'Edit [Feature]',
description: 'Make changes to the [feature] details.',
},
deleteDialog: {
title: 'Delete [Feature]',
description: 'Are you sure you want to delete {name}? This action cannot be undone.',
},
detail: {
description: 'Manage [feature] details and settings.',
},
sidebar: {
about: 'About',
[subpage]: '[Subpage]',
},
about: {
title: 'About',
description: '[Feature] information and details.',
},
[subpage]: {
title: '[Subpage]',
description: '[Feature] [subpage] information.',
},
navigation: {
goToDetails: 'Go to [feature] details',
},
create: {
success: '[Feature] created successfully!',
error: 'Failed to create [feature].',
},
update: {
success: '[Feature] updated successfully!',
error: 'Failed to update [feature].',
},
delete: {
success: '[Feature] deleted successfully!',
error: 'Failed to delete [feature].',
},
},
8. GraphQL Integration
- Add GraphQL types to
schema.graphql - Run
npx graphql-codegento update generated types - Ensure query/mutation names match the hook expectations
9. Navigation Integration
Add to src/components/layout/data/sidebar-data.tsx:
export function get[Feature]NavItems() {
return [
{
title: '[feature].sidebar.title',
href: '/o/$orgSlug/[feature]',
icon: <Icon size={18} />,
},
]
}
10. Query Keys
Add to src/lib/query-keys.ts:
[feature]: () => ['[feature]'] as const,
[feature]Detail: (id: string) => ['[feature]', id] as const,
Configuration Details
Input Requirements
Provide the agent with:
- Feature Name: e.g., "applications"
- GraphQL API Schema: e.g., "createApplication(input: ApplicationInput!): Application! updateApplication(id: ID!, input: ApplicationInput!): Application! deleteApplication(id: ID!): Boolean! applications(workspaceId: ID, first: Int, after: String, last: Int, before: String, sort: [SortInput]): ApplicationConnection"
- Required Fields: e.g., "id|name|status|description|createdAt|updatedAt"
- Status Enums: e.g., "PENDING|APPROVED|REJECTED|CANCELLED"
- Sub-pages: e.g., "about,documents,reviews" (optional)
Output Deliverables
The agent will create:
- ✅ Complete listing page with table, filters, search, and navigation arrows
- ✅ Detail page layout with breadcrumb navigation and sidebar
- ✅ About page and specified sub-pages
- ✅ Full CRUD operations with proper error handling
- ✅ Complete translation setup in all languages
- ✅ Proper routing structure for nested pages
- ✅ Context providers for data sharing
- ✅ GraphQL integration with generated types
- ✅ Navigation integration and query keys
- ✅ Type-safe schemas and data transformation
Validation Checklist
- Listing page displays with proper columns and navigation arrows
- Detail pages accessible via name links and navigation arrows
- Sidebar navigation works between about and sub-pages
- CRUD operations function correctly with proper notifications
- Translations display properly in all languages
- TypeScript compilation passes without errors
- GraphQL queries and mutations work correctly
- Responsive design works on mobile and desktop
- Loading and error states handled properly
- Breadcrumbs and navigation work correctly
This agent ensures complete, production-ready feature implementations following established applicants/teams patterns while avoiding common pitfalls discovered during development. Key architectural principles include proper separation between listing and detail concerns, consolidated schema organization, route-based data fetching, and robust type-safe translation systems.