create-questpie 2.0.0 → 2.0.2

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.
Files changed (45) hide show
  1. package/README.md +10 -6
  2. package/dist/index.mjs +140 -25
  3. package/package.json +5 -3
  4. package/skills/questpie/AGENTS.md +2664 -0
  5. package/skills/questpie/SKILL.md +181 -0
  6. package/skills/questpie/references/auth.md +121 -0
  7. package/skills/questpie/references/business-logic.md +550 -0
  8. package/skills/questpie/references/codegen-plugin-api.md +382 -0
  9. package/skills/questpie/references/crud-api.md +378 -0
  10. package/skills/questpie/references/data-modeling.md +489 -0
  11. package/skills/questpie/references/extend.md +493 -0
  12. package/skills/questpie/references/field-types.md +386 -0
  13. package/skills/questpie/references/infrastructure-adapters.md +545 -0
  14. package/skills/questpie/references/multi-tenancy.md +364 -0
  15. package/skills/questpie/references/production.md +475 -0
  16. package/skills/questpie/references/query-operators.md +125 -0
  17. package/skills/questpie/references/quickstart.md +549 -0
  18. package/skills/questpie/references/rules.md +327 -0
  19. package/skills/questpie/references/tanstack-query.md +520 -0
  20. package/skills/questpie-admin/AGENTS.md +1442 -0
  21. package/skills/questpie-admin/SKILL.md +410 -0
  22. package/skills/questpie-admin/references/blocks.md +307 -0
  23. package/skills/questpie-admin/references/custom-ui.md +305 -0
  24. package/skills/questpie-admin/references/views.md +433 -0
  25. package/templates/tanstack-start/AGENTS.md +71 -62
  26. package/templates/tanstack-start/CLAUDE.md +26 -23
  27. package/templates/tanstack-start/README.md +32 -20
  28. package/templates/tanstack-start/env.example +1 -1
  29. package/templates/tanstack-start/package.json +20 -6
  30. package/templates/tanstack-start/src/lib/client.ts +2 -2
  31. package/templates/tanstack-start/src/lib/env.ts +1 -1
  32. package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
  33. package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
  34. package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +117 -241
  35. package/templates/tanstack-start/src/questpie/server/.generated/index.ts +129 -81
  36. package/templates/tanstack-start/src/questpie/server/app.ts +1 -1
  37. package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
  38. package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +1 -1
  39. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
  40. package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
  41. package/templates/tanstack-start/src/routes/__root.tsx +0 -2
  42. package/templates/tanstack-start/src/routes/admin.tsx +8 -1
  43. package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
  44. package/templates/tanstack-start/src/vite-env.d.ts +1 -0
  45. package/templates/tanstack-start/vite.config.ts +1 -3
@@ -0,0 +1,305 @@
1
+ ---
2
+ name: questpie-admin/custom-ui
3
+ description: QUESTPIE custom-fields custom-views registries field-registry view-registry component-registry reactive-fields dynamic-options widgets field-renderer cell-renderer
4
+ ---
5
+
6
+ # QUESTPIE Custom UI
7
+
8
+ This skill builds on questpie-admin.
9
+
10
+ Extend the QUESTPIE admin with custom field types, custom view types, custom components, and reactive field behaviors.
11
+
12
+ ## Registries
13
+
14
+ Registries connect server-side schema to client-side rendering. When the admin encounters a field type, it looks up the renderer in the field registry.
15
+
16
+ ```text
17
+ Server: f.text().required()
18
+ |
19
+ Generated: { type: "text", options: {...} }
20
+ |
21
+ Admin Client: fieldRegistry.get("text")
22
+ |
23
+ React: <TextFieldRenderer value={...} onChange={...} />
24
+ ```
25
+
26
+ ### Built-in Field Registry
27
+
28
+ ```
29
+ text -> TextInput
30
+ textarea -> TextareaInput
31
+ richText -> RichTextEditor (TipTap)
32
+ number -> NumberInput
33
+ boolean -> Checkbox / Switch
34
+ date -> DatePicker
35
+ datetime -> DateTimePicker
36
+ select -> SelectDropdown
37
+ relation -> RelationPicker
38
+ upload -> FileUpload
39
+ object -> NestedForm
40
+ array -> RepeatableItems
41
+ blocks -> BlockEditor
42
+ json -> JSONEditor
43
+ ```
44
+
45
+ ### Extending Registries
46
+
47
+ Place files in the admin directory. Codegen discovers them automatically:
48
+
49
+ ```
50
+ questpie/admin/
51
+ fields/
52
+ color.tsx # Custom color field renderer
53
+ currency.tsx # Custom currency field renderer
54
+ views/
55
+ kanban.tsx # Custom kanban list view
56
+ ```
57
+
58
+ These are merged with built-in defaults during codegen and exported in `.generated/client.ts`.
59
+
60
+ ## Custom Fields
61
+
62
+ ### Server-Side Registration
63
+
64
+ Register custom fields through modules:
65
+
66
+ ```ts
67
+ const myModule = module({
68
+ name: "custom-fields",
69
+ fields: {
70
+ color: colorField,
71
+ currency: currencyField,
72
+ phone: phoneField,
73
+ },
74
+ });
75
+ ```
76
+
77
+ Once registered and codegen runs, the field becomes available on the `f` builder:
78
+
79
+ ```ts
80
+ .fields(({ f }) => ({
81
+ brandColor: f.color().default("#000000"),
82
+ price: f.currency({ currency: "USD" }),
83
+ }))
84
+ ```
85
+
86
+ ### Admin Field Renderer
87
+
88
+ Create a React component for the field's edit form:
89
+
90
+ ```tsx title="admin/fields/color.tsx"
91
+ import { Icon } from "@iconify/react";
92
+
93
+ function ColorFieldRenderer({ value, onChange }) {
94
+ return (
95
+ <div className="flex items-center gap-2">
96
+ <input
97
+ type="color"
98
+ value={value || "#000000"}
99
+ onChange={(e) => onChange(e.target.value)}
100
+ className="w-10 h-10 border border-border cursor-pointer"
101
+ />
102
+ <span className="font-mono text-sm text-muted-foreground">
103
+ {value || "#000000"}
104
+ </span>
105
+ </div>
106
+ );
107
+ }
108
+ ```
109
+
110
+ ### Cell Renderer
111
+
112
+ For custom table column rendering, provide a `cell` component alongside the field renderer:
113
+
114
+ ```tsx title="admin/fields/color.tsx"
115
+ // Cell component for list view table
116
+ export function ColorCell({ value }) {
117
+ return (
118
+ <div className="flex items-center gap-2">
119
+ <div
120
+ className="w-4 h-4 border border-border"
121
+ style={{ backgroundColor: value || "transparent" }}
122
+ />
123
+ <span className="text-xs font-mono">{value}</span>
124
+ </div>
125
+ );
126
+ }
127
+ ```
128
+
129
+ ## Custom Views
130
+
131
+ Create view types beyond built-in table and form — kanban boards, calendars, galleries.
132
+
133
+ ### Server-Side Declaration
134
+
135
+ ```ts
136
+ const myModule = module({
137
+ name: "custom-views",
138
+ views: {
139
+ kanban: kanbanViewDefinition,
140
+ calendar: calendarViewDefinition,
141
+ },
142
+ });
143
+ ```
144
+
145
+ ### Usage in Collections
146
+
147
+ ```ts
148
+ .list(({ v }) => v.kanban({
149
+ columns: "status",
150
+ cardTitle: "title",
151
+ }))
152
+ ```
153
+
154
+ ### Client Rendering
155
+
156
+ ```tsx title="admin/views/kanban.tsx"
157
+ function KanbanView({ data, columns, onDrop }) {
158
+ return (
159
+ <div className="flex gap-4">
160
+ {columns.map((col) => (
161
+ <div key={col.id} className="flex-1">
162
+ <h3 className="font-mono text-sm font-semibold mb-2">{col.label}</h3>
163
+ {data
164
+ .filter((item) => item.status === col.id)
165
+ .map((item) => (
166
+ <div
167
+ key={item.id}
168
+ className="border border-border bg-card p-3 mb-2"
169
+ >
170
+ {item.title}
171
+ </div>
172
+ ))}
173
+ </div>
174
+ ))}
175
+ </div>
176
+ );
177
+ }
178
+ ```
179
+
180
+ ## Reactive Field System
181
+
182
+ Fields support reactive behaviors configured in the collection's `.form()` view or on the field definition itself.
183
+
184
+ ### Conditional Visibility
185
+
186
+ ```ts
187
+ {
188
+ field: f.cancellationReason,
189
+ hidden: ({ data }) => data.status !== "cancelled",
190
+ }
191
+ ```
192
+
193
+ ### Read-Only
194
+
195
+ ```ts
196
+ {
197
+ field: f.customerName,
198
+ readOnly: ({ data }) => !!data.customer,
199
+ }
200
+ ```
201
+
202
+ ### Computed Values
203
+
204
+ ```ts
205
+ {
206
+ field: f.slug,
207
+ compute: {
208
+ handler: ({ data }) => {
209
+ if (data.name && !data.slug?.trim()) {
210
+ return slugify(data.name);
211
+ }
212
+ return undefined;
213
+ },
214
+ deps: ({ data }) => [data.name, data.slug],
215
+ debounce: 300,
216
+ },
217
+ }
218
+ ```
219
+
220
+ ### Dynamic Options (Server-Side)
221
+
222
+ For select/relation fields with options that depend on other field values:
223
+
224
+ ```ts
225
+ city: f.relation("cities").admin({
226
+ options: {
227
+ handler: async ({ data, search, ctx }) => {
228
+ const cities = await ctx.db.query.cities.findMany({
229
+ where: { countryId: data.country },
230
+ });
231
+ return {
232
+ options: cities.map((c) => ({ value: c.id, label: c.name })),
233
+ };
234
+ },
235
+ deps: ({ data }) => [data.country],
236
+ },
237
+ }),
238
+ ```
239
+
240
+ The `handler` runs **server-side** with full access to `ctx.db`, `ctx.user`, `ctx.req`. It re-executes when any value in `deps` changes.
241
+
242
+ ## UI Component Reference
243
+
244
+ When building custom admin UI, use these patterns:
245
+
246
+ ### Icons
247
+
248
+ ```tsx
249
+ import { Icon } from "@iconify/react";
250
+
251
+ // Phosphor icon set with ph: prefix
252
+ <Icon icon="ph:house" width={20} height={20} />
253
+ <Icon icon="ph:caret-down-bold" width={16} height={16} /> // bold weight
254
+ <Icon icon="ph:heart-fill" width={16} height={16} /> // fill weight
255
+ ```
256
+
257
+ ### Toasts
258
+
259
+ ```tsx
260
+ import { toast } from "sonner";
261
+
262
+ toast.success("Record saved");
263
+ toast.error("Failed to save");
264
+ ```
265
+
266
+ ### Primitives (base-ui)
267
+
268
+ ```tsx
269
+ // CORRECT — render prop
270
+ <DialogTrigger render={<Button>Open</Button>} />
271
+
272
+ // WRONG — asChild is Radix, not base-ui
273
+ <DialogTrigger asChild><Button>Open</Button></DialogTrigger>
274
+ ```
275
+
276
+ ### Responsive Components
277
+
278
+ - `ResponsivePopover` — Popover on desktop, Drawer on mobile
279
+ - `ResponsiveDialog` — Dialog on desktop, fullscreen Drawer on mobile
280
+ - Hooks: `useIsMobile()`, `useIsDesktop()`, `useMediaQuery()`
281
+
282
+ ## Common Mistakes
283
+
284
+ 1. **HIGH: Not registering custom field in the field registry** — if codegen doesn't discover the field renderer file, the admin will render nothing for that field type. Place it in `questpie/admin/fields/<name>.tsx`.
285
+
286
+ 2. **HIGH: Missing `cell` component for custom fields** — without a cell component, the list view table shows raw values for your custom field instead of a formatted display.
287
+
288
+ 3. **MEDIUM: Reactive field handlers running client-side** — `options.handler`, `compute.handler`, and other reactive handlers run **SERVER-SIDE** with access to `ctx.db`, `ctx.user`. Do not import client-side modules or use browser APIs in them.
289
+
290
+ 4. **MEDIUM: Using `onChange` wrong in field components** — the field renderer receives `onChange` that expects the **value directly**, not a DOM event.
291
+
292
+ ```tsx
293
+ // WRONG
294
+ onChange={(e) => onChange(e)}
295
+ // CORRECT
296
+ onChange={(e) => onChange(e.target.value)}
297
+ // Or for non-DOM values:
298
+ onChange={newValue}
299
+ ```
300
+
301
+ 5. **MEDIUM: Importing from `@radix-ui/*`** — QUESTPIE admin uses `@base-ui/react`. Never import Radix primitives.
302
+
303
+ 6. **MEDIUM: Using `@phosphor-icons/react` or `lucide-react`** — use `@iconify/react` with `ph:` prefix for all icons.
304
+
305
+ 7. **LOW: Not using shadcn components** — prefer `<Button>`, `<Card>`, `<Input>` from the shadcn component library instead of raw HTML elements. The admin has a consistent brutalist design system.