@smicolon/ai-kit 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -40
- package/dist/index.js +260 -126
- package/package.json +5 -5
- package/.claude-plugin/marketplace.json +0 -369
- package/packs/architect/CHANGELOG.md +0 -17
- package/packs/architect/README.md +0 -58
- package/packs/architect/agents/system-architect.md +0 -768
- package/packs/architect/commands/diagram-create.md +0 -300
- package/packs/better-auth/.mcp.json +0 -14
- package/packs/better-auth/CHANGELOG.md +0 -26
- package/packs/better-auth/README.md +0 -125
- package/packs/better-auth/agents/auth-architect.md +0 -278
- package/packs/better-auth/commands/auth-provider-add.md +0 -265
- package/packs/better-auth/commands/auth-setup.md +0 -298
- package/packs/better-auth/skills/auth-security/SKILL.md +0 -425
- package/packs/better-auth/skills/better-auth-patterns/SKILL.md +0 -455
- package/packs/dev-loop/CHANGELOG.md +0 -69
- package/packs/dev-loop/README.md +0 -155
- package/packs/dev-loop/commands/cancel-dev.md +0 -21
- package/packs/dev-loop/commands/dev-loop.md +0 -72
- package/packs/dev-loop/commands/dev-plan.md +0 -351
- package/packs/dev-loop/hooks/hooks.json +0 -15
- package/packs/dev-loop/hooks/stop-hook.sh +0 -178
- package/packs/dev-loop/scripts/setup-dev-loop.sh +0 -194
- package/packs/dev-loop/skills/tdd-planner/SKILL.md +0 -249
- package/packs/dev-loop/skills/tdd-planner/references/framework-patterns.md +0 -874
- package/packs/dev-loop/skills/tdd-planner/references/good-example.md +0 -260
- package/packs/dev-loop/skills/tdd-planner/references/plan-template.md +0 -275
- package/packs/django/CHANGELOG.md +0 -39
- package/packs/django/README.md +0 -92
- package/packs/django/agents/django-architect.md +0 -182
- package/packs/django/agents/django-builder.md +0 -250
- package/packs/django/agents/django-feature-based.md +0 -420
- package/packs/django/agents/django-reviewer.md +0 -253
- package/packs/django/agents/django-tester.md +0 -230
- package/packs/django/commands/api-endpoint.md +0 -285
- package/packs/django/commands/model-create.md +0 -178
- package/packs/django/commands/test-generate.md +0 -325
- package/packs/django/rules/migrations.md +0 -138
- package/packs/django/rules/models.md +0 -167
- package/packs/django/rules/serializers.md +0 -126
- package/packs/django/rules/services.md +0 -131
- package/packs/django/rules/tests.md +0 -140
- package/packs/django/rules/views.md +0 -102
- package/packs/django/skills/import-convention-enforcer/SKILL.md +0 -226
- package/packs/django/skills/import-convention-enforcer/patterns/django-imports.md +0 -343
- package/packs/django/skills/migration-safety-checker/SKILL.md +0 -375
- package/packs/django/skills/model-entity-validator/SKILL.md +0 -298
- package/packs/django/skills/performance-optimizer/SKILL.md +0 -447
- package/packs/django/skills/red-phase-verifier/SKILL.md +0 -180
- package/packs/django/skills/security-first-validator/SKILL.md +0 -435
- package/packs/django/skills/test-coverage-advisor/SKILL.md +0 -394
- package/packs/django/skills/test-validity-checker/SKILL.md +0 -194
- package/packs/failure-log/CHANGELOG.md +0 -20
- package/packs/failure-log/README.md +0 -168
- package/packs/failure-log/commands/failure-add.md +0 -106
- package/packs/failure-log/commands/failure-list.md +0 -89
- package/packs/failure-log/hooks/hooks.json +0 -16
- package/packs/failure-log/hooks/scripts/inject-failures.sh +0 -64
- package/packs/failure-log/skills/failure-log-manager/SKILL.md +0 -164
- package/packs/flutter/CHANGELOG.md +0 -19
- package/packs/flutter/README.md +0 -170
- package/packs/flutter/agents/flutter-architect.md +0 -166
- package/packs/flutter/agents/flutter-builder.md +0 -303
- package/packs/flutter/agents/release-manager.md +0 -355
- package/packs/flutter/commands/fastlane-setup.md +0 -188
- package/packs/flutter/commands/flutter-build.md +0 -90
- package/packs/flutter/commands/flutter-deploy.md +0 -133
- package/packs/flutter/commands/flutter-test.md +0 -117
- package/packs/flutter/commands/signing-setup.md +0 -209
- package/packs/flutter/hooks/hooks.json +0 -17
- package/packs/flutter/skills/fastlane-knowledge/SKILL.md +0 -193
- package/packs/flutter/skills/flutter-architecture/SKILL.md +0 -127
- package/packs/flutter/skills/store-publishing/SKILL.md +0 -163
- package/packs/hono/CHANGELOG.md +0 -19
- package/packs/hono/README.md +0 -143
- package/packs/hono/agents/hono-architect.md +0 -240
- package/packs/hono/agents/hono-builder.md +0 -285
- package/packs/hono/agents/hono-reviewer.md +0 -279
- package/packs/hono/agents/hono-tester.md +0 -346
- package/packs/hono/commands/middleware-create.md +0 -223
- package/packs/hono/commands/project-init.md +0 -306
- package/packs/hono/commands/route-create.md +0 -153
- package/packs/hono/commands/rpc-client.md +0 -263
- package/packs/hono/skills/cloudflare-bindings/SKILL.md +0 -408
- package/packs/hono/skills/hono-patterns/SKILL.md +0 -309
- package/packs/hono/skills/rpc-typesafe/SKILL.md +0 -388
- package/packs/hono/skills/zod-validation/SKILL.md +0 -332
- package/packs/nestjs/CHANGELOG.md +0 -29
- package/packs/nestjs/README.md +0 -75
- package/packs/nestjs/agents/nestjs-architect.md +0 -402
- package/packs/nestjs/agents/nestjs-builder.md +0 -301
- package/packs/nestjs/agents/nestjs-tester.md +0 -437
- package/packs/nestjs/commands/module-create.md +0 -369
- package/packs/nestjs/rules/controllers.md +0 -92
- package/packs/nestjs/rules/dto.md +0 -124
- package/packs/nestjs/rules/entities.md +0 -102
- package/packs/nestjs/rules/services.md +0 -106
- package/packs/nestjs/skills/barrel-export-manager/SKILL.md +0 -389
- package/packs/nestjs/skills/import-convention-enforcer/SKILL.md +0 -365
- package/packs/nextjs/CHANGELOG.md +0 -36
- package/packs/nextjs/README.md +0 -76
- package/packs/nextjs/agents/frontend-tester.md +0 -680
- package/packs/nextjs/agents/frontend-visual.md +0 -820
- package/packs/nextjs/agents/nextjs-architect.md +0 -331
- package/packs/nextjs/agents/nextjs-modular.md +0 -433
- package/packs/nextjs/commands/component-create.md +0 -398
- package/packs/nextjs/rules/api-routes.md +0 -129
- package/packs/nextjs/rules/components.md +0 -106
- package/packs/nextjs/rules/hooks.md +0 -132
- package/packs/nextjs/skills/accessibility-validator/SKILL.md +0 -445
- package/packs/nextjs/skills/import-convention-enforcer/SKILL.md +0 -399
- package/packs/nextjs/skills/react-form-validator/SKILL.md +0 -569
- package/packs/nuxtjs/CHANGELOG.md +0 -30
- package/packs/nuxtjs/README.md +0 -56
- package/packs/nuxtjs/agents/frontend-tester.md +0 -680
- package/packs/nuxtjs/agents/frontend-visual.md +0 -820
- package/packs/nuxtjs/agents/nuxtjs-architect.md +0 -537
- package/packs/nuxtjs/commands/component-create.md +0 -223
- package/packs/nuxtjs/rules/components.md +0 -101
- package/packs/nuxtjs/rules/composables.md +0 -118
- package/packs/nuxtjs/rules/server-routes.md +0 -127
- package/packs/nuxtjs/skills/accessibility-validator/SKILL.md +0 -183
- package/packs/nuxtjs/skills/import-convention-enforcer/SKILL.md +0 -196
- package/packs/nuxtjs/skills/veevalidate-form-validator/SKILL.md +0 -190
- package/packs/onboard/CHANGELOG.md +0 -22
- package/packs/onboard/README.md +0 -103
- package/packs/onboard/agents/onboard-guide.md +0 -118
- package/packs/onboard/commands/onboard.md +0 -313
- package/packs/onboard/skills/onboard-context-provider/SKILL.md +0 -98
- package/packs/tanstack-router/CHANGELOG.md +0 -30
- package/packs/tanstack-router/README.md +0 -113
- package/packs/tanstack-router/agents/tanstack-architect.md +0 -173
- package/packs/tanstack-router/agents/tanstack-builder.md +0 -360
- package/packs/tanstack-router/agents/tanstack-tester.md +0 -454
- package/packs/tanstack-router/commands/form-create.md +0 -313
- package/packs/tanstack-router/commands/query-create.md +0 -263
- package/packs/tanstack-router/commands/route-create.md +0 -190
- package/packs/tanstack-router/commands/table-create.md +0 -413
- package/packs/tanstack-router/skills/ai-patterns/SKILL.md +0 -370
- package/packs/tanstack-router/skills/db-patterns/SKILL.md +0 -346
- package/packs/tanstack-router/skills/devtools-patterns/SKILL.md +0 -415
- package/packs/tanstack-router/skills/form-patterns/SKILL.md +0 -425
- package/packs/tanstack-router/skills/pacer-patterns/SKILL.md +0 -341
- package/packs/tanstack-router/skills/query-patterns/SKILL.md +0 -359
- package/packs/tanstack-router/skills/router-patterns/SKILL.md +0 -285
- package/packs/tanstack-router/skills/store-patterns/SKILL.md +0 -351
- package/packs/tanstack-router/skills/table-patterns/SKILL.md +0 -531
- package/packs/tanstack-router/skills/tanstack-conventions/SKILL.md +0 -428
- package/packs/tanstack-router/skills/virtual-patterns/SKILL.md +0 -490
- package/packs/worktree/CHANGELOG.md +0 -45
- package/packs/worktree/README.md +0 -219
- package/packs/worktree/commands/wt.md +0 -93
- package/packs/worktree/scripts/wt.sh +0 -957
- package/packs/worktree/skills/worktree-manager/SKILL.md +0 -113
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: route-create
|
|
3
|
-
description: Create TanStack Router routes with proper file structure and conventions
|
|
4
|
-
args:
|
|
5
|
-
- name: path
|
|
6
|
-
description: Route path (e.g., /posts, /posts/$postId, /users/$userId/settings)
|
|
7
|
-
required: false
|
|
8
|
-
- name: type
|
|
9
|
-
description: Route type (page, layout, pathless, index)
|
|
10
|
-
required: false
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Create TanStack Router Route
|
|
14
|
-
|
|
15
|
-
Create a new route following TanStack Router file-based conventions.
|
|
16
|
-
|
|
17
|
-
## Instructions
|
|
18
|
-
|
|
19
|
-
1. **Gather Route Information** (if not provided via args):
|
|
20
|
-
- Ask for the route path (e.g., `/posts/$postId`)
|
|
21
|
-
- Ask for the route type: page, layout, pathless layout, or index
|
|
22
|
-
- Ask if the route needs data loading (loader)
|
|
23
|
-
- Ask if the route needs search params validation
|
|
24
|
-
|
|
25
|
-
2. **Determine File Name**:
|
|
26
|
-
Convert the path to TanStack Router file naming convention:
|
|
27
|
-
- `/posts` → `posts.tsx` (layout) or `posts.index.tsx` (index)
|
|
28
|
-
- `/posts/$postId` → `posts.$postId.tsx`
|
|
29
|
-
- `/posts/$postId/edit` → `posts_.$postId.edit.tsx`
|
|
30
|
-
- Pathless layout → `_auth.tsx` (prefix with `_`)
|
|
31
|
-
|
|
32
|
-
3. **Create Route File** in `src/routes/`:
|
|
33
|
-
|
|
34
|
-
For a **page route with loader**:
|
|
35
|
-
```typescript
|
|
36
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
37
|
-
import { {Feature}QueryOptions } from '@/features/{feature}/queries'
|
|
38
|
-
import { {Component} } from '@/features/{feature}/components'
|
|
39
|
-
|
|
40
|
-
export const Route = createFileRoute('{path}')({
|
|
41
|
-
loader: ({ context: { queryClient }, params }) =>
|
|
42
|
-
queryClient.ensureQueryData({feature}QueryOptions(params.{param})),
|
|
43
|
-
component: {Component}Page,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
function {Component}Page() {
|
|
47
|
-
const data = Route.useLoaderData()
|
|
48
|
-
const params = Route.useParams()
|
|
49
|
-
|
|
50
|
-
return <{Component} data={data} />
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
For a **route with search params**:
|
|
55
|
-
```typescript
|
|
56
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
57
|
-
import { z } from 'zod'
|
|
58
|
-
|
|
59
|
-
const searchSchema = z.object({
|
|
60
|
-
page: z.number().default(1),
|
|
61
|
-
sort: z.enum(['newest', 'oldest']).default('newest'),
|
|
62
|
-
search: z.string().optional(),
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
export const Route = createFileRoute('{path}')({
|
|
66
|
-
validateSearch: searchSchema,
|
|
67
|
-
component: {Component}Page,
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
function {Component}Page() {
|
|
71
|
-
const { page, sort, search } = Route.useSearch()
|
|
72
|
-
const navigate = Route.useNavigate()
|
|
73
|
-
|
|
74
|
-
// Update search params
|
|
75
|
-
const setPage = (newPage: number) => {
|
|
76
|
-
navigate({ search: (prev) => ({ ...prev, page: newPage }) })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return <{Component} page={page} sort={sort} search={search} onPageChange={setPage} />
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
For a **layout route**:
|
|
84
|
-
```typescript
|
|
85
|
-
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
|
86
|
-
|
|
87
|
-
export const Route = createFileRoute('{path}')({
|
|
88
|
-
component: {Feature}Layout,
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
function {Feature}Layout() {
|
|
92
|
-
return (
|
|
93
|
-
<div className="{feature}-layout">
|
|
94
|
-
<nav>{/* Feature navigation */}</nav>
|
|
95
|
-
<main>
|
|
96
|
-
<Outlet />
|
|
97
|
-
</main>
|
|
98
|
-
</div>
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
For a **pathless layout** (e.g., auth guard):
|
|
104
|
-
```typescript
|
|
105
|
-
import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'
|
|
106
|
-
|
|
107
|
-
export const Route = createFileRoute('/_auth')({
|
|
108
|
-
beforeLoad: async ({ context }) => {
|
|
109
|
-
if (!context.user) {
|
|
110
|
-
throw redirect({ to: '/login' })
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
component: AuthLayout,
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
function AuthLayout() {
|
|
117
|
-
return <Outlet />
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
4. **Add Error and Loading States** (if data loading):
|
|
122
|
-
```typescript
|
|
123
|
-
export const Route = createFileRoute('{path}')({
|
|
124
|
-
loader: ...,
|
|
125
|
-
pendingComponent: () => <{Component}Skeleton />,
|
|
126
|
-
errorComponent: ({ error }) => (
|
|
127
|
-
<div className="error">
|
|
128
|
-
<h2>Error loading {feature}</h2>
|
|
129
|
-
<p>{error.message}</p>
|
|
130
|
-
</div>
|
|
131
|
-
),
|
|
132
|
-
component: {Component}Page,
|
|
133
|
-
})
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
5. **Create Query Options** (if needed) in `src/features/{feature}/queries/`:
|
|
137
|
-
```typescript
|
|
138
|
-
import { queryOptions } from '@tanstack/react-query'
|
|
139
|
-
import { queryKeys } from '@/lib/query-keys'
|
|
140
|
-
import { {feature}Api } from '@/features/{feature}/api'
|
|
141
|
-
|
|
142
|
-
export const {feature}QueryOptions = (id: string) =>
|
|
143
|
-
queryOptions({
|
|
144
|
-
queryKey: queryKeys.{feature}.detail(id),
|
|
145
|
-
queryFn: () => {feature}Api.get{Feature}(id),
|
|
146
|
-
staleTime: 5 * 60 * 1000,
|
|
147
|
-
})
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
6. **Update Query Keys** (if new feature) in `src/lib/query-keys.ts`:
|
|
151
|
-
```typescript
|
|
152
|
-
export const queryKeys = {
|
|
153
|
-
// existing keys...
|
|
154
|
-
{feature}: {
|
|
155
|
-
all: () => ['{feature}'] as const,
|
|
156
|
-
lists: () => [...queryKeys.{feature}.all(), 'list'] as const,
|
|
157
|
-
list: (filters: {Feature}Filters) => [...queryKeys.{feature}.lists(), filters] as const,
|
|
158
|
-
details: () => [...queryKeys.{feature}.all(), 'detail'] as const,
|
|
159
|
-
detail: (id: string) => [...queryKeys.{feature}.details(), id] as const,
|
|
160
|
-
},
|
|
161
|
-
} as const
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
7. **Regenerate Route Tree**:
|
|
165
|
-
```bash
|
|
166
|
-
bun run routes:generate
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
## File Naming Quick Reference
|
|
170
|
-
|
|
171
|
-
| URL Path | File Name |
|
|
172
|
-
|----------|-----------|
|
|
173
|
-
| `/` | `index.tsx` |
|
|
174
|
-
| `/about` | `about.tsx` |
|
|
175
|
-
| `/posts` (layout) | `posts.tsx` |
|
|
176
|
-
| `/posts` (page) | `posts.index.tsx` |
|
|
177
|
-
| `/posts/$postId` | `posts.$postId.tsx` |
|
|
178
|
-
| `/posts/$postId/edit` | `posts_.$postId.edit.tsx` |
|
|
179
|
-
| Auth wrapper | `_auth.tsx` |
|
|
180
|
-
| Catch-all | `$.tsx` |
|
|
181
|
-
|
|
182
|
-
## Quality Checklist
|
|
183
|
-
|
|
184
|
-
- [ ] File name follows TanStack Router conventions
|
|
185
|
-
- [ ] Route uses `createFileRoute` with correct path
|
|
186
|
-
- [ ] Loader uses `ensureQueryData` (not direct fetch)
|
|
187
|
-
- [ ] Search params validated with Zod schema
|
|
188
|
-
- [ ] Error and pending components provided for data routes
|
|
189
|
-
- [ ] Component uses `Route.useParams()` and `Route.useLoaderData()`
|
|
190
|
-
- [ ] Route tree regenerated after creation
|
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: table-create
|
|
3
|
-
description: Create TanStack Table components with sorting, filtering, and pagination
|
|
4
|
-
args:
|
|
5
|
-
- name: name
|
|
6
|
-
description: Table name (e.g., PostsTable, UsersTable)
|
|
7
|
-
required: false
|
|
8
|
-
- name: feature
|
|
9
|
-
description: Feature the table belongs to (e.g., posts, users)
|
|
10
|
-
required: false
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Create TanStack Table
|
|
14
|
-
|
|
15
|
-
Create a data table component with TanStack Table, including sorting, filtering, and pagination.
|
|
16
|
-
|
|
17
|
-
## Instructions
|
|
18
|
-
|
|
19
|
-
1. **Gather Information** (if not provided via args):
|
|
20
|
-
- Ask for the table name (e.g., `PostsTable`)
|
|
21
|
-
- Ask for the feature it belongs to
|
|
22
|
-
- Ask what columns to include
|
|
23
|
-
- Ask what features to enable: sorting, filtering, pagination, row selection
|
|
24
|
-
|
|
25
|
-
2. **Create Column Definitions** in `src/features/{feature}/components/{Name}Columns.tsx`:
|
|
26
|
-
```typescript
|
|
27
|
-
import { createColumnHelper } from '@tanstack/react-table'
|
|
28
|
-
import { Link } from '@tanstack/react-router'
|
|
29
|
-
import type { {Feature} } from '../types'
|
|
30
|
-
|
|
31
|
-
const columnHelper = createColumnHelper<{Feature}>()
|
|
32
|
-
|
|
33
|
-
export const {name}Columns = [
|
|
34
|
-
columnHelper.accessor('title', {
|
|
35
|
-
header: ({ column }) => (
|
|
36
|
-
<button
|
|
37
|
-
onClick={() => column.toggleSorting()}
|
|
38
|
-
className="flex items-center gap-1"
|
|
39
|
-
>
|
|
40
|
-
Title
|
|
41
|
-
{column.getIsSorted() === 'asc' && ' ↑'}
|
|
42
|
-
{column.getIsSorted() === 'desc' && ' ↓'}
|
|
43
|
-
</button>
|
|
44
|
-
),
|
|
45
|
-
cell: (info) => (
|
|
46
|
-
<Link
|
|
47
|
-
to="/{feature}/$id"
|
|
48
|
-
params={{ id: info.row.original.id }}
|
|
49
|
-
className="hover:underline"
|
|
50
|
-
>
|
|
51
|
-
{info.getValue()}
|
|
52
|
-
</Link>
|
|
53
|
-
),
|
|
54
|
-
}),
|
|
55
|
-
|
|
56
|
-
columnHelper.accessor('status', {
|
|
57
|
-
header: 'Status',
|
|
58
|
-
cell: (info) => (
|
|
59
|
-
<span className={`badge badge-${info.getValue()}`}>
|
|
60
|
-
{info.getValue()}
|
|
61
|
-
</span>
|
|
62
|
-
),
|
|
63
|
-
filterFn: 'equals',
|
|
64
|
-
}),
|
|
65
|
-
|
|
66
|
-
columnHelper.accessor('createdAt', {
|
|
67
|
-
header: 'Created',
|
|
68
|
-
cell: (info) => new Date(info.getValue()).toLocaleDateString(),
|
|
69
|
-
sortingFn: 'datetime',
|
|
70
|
-
}),
|
|
71
|
-
|
|
72
|
-
columnHelper.display({
|
|
73
|
-
id: 'actions',
|
|
74
|
-
header: 'Actions',
|
|
75
|
-
cell: ({ row }) => (
|
|
76
|
-
<div className="flex gap-2">
|
|
77
|
-
<Link to="/{feature}/$id/edit" params={{ id: row.original.id }}>
|
|
78
|
-
Edit
|
|
79
|
-
</Link>
|
|
80
|
-
<button onClick={() => handleDelete(row.original.id)}>
|
|
81
|
-
Delete
|
|
82
|
-
</button>
|
|
83
|
-
</div>
|
|
84
|
-
),
|
|
85
|
-
}),
|
|
86
|
-
]
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
3. **Create Table Component** in `src/features/{feature}/components/{Name}Table.tsx`:
|
|
90
|
-
|
|
91
|
-
For a **basic table**:
|
|
92
|
-
```typescript
|
|
93
|
-
import {
|
|
94
|
-
useReactTable,
|
|
95
|
-
getCoreRowModel,
|
|
96
|
-
flexRender,
|
|
97
|
-
} from '@tanstack/react-table'
|
|
98
|
-
import { {name}Columns } from './{Name}Columns'
|
|
99
|
-
import type { {Feature} } from '../types'
|
|
100
|
-
|
|
101
|
-
interface {Name}TableProps {
|
|
102
|
-
data: {Feature}[]
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function {Name}Table({ data }: {Name}TableProps) {
|
|
106
|
-
const table = useReactTable({
|
|
107
|
-
data,
|
|
108
|
-
columns: {name}Columns,
|
|
109
|
-
getCoreRowModel: getCoreRowModel(),
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<table className="w-full">
|
|
114
|
-
<thead>
|
|
115
|
-
{table.getHeaderGroups().map((headerGroup) => (
|
|
116
|
-
<tr key={headerGroup.id}>
|
|
117
|
-
{headerGroup.headers.map((header) => (
|
|
118
|
-
<th key={header.id} className="text-left p-2">
|
|
119
|
-
{header.isPlaceholder
|
|
120
|
-
? null
|
|
121
|
-
: flexRender(
|
|
122
|
-
header.column.columnDef.header,
|
|
123
|
-
header.getContext()
|
|
124
|
-
)}
|
|
125
|
-
</th>
|
|
126
|
-
))}
|
|
127
|
-
</tr>
|
|
128
|
-
))}
|
|
129
|
-
</thead>
|
|
130
|
-
<tbody>
|
|
131
|
-
{table.getRowModel().rows.map((row) => (
|
|
132
|
-
<tr key={row.id} className="border-t">
|
|
133
|
-
{row.getVisibleCells().map((cell) => (
|
|
134
|
-
<td key={cell.id} className="p-2">
|
|
135
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
136
|
-
</td>
|
|
137
|
-
))}
|
|
138
|
-
</tr>
|
|
139
|
-
))}
|
|
140
|
-
</tbody>
|
|
141
|
-
</table>
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
For a **full-featured table** with sorting, filtering, pagination:
|
|
147
|
-
```typescript
|
|
148
|
-
import { useState } from 'react'
|
|
149
|
-
import {
|
|
150
|
-
useReactTable,
|
|
151
|
-
getCoreRowModel,
|
|
152
|
-
getSortedRowModel,
|
|
153
|
-
getFilteredRowModel,
|
|
154
|
-
getPaginationRowModel,
|
|
155
|
-
flexRender,
|
|
156
|
-
type SortingState,
|
|
157
|
-
type ColumnFiltersState,
|
|
158
|
-
} from '@tanstack/react-table'
|
|
159
|
-
import { {name}Columns } from './{Name}Columns'
|
|
160
|
-
import type { {Feature} } from '../types'
|
|
161
|
-
|
|
162
|
-
interface {Name}TableProps {
|
|
163
|
-
data: {Feature}[]
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function {Name}Table({ data }: {Name}TableProps) {
|
|
167
|
-
const [sorting, setSorting] = useState<SortingState>([])
|
|
168
|
-
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
|
|
169
|
-
const [globalFilter, setGlobalFilter] = useState('')
|
|
170
|
-
|
|
171
|
-
const table = useReactTable({
|
|
172
|
-
data,
|
|
173
|
-
columns: {name}Columns,
|
|
174
|
-
state: {
|
|
175
|
-
sorting,
|
|
176
|
-
columnFilters,
|
|
177
|
-
globalFilter,
|
|
178
|
-
},
|
|
179
|
-
onSortingChange: setSorting,
|
|
180
|
-
onColumnFiltersChange: setColumnFilters,
|
|
181
|
-
onGlobalFilterChange: setGlobalFilter,
|
|
182
|
-
getCoreRowModel: getCoreRowModel(),
|
|
183
|
-
getSortedRowModel: getSortedRowModel(),
|
|
184
|
-
getFilteredRowModel: getFilteredRowModel(),
|
|
185
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
186
|
-
initialState: {
|
|
187
|
-
pagination: { pageSize: 10 },
|
|
188
|
-
},
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
return (
|
|
192
|
-
<div className="space-y-4">
|
|
193
|
-
{/* Search */}
|
|
194
|
-
<input
|
|
195
|
-
type="search"
|
|
196
|
-
placeholder="Search..."
|
|
197
|
-
value={globalFilter}
|
|
198
|
-
onChange={(e) => setGlobalFilter(e.target.value)}
|
|
199
|
-
className="input"
|
|
200
|
-
/>
|
|
201
|
-
|
|
202
|
-
{/* Column Filters */}
|
|
203
|
-
<div className="flex gap-4">
|
|
204
|
-
<select
|
|
205
|
-
value={(table.getColumn('status')?.getFilterValue() as string) ?? ''}
|
|
206
|
-
onChange={(e) =>
|
|
207
|
-
table.getColumn('status')?.setFilterValue(e.target.value || undefined)
|
|
208
|
-
}
|
|
209
|
-
>
|
|
210
|
-
<option value="">All statuses</option>
|
|
211
|
-
<option value="draft">Draft</option>
|
|
212
|
-
<option value="published">Published</option>
|
|
213
|
-
</select>
|
|
214
|
-
</div>
|
|
215
|
-
|
|
216
|
-
{/* Table */}
|
|
217
|
-
<table className="w-full">
|
|
218
|
-
<thead>
|
|
219
|
-
{table.getHeaderGroups().map((headerGroup) => (
|
|
220
|
-
<tr key={headerGroup.id}>
|
|
221
|
-
{headerGroup.headers.map((header) => (
|
|
222
|
-
<th key={header.id} className="text-left p-2">
|
|
223
|
-
{header.isPlaceholder
|
|
224
|
-
? null
|
|
225
|
-
: flexRender(
|
|
226
|
-
header.column.columnDef.header,
|
|
227
|
-
header.getContext()
|
|
228
|
-
)}
|
|
229
|
-
</th>
|
|
230
|
-
))}
|
|
231
|
-
</tr>
|
|
232
|
-
))}
|
|
233
|
-
</thead>
|
|
234
|
-
<tbody>
|
|
235
|
-
{table.getRowModel().rows.length === 0 ? (
|
|
236
|
-
<tr>
|
|
237
|
-
<td colSpan={table.getAllColumns().length} className="text-center p-4">
|
|
238
|
-
No results found
|
|
239
|
-
</td>
|
|
240
|
-
</tr>
|
|
241
|
-
) : (
|
|
242
|
-
table.getRowModel().rows.map((row) => (
|
|
243
|
-
<tr key={row.id} className="border-t hover:bg-gray-50">
|
|
244
|
-
{row.getVisibleCells().map((cell) => (
|
|
245
|
-
<td key={cell.id} className="p-2">
|
|
246
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
247
|
-
</td>
|
|
248
|
-
))}
|
|
249
|
-
</tr>
|
|
250
|
-
))
|
|
251
|
-
)}
|
|
252
|
-
</tbody>
|
|
253
|
-
</table>
|
|
254
|
-
|
|
255
|
-
{/* Pagination */}
|
|
256
|
-
<div className="flex items-center justify-between">
|
|
257
|
-
<span>
|
|
258
|
-
Showing {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} to{' '}
|
|
259
|
-
{Math.min(
|
|
260
|
-
(table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize,
|
|
261
|
-
table.getFilteredRowModel().rows.length
|
|
262
|
-
)}{' '}
|
|
263
|
-
of {table.getFilteredRowModel().rows.length}
|
|
264
|
-
</span>
|
|
265
|
-
|
|
266
|
-
<div className="flex gap-2">
|
|
267
|
-
<button
|
|
268
|
-
onClick={() => table.previousPage()}
|
|
269
|
-
disabled={!table.getCanPreviousPage()}
|
|
270
|
-
className="btn"
|
|
271
|
-
>
|
|
272
|
-
Previous
|
|
273
|
-
</button>
|
|
274
|
-
<span className="flex items-center">
|
|
275
|
-
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
|
|
276
|
-
</span>
|
|
277
|
-
<button
|
|
278
|
-
onClick={() => table.nextPage()}
|
|
279
|
-
disabled={!table.getCanNextPage()}
|
|
280
|
-
className="btn"
|
|
281
|
-
>
|
|
282
|
-
Next
|
|
283
|
-
</button>
|
|
284
|
-
</div>
|
|
285
|
-
|
|
286
|
-
<select
|
|
287
|
-
value={table.getState().pagination.pageSize}
|
|
288
|
-
onChange={(e) => table.setPageSize(Number(e.target.value))}
|
|
289
|
-
>
|
|
290
|
-
{[10, 20, 50].map((size) => (
|
|
291
|
-
<option key={size} value={size}>
|
|
292
|
-
Show {size}
|
|
293
|
-
</option>
|
|
294
|
-
))}
|
|
295
|
-
</select>
|
|
296
|
-
</div>
|
|
297
|
-
</div>
|
|
298
|
-
)
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
4. **Create Server-Side Pagination Table** (for large datasets):
|
|
303
|
-
```typescript
|
|
304
|
-
import { Route } from '@tanstack/react-router'
|
|
305
|
-
|
|
306
|
-
export function {Name}Table() {
|
|
307
|
-
const { page, pageSize, sort } = Route.useSearch()
|
|
308
|
-
const navigate = Route.useNavigate()
|
|
309
|
-
|
|
310
|
-
const { data, isLoading } = useQuery({
|
|
311
|
-
queryKey: queryKeys.{feature}.list({ page, pageSize, sort }),
|
|
312
|
-
queryFn: () => {feature}Api.get{Feature}s({ page, pageSize, sort }),
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
const table = useReactTable({
|
|
316
|
-
data: data?.items ?? [],
|
|
317
|
-
columns: {name}Columns,
|
|
318
|
-
pageCount: data?.pageCount ?? -1,
|
|
319
|
-
state: {
|
|
320
|
-
pagination: { pageIndex: page - 1, pageSize },
|
|
321
|
-
},
|
|
322
|
-
onPaginationChange: (updater) => {
|
|
323
|
-
const newState =
|
|
324
|
-
typeof updater === 'function'
|
|
325
|
-
? updater({ pageIndex: page - 1, pageSize })
|
|
326
|
-
: updater
|
|
327
|
-
navigate({
|
|
328
|
-
search: (prev) => ({
|
|
329
|
-
...prev,
|
|
330
|
-
page: newState.pageIndex + 1,
|
|
331
|
-
pageSize: newState.pageSize,
|
|
332
|
-
}),
|
|
333
|
-
})
|
|
334
|
-
},
|
|
335
|
-
getCoreRowModel: getCoreRowModel(),
|
|
336
|
-
manualPagination: true,
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
if (isLoading) return <TableSkeleton />
|
|
340
|
-
|
|
341
|
-
return (/* Table JSX */)
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
5. **Add Row Selection** (if needed):
|
|
346
|
-
```typescript
|
|
347
|
-
import { useState } from 'react'
|
|
348
|
-
import type { RowSelectionState } from '@tanstack/react-table'
|
|
349
|
-
|
|
350
|
-
export function {Name}Table({ data, onSelectionChange }: Props) {
|
|
351
|
-
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
|
352
|
-
|
|
353
|
-
// Add selection column
|
|
354
|
-
const columnsWithSelection = [
|
|
355
|
-
columnHelper.display({
|
|
356
|
-
id: 'select',
|
|
357
|
-
header: ({ table }) => (
|
|
358
|
-
<input
|
|
359
|
-
type="checkbox"
|
|
360
|
-
checked={table.getIsAllRowsSelected()}
|
|
361
|
-
onChange={table.getToggleAllRowsSelectedHandler()}
|
|
362
|
-
/>
|
|
363
|
-
),
|
|
364
|
-
cell: ({ row }) => (
|
|
365
|
-
<input
|
|
366
|
-
type="checkbox"
|
|
367
|
-
checked={row.getIsSelected()}
|
|
368
|
-
onChange={row.getToggleSelectedHandler()}
|
|
369
|
-
/>
|
|
370
|
-
),
|
|
371
|
-
}),
|
|
372
|
-
...{name}Columns,
|
|
373
|
-
]
|
|
374
|
-
|
|
375
|
-
const table = useReactTable({
|
|
376
|
-
data,
|
|
377
|
-
columns: columnsWithSelection,
|
|
378
|
-
state: { rowSelection },
|
|
379
|
-
onRowSelectionChange: setRowSelection,
|
|
380
|
-
getCoreRowModel: getCoreRowModel(),
|
|
381
|
-
enableRowSelection: true,
|
|
382
|
-
})
|
|
383
|
-
|
|
384
|
-
// Get selected rows
|
|
385
|
-
const selectedRows = table.getSelectedRowModel().rows.map((r) => r.original)
|
|
386
|
-
|
|
387
|
-
return (
|
|
388
|
-
<div>
|
|
389
|
-
<span>{selectedRows.length} selected</span>
|
|
390
|
-
{/* Table JSX */}
|
|
391
|
-
</div>
|
|
392
|
-
)
|
|
393
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
6. **Update Barrel Exports**:
|
|
397
|
-
```typescript
|
|
398
|
-
// src/features/{feature}/components/index.ts
|
|
399
|
-
export { {Name}Table } from './{Name}Table'
|
|
400
|
-
export { {name}Columns } from './{Name}Columns'
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
## Quality Checklist
|
|
404
|
-
|
|
405
|
-
- [ ] Columns defined outside component or memoized
|
|
406
|
-
- [ ] All cells use `flexRender()`
|
|
407
|
-
- [ ] Sortable columns have toggle handler
|
|
408
|
-
- [ ] Empty state displayed when no data
|
|
409
|
-
- [ ] Pagination shows current range and total
|
|
410
|
-
- [ ] Row selection includes select-all checkbox
|
|
411
|
-
- [ ] Server pagination uses `manualPagination: true`
|
|
412
|
-
- [ ] Loading state shown during data fetch
|
|
413
|
-
- [ ] Actions column has proper key handling
|