Skip to main content

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.ts file
  • 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-codegen to 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.