@smicolon/ai-kit 0.3.2 → 0.4.1
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 +312 -127
- 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,313 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: form-create
|
|
3
|
-
description: Create TanStack Form components with Zod validation
|
|
4
|
-
args:
|
|
5
|
-
- name: name
|
|
6
|
-
description: Form name (e.g., CreatePost, EditUser, Login)
|
|
7
|
-
required: false
|
|
8
|
-
- name: feature
|
|
9
|
-
description: Feature the form belongs to (e.g., posts, users, auth)
|
|
10
|
-
required: false
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Create TanStack Form
|
|
14
|
-
|
|
15
|
-
Create a form component with TanStack Form and Zod validation.
|
|
16
|
-
|
|
17
|
-
## Instructions
|
|
18
|
-
|
|
19
|
-
1. **Gather Information** (if not provided via args):
|
|
20
|
-
- Ask for the form name (e.g., `CreatePostForm`, `EditUserForm`)
|
|
21
|
-
- Ask for the feature it belongs to
|
|
22
|
-
- Ask for the fields the form should have
|
|
23
|
-
- Ask if it should integrate with a mutation hook
|
|
24
|
-
|
|
25
|
-
2. **Create Zod Schema** in `src/features/{feature}/schemas/{name}Schema.ts`:
|
|
26
|
-
```typescript
|
|
27
|
-
import { z } from 'zod'
|
|
28
|
-
|
|
29
|
-
export const {name}Schema = z.object({
|
|
30
|
-
title: z.string().min(3, 'Title must be at least 3 characters'),
|
|
31
|
-
content: z.string().min(10, 'Content must be at least 10 characters'),
|
|
32
|
-
email: z.string().email('Invalid email address'),
|
|
33
|
-
// Add more fields as needed
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
export type {Name}FormData = z.infer<typeof {name}Schema>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
3. **Create Form Component** in `src/features/{feature}/components/{Name}Form.tsx`:
|
|
40
|
-
|
|
41
|
-
For a **create form**:
|
|
42
|
-
```typescript
|
|
43
|
-
import { useForm } from '@tanstack/react-form'
|
|
44
|
-
import { zodValidator } from '@tanstack/zod-form-adapter'
|
|
45
|
-
import { {name}Schema, type {Name}FormData } from '../schemas/{name}Schema'
|
|
46
|
-
import { useCreate{Feature} } from '../hooks'
|
|
47
|
-
import { FormField } from '@/components/ui/FormField'
|
|
48
|
-
|
|
49
|
-
interface {Name}FormProps {
|
|
50
|
-
onSuccess?: () => void
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function {Name}Form({ onSuccess }: {Name}FormProps) {
|
|
54
|
-
const create{Feature} = useCreate{Feature}()
|
|
55
|
-
|
|
56
|
-
const form = useForm({
|
|
57
|
-
defaultValues: {
|
|
58
|
-
title: '',
|
|
59
|
-
content: '',
|
|
60
|
-
// ... default values for all fields
|
|
61
|
-
} satisfies {Name}FormData,
|
|
62
|
-
onSubmit: async ({ value }) => {
|
|
63
|
-
await create{Feature}.mutateAsync(value)
|
|
64
|
-
onSuccess?.()
|
|
65
|
-
},
|
|
66
|
-
validatorAdapter: zodValidator(),
|
|
67
|
-
validators: {
|
|
68
|
-
onChange: {name}Schema,
|
|
69
|
-
},
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<form
|
|
74
|
-
onSubmit={(e) => {
|
|
75
|
-
e.preventDefault()
|
|
76
|
-
form.handleSubmit()
|
|
77
|
-
}}
|
|
78
|
-
className="space-y-4"
|
|
79
|
-
>
|
|
80
|
-
<form.Field
|
|
81
|
-
name="title"
|
|
82
|
-
children={(field) => (
|
|
83
|
-
<div className="form-field">
|
|
84
|
-
<label htmlFor={field.name}>Title</label>
|
|
85
|
-
<input
|
|
86
|
-
id={field.name}
|
|
87
|
-
value={field.state.value}
|
|
88
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
89
|
-
onBlur={field.handleBlur}
|
|
90
|
-
aria-invalid={field.state.meta.errors.length > 0}
|
|
91
|
-
aria-describedby={`${field.name}-error`}
|
|
92
|
-
/>
|
|
93
|
-
{field.state.meta.isTouched && field.state.meta.errors.length > 0 && (
|
|
94
|
-
<span id={`${field.name}-error`} className="error" role="alert">
|
|
95
|
-
{field.state.meta.errors[0]}
|
|
96
|
-
</span>
|
|
97
|
-
)}
|
|
98
|
-
</div>
|
|
99
|
-
)}
|
|
100
|
-
/>
|
|
101
|
-
|
|
102
|
-
<form.Field
|
|
103
|
-
name="content"
|
|
104
|
-
children={(field) => (
|
|
105
|
-
<div className="form-field">
|
|
106
|
-
<label htmlFor={field.name}>Content</label>
|
|
107
|
-
<textarea
|
|
108
|
-
id={field.name}
|
|
109
|
-
value={field.state.value}
|
|
110
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
111
|
-
onBlur={field.handleBlur}
|
|
112
|
-
rows={5}
|
|
113
|
-
aria-invalid={field.state.meta.errors.length > 0}
|
|
114
|
-
/>
|
|
115
|
-
{field.state.meta.isTouched && field.state.meta.errors.length > 0 && (
|
|
116
|
-
<span className="error" role="alert">
|
|
117
|
-
{field.state.meta.errors[0]}
|
|
118
|
-
</span>
|
|
119
|
-
)}
|
|
120
|
-
</div>
|
|
121
|
-
)}
|
|
122
|
-
/>
|
|
123
|
-
|
|
124
|
-
{/* Add more fields as needed */}
|
|
125
|
-
|
|
126
|
-
<form.Subscribe
|
|
127
|
-
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
|
128
|
-
children={([canSubmit, isSubmitting]) => (
|
|
129
|
-
<button
|
|
130
|
-
type="submit"
|
|
131
|
-
disabled={!canSubmit || isSubmitting}
|
|
132
|
-
className="btn btn-primary"
|
|
133
|
-
>
|
|
134
|
-
{isSubmitting ? 'Saving...' : 'Save'}
|
|
135
|
-
</button>
|
|
136
|
-
)}
|
|
137
|
-
/>
|
|
138
|
-
|
|
139
|
-
{create{Feature}.isError && (
|
|
140
|
-
<div className="error" role="alert">
|
|
141
|
-
{create{Feature}.error.message}
|
|
142
|
-
</div>
|
|
143
|
-
)}
|
|
144
|
-
</form>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
For an **edit form** with initial data:
|
|
150
|
-
```typescript
|
|
151
|
-
import { useForm } from '@tanstack/react-form'
|
|
152
|
-
import { zodValidator } from '@tanstack/zod-form-adapter'
|
|
153
|
-
import { {name}Schema, type {Name}FormData } from '../schemas/{name}Schema'
|
|
154
|
-
import { useUpdate{Feature} } from '../hooks'
|
|
155
|
-
import type { {Feature} } from '../types'
|
|
156
|
-
|
|
157
|
-
interface {Name}FormProps {
|
|
158
|
-
{feature}: {Feature}
|
|
159
|
-
onSuccess?: () => void
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function {Name}Form({ {feature}, onSuccess }: {Name}FormProps) {
|
|
163
|
-
const update{Feature} = useUpdate{Feature}()
|
|
164
|
-
|
|
165
|
-
const form = useForm({
|
|
166
|
-
defaultValues: {
|
|
167
|
-
title: {feature}.title,
|
|
168
|
-
content: {feature}.content,
|
|
169
|
-
// ... populate from existing data
|
|
170
|
-
} satisfies {Name}FormData,
|
|
171
|
-
onSubmit: async ({ value }) => {
|
|
172
|
-
await update{Feature}.mutateAsync({ id: {feature}.id, ...value })
|
|
173
|
-
onSuccess?.()
|
|
174
|
-
},
|
|
175
|
-
validatorAdapter: zodValidator(),
|
|
176
|
-
validators: {
|
|
177
|
-
onChange: {name}Schema,
|
|
178
|
-
},
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
return (
|
|
182
|
-
<form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>
|
|
183
|
-
{/* Same field structure as create form */}
|
|
184
|
-
</form>
|
|
185
|
-
)
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
4. **Create Reusable Form Field Component** (if not exists) in `src/components/ui/FormField.tsx`:
|
|
190
|
-
```typescript
|
|
191
|
-
import type { FieldApi } from '@tanstack/react-form'
|
|
192
|
-
|
|
193
|
-
interface FormFieldProps<T> {
|
|
194
|
-
field: FieldApi<any, any, any, any, T>
|
|
195
|
-
label: string
|
|
196
|
-
type?: 'text' | 'email' | 'password' | 'textarea' | 'number'
|
|
197
|
-
placeholder?: string
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function FormField<T extends string | number>({
|
|
201
|
-
field,
|
|
202
|
-
label,
|
|
203
|
-
type = 'text',
|
|
204
|
-
placeholder,
|
|
205
|
-
}: FormFieldProps<T>) {
|
|
206
|
-
const hasError = field.state.meta.isTouched && field.state.meta.errors.length > 0
|
|
207
|
-
|
|
208
|
-
const inputProps = {
|
|
209
|
-
id: field.name,
|
|
210
|
-
name: field.name,
|
|
211
|
-
value: field.state.value,
|
|
212
|
-
onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
213
|
-
const value = type === 'number' ? Number(e.target.value) : e.target.value
|
|
214
|
-
field.handleChange(value as T)
|
|
215
|
-
},
|
|
216
|
-
onBlur: field.handleBlur,
|
|
217
|
-
placeholder,
|
|
218
|
-
'aria-invalid': hasError,
|
|
219
|
-
'aria-describedby': hasError ? `${field.name}-error` : undefined,
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<div className="form-field">
|
|
224
|
-
<label htmlFor={field.name}>{label}</label>
|
|
225
|
-
{type === 'textarea' ? (
|
|
226
|
-
<textarea {...inputProps} rows={5} />
|
|
227
|
-
) : (
|
|
228
|
-
<input type={type} {...inputProps} />
|
|
229
|
-
)}
|
|
230
|
-
{hasError && (
|
|
231
|
-
<span id={`${field.name}-error`} className="error" role="alert">
|
|
232
|
-
{field.state.meta.errors[0]}
|
|
233
|
-
</span>
|
|
234
|
-
)}
|
|
235
|
-
</div>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
5. **Handle Array Fields** (if needed):
|
|
241
|
-
```typescript
|
|
242
|
-
<form.Field
|
|
243
|
-
name="tags"
|
|
244
|
-
mode="array"
|
|
245
|
-
children={(field) => (
|
|
246
|
-
<div className="form-field">
|
|
247
|
-
<label>Tags</label>
|
|
248
|
-
{field.state.value.map((_, index) => (
|
|
249
|
-
<div key={index} className="flex gap-2">
|
|
250
|
-
<form.Field
|
|
251
|
-
name={`tags[${index}]`}
|
|
252
|
-
children={(tagField) => (
|
|
253
|
-
<input
|
|
254
|
-
value={tagField.state.value}
|
|
255
|
-
onChange={(e) => tagField.handleChange(e.target.value)}
|
|
256
|
-
/>
|
|
257
|
-
)}
|
|
258
|
-
/>
|
|
259
|
-
<button type="button" onClick={() => field.removeValue(index)}>
|
|
260
|
-
Remove
|
|
261
|
-
</button>
|
|
262
|
-
</div>
|
|
263
|
-
))}
|
|
264
|
-
<button type="button" onClick={() => field.pushValue('')}>
|
|
265
|
-
Add Tag
|
|
266
|
-
</button>
|
|
267
|
-
</div>
|
|
268
|
-
)}
|
|
269
|
-
/>
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
6. **Add Async Validation** (if needed):
|
|
273
|
-
```typescript
|
|
274
|
-
<form.Field
|
|
275
|
-
name="username"
|
|
276
|
-
validators={{
|
|
277
|
-
onChange: z.string().min(3),
|
|
278
|
-
onChangeAsyncDebounceMs: 500,
|
|
279
|
-
onChangeAsync: async ({ value }) => {
|
|
280
|
-
const exists = await checkUsernameExists(value)
|
|
281
|
-
return exists ? 'Username already taken' : undefined
|
|
282
|
-
},
|
|
283
|
-
}}
|
|
284
|
-
children={(field) => (
|
|
285
|
-
<div>
|
|
286
|
-
<input {...inputProps} />
|
|
287
|
-
{field.state.meta.isValidating && <span>Checking...</span>}
|
|
288
|
-
{/* error display */}
|
|
289
|
-
</div>
|
|
290
|
-
)}
|
|
291
|
-
/>
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
7. **Update Barrel Exports**:
|
|
295
|
-
```typescript
|
|
296
|
-
// src/features/{feature}/components/index.ts
|
|
297
|
-
export { {Name}Form } from './{Name}Form'
|
|
298
|
-
|
|
299
|
-
// src/features/{feature}/schemas/index.ts
|
|
300
|
-
export * from './{name}Schema'
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Quality Checklist
|
|
304
|
-
|
|
305
|
-
- [ ] Zod schema validates all fields
|
|
306
|
-
- [ ] Form uses `zodValidator()` adapter
|
|
307
|
-
- [ ] All fields have proper labels with htmlFor
|
|
308
|
-
- [ ] Error messages use role="alert" for accessibility
|
|
309
|
-
- [ ] aria-invalid and aria-describedby properly set
|
|
310
|
-
- [ ] Submit button disabled during submission
|
|
311
|
-
- [ ] Mutation errors displayed to user
|
|
312
|
-
- [ ] defaultValues use `satisfies` for type checking
|
|
313
|
-
- [ ] Form prevents default submission
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: query-create
|
|
3
|
-
description: Create TanStack Query options, mutations, and hooks with factory pattern
|
|
4
|
-
args:
|
|
5
|
-
- name: feature
|
|
6
|
-
description: Feature name (e.g., posts, users, orders)
|
|
7
|
-
required: false
|
|
8
|
-
- name: type
|
|
9
|
-
description: Query type (query, mutation, infinite)
|
|
10
|
-
required: false
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Create TanStack Query
|
|
14
|
-
|
|
15
|
-
Create query options, mutations, and hooks following the factory pattern.
|
|
16
|
-
|
|
17
|
-
## Instructions
|
|
18
|
-
|
|
19
|
-
1. **Gather Information** (if not provided via args):
|
|
20
|
-
- Ask for the feature name (e.g., `posts`, `users`)
|
|
21
|
-
- Ask for the query type: query, mutation, or infinite query
|
|
22
|
-
- Ask for the operation (e.g., list, detail, create, update, delete)
|
|
23
|
-
|
|
24
|
-
2. **Create Feature Directory Structure** (if new feature):
|
|
25
|
-
```
|
|
26
|
-
src/features/{feature}/
|
|
27
|
-
├── api/
|
|
28
|
-
│ └── {feature}Api.ts
|
|
29
|
-
├── queries/
|
|
30
|
-
│ ├── {feature}Queries.ts
|
|
31
|
-
│ └── index.ts
|
|
32
|
-
├── hooks/
|
|
33
|
-
│ ├── use{Operation}{Feature}.ts
|
|
34
|
-
│ └── index.ts
|
|
35
|
-
├── types.ts
|
|
36
|
-
└── index.ts
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
3. **Create/Update Query Keys** in `src/lib/query-keys.ts`:
|
|
40
|
-
```typescript
|
|
41
|
-
export const queryKeys = {
|
|
42
|
-
{feature}: {
|
|
43
|
-
all: () => ['{feature}'] as const,
|
|
44
|
-
lists: () => [...queryKeys.{feature}.all(), 'list'] as const,
|
|
45
|
-
list: (filters: {Feature}Filters) => [...queryKeys.{feature}.lists(), filters] as const,
|
|
46
|
-
details: () => [...queryKeys.{feature}.all(), 'detail'] as const,
|
|
47
|
-
detail: (id: string) => [...queryKeys.{feature}.details(), id] as const,
|
|
48
|
-
},
|
|
49
|
-
} as const
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
4. **Create API Functions** in `src/features/{feature}/api/{feature}Api.ts`:
|
|
53
|
-
```typescript
|
|
54
|
-
import { apiClient } from '@/lib/api-client'
|
|
55
|
-
import type { {Feature}, Create{Feature}Input, Update{Feature}Input, {Feature}Filters } from '../types'
|
|
56
|
-
|
|
57
|
-
export const {feature}Api = {
|
|
58
|
-
get{Feature}s: async (filters?: {Feature}Filters): Promise<{Feature}[]> => {
|
|
59
|
-
const response = await apiClient.get('/{feature}', { params: filters })
|
|
60
|
-
return response.data
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
get{Feature}: async (id: string): Promise<{Feature}> => {
|
|
64
|
-
const response = await apiClient.get(`/{feature}/${id}`)
|
|
65
|
-
return response.data
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
create{Feature}: async (input: Create{Feature}Input): Promise<{Feature}> => {
|
|
69
|
-
const response = await apiClient.post('/{feature}', input)
|
|
70
|
-
return response.data
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
update{Feature}: async ({ id, ...input }: Update{Feature}Input): Promise<{Feature}> => {
|
|
74
|
-
const response = await apiClient.patch(`/{feature}/${id}`, input)
|
|
75
|
-
return response.data
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
delete{Feature}: async (id: string): Promise<void> => {
|
|
79
|
-
await apiClient.delete(`/{feature}/${id}`)
|
|
80
|
-
},
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
5. **Create Query Options** in `src/features/{feature}/queries/{feature}Queries.ts`:
|
|
85
|
-
|
|
86
|
-
For **list query**:
|
|
87
|
-
```typescript
|
|
88
|
-
import { queryOptions } from '@tanstack/react-query'
|
|
89
|
-
import { queryKeys } from '@/lib/query-keys'
|
|
90
|
-
import { {feature}Api } from '../api/{feature}Api'
|
|
91
|
-
import type { {Feature}Filters } from '../types'
|
|
92
|
-
|
|
93
|
-
export const {feature}sQueryOptions = (filters: {Feature}Filters = {}) =>
|
|
94
|
-
queryOptions({
|
|
95
|
-
queryKey: queryKeys.{feature}.list(filters),
|
|
96
|
-
queryFn: () => {feature}Api.get{Feature}s(filters),
|
|
97
|
-
staleTime: 1 * 60 * 1000, // 1 minute
|
|
98
|
-
})
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
For **detail query**:
|
|
102
|
-
```typescript
|
|
103
|
-
export const {feature}QueryOptions = (id: string) =>
|
|
104
|
-
queryOptions({
|
|
105
|
-
queryKey: queryKeys.{feature}.detail(id),
|
|
106
|
-
queryFn: () => {feature}Api.get{Feature}(id),
|
|
107
|
-
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
108
|
-
})
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
For **infinite query**:
|
|
112
|
-
```typescript
|
|
113
|
-
import { infiniteQueryOptions } from '@tanstack/react-query'
|
|
114
|
-
|
|
115
|
-
export const {feature}sInfiniteQueryOptions = (filters: {Feature}Filters = {}) =>
|
|
116
|
-
infiniteQueryOptions({
|
|
117
|
-
queryKey: queryKeys.{feature}.list({ ...filters, infinite: true }),
|
|
118
|
-
queryFn: ({ pageParam = 1 }) =>
|
|
119
|
-
{feature}Api.get{Feature}s({ ...filters, page: pageParam }),
|
|
120
|
-
getNextPageParam: (lastPage) =>
|
|
121
|
-
lastPage.hasMore ? lastPage.nextPage : undefined,
|
|
122
|
-
initialPageParam: 1,
|
|
123
|
-
})
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
6. **Create Mutation Hooks** in `src/features/{feature}/hooks/`:
|
|
127
|
-
|
|
128
|
-
For **create mutation** (`useCreate{Feature}.ts`):
|
|
129
|
-
```typescript
|
|
130
|
-
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
131
|
-
import { queryKeys } from '@/lib/query-keys'
|
|
132
|
-
import { {feature}Api } from '../api/{feature}Api'
|
|
133
|
-
|
|
134
|
-
export function useCreate{Feature}() {
|
|
135
|
-
const queryClient = useQueryClient()
|
|
136
|
-
|
|
137
|
-
return useMutation({
|
|
138
|
-
mutationFn: {feature}Api.create{Feature},
|
|
139
|
-
onSuccess: () => {
|
|
140
|
-
queryClient.invalidateQueries({ queryKey: queryKeys.{feature}.lists() })
|
|
141
|
-
},
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
For **update mutation** with optimistic update (`useUpdate{Feature}.ts`):
|
|
147
|
-
```typescript
|
|
148
|
-
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
149
|
-
import { queryKeys } from '@/lib/query-keys'
|
|
150
|
-
import { {feature}Api } from '../api/{feature}Api'
|
|
151
|
-
import type { {Feature}, Update{Feature}Input } from '../types'
|
|
152
|
-
|
|
153
|
-
export function useUpdate{Feature}() {
|
|
154
|
-
const queryClient = useQueryClient()
|
|
155
|
-
|
|
156
|
-
return useMutation({
|
|
157
|
-
mutationFn: {feature}Api.update{Feature},
|
|
158
|
-
onMutate: async (newData: Update{Feature}Input) => {
|
|
159
|
-
await queryClient.cancelQueries({
|
|
160
|
-
queryKey: queryKeys.{feature}.detail(newData.id)
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
const previous = queryClient.getQueryData<{Feature}>(
|
|
164
|
-
queryKeys.{feature}.detail(newData.id)
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
queryClient.setQueryData(
|
|
168
|
-
queryKeys.{feature}.detail(newData.id),
|
|
169
|
-
(old: {Feature} | undefined) => old ? { ...old, ...newData } : undefined
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
return { previous }
|
|
173
|
-
},
|
|
174
|
-
onError: (err, newData, context) => {
|
|
175
|
-
if (context?.previous) {
|
|
176
|
-
queryClient.setQueryData(
|
|
177
|
-
queryKeys.{feature}.detail(newData.id),
|
|
178
|
-
context.previous
|
|
179
|
-
)
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
onSettled: (data, error, variables) => {
|
|
183
|
-
queryClient.invalidateQueries({
|
|
184
|
-
queryKey: queryKeys.{feature}.detail(variables.id)
|
|
185
|
-
})
|
|
186
|
-
},
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
For **delete mutation** (`useDelete{Feature}.ts`):
|
|
192
|
-
```typescript
|
|
193
|
-
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
194
|
-
import { queryKeys } from '@/lib/query-keys'
|
|
195
|
-
import { {feature}Api } from '../api/{feature}Api'
|
|
196
|
-
|
|
197
|
-
export function useDelete{Feature}() {
|
|
198
|
-
const queryClient = useQueryClient()
|
|
199
|
-
|
|
200
|
-
return useMutation({
|
|
201
|
-
mutationFn: {feature}Api.delete{Feature},
|
|
202
|
-
onSuccess: () => {
|
|
203
|
-
queryClient.invalidateQueries({ queryKey: queryKeys.{feature}.all() })
|
|
204
|
-
},
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
7. **Create Types** in `src/features/{feature}/types.ts`:
|
|
210
|
-
```typescript
|
|
211
|
-
export interface {Feature} {
|
|
212
|
-
id: string
|
|
213
|
-
// ... feature fields
|
|
214
|
-
createdAt: string
|
|
215
|
-
updatedAt: string
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export interface Create{Feature}Input {
|
|
219
|
-
// ... creation fields
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export interface Update{Feature}Input extends Partial<Create{Feature}Input> {
|
|
223
|
-
id: string
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export interface {Feature}Filters {
|
|
227
|
-
page?: number
|
|
228
|
-
pageSize?: number
|
|
229
|
-
search?: string
|
|
230
|
-
// ... filter fields
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
8. **Create Barrel Exports**:
|
|
235
|
-
|
|
236
|
-
`src/features/{feature}/queries/index.ts`:
|
|
237
|
-
```typescript
|
|
238
|
-
export * from './{feature}Queries'
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
`src/features/{feature}/hooks/index.ts`:
|
|
242
|
-
```typescript
|
|
243
|
-
export * from './useCreate{Feature}'
|
|
244
|
-
export * from './useUpdate{Feature}'
|
|
245
|
-
export * from './useDelete{Feature}'
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
`src/features/{feature}/index.ts`:
|
|
249
|
-
```typescript
|
|
250
|
-
export * from './queries'
|
|
251
|
-
export * from './hooks'
|
|
252
|
-
export * from './types'
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
## Quality Checklist
|
|
256
|
-
|
|
257
|
-
- [ ] Query keys follow factory pattern
|
|
258
|
-
- [ ] Query options use `queryOptions()` helper
|
|
259
|
-
- [ ] Mutations invalidate relevant queries
|
|
260
|
-
- [ ] Optimistic updates include rollback logic
|
|
261
|
-
- [ ] Types are properly defined and exported
|
|
262
|
-
- [ ] Barrel exports are in place
|
|
263
|
-
- [ ] API functions match expected backend endpoints
|