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.
- package/README.md +10 -6
- package/dist/index.mjs +140 -25
- package/package.json +5 -3
- package/skills/questpie/AGENTS.md +2664 -0
- package/skills/questpie/SKILL.md +181 -0
- package/skills/questpie/references/auth.md +121 -0
- package/skills/questpie/references/business-logic.md +550 -0
- package/skills/questpie/references/codegen-plugin-api.md +382 -0
- package/skills/questpie/references/crud-api.md +378 -0
- package/skills/questpie/references/data-modeling.md +489 -0
- package/skills/questpie/references/extend.md +493 -0
- package/skills/questpie/references/field-types.md +386 -0
- package/skills/questpie/references/infrastructure-adapters.md +545 -0
- package/skills/questpie/references/multi-tenancy.md +364 -0
- package/skills/questpie/references/production.md +475 -0
- package/skills/questpie/references/query-operators.md +125 -0
- package/skills/questpie/references/quickstart.md +549 -0
- package/skills/questpie/references/rules.md +327 -0
- package/skills/questpie/references/tanstack-query.md +520 -0
- package/skills/questpie-admin/AGENTS.md +1442 -0
- package/skills/questpie-admin/SKILL.md +410 -0
- package/skills/questpie-admin/references/blocks.md +307 -0
- package/skills/questpie-admin/references/custom-ui.md +305 -0
- package/skills/questpie-admin/references/views.md +433 -0
- package/templates/tanstack-start/AGENTS.md +71 -62
- package/templates/tanstack-start/CLAUDE.md +26 -23
- package/templates/tanstack-start/README.md +32 -20
- package/templates/tanstack-start/env.example +1 -1
- package/templates/tanstack-start/package.json +20 -6
- package/templates/tanstack-start/src/lib/client.ts +2 -2
- package/templates/tanstack-start/src/lib/env.ts +1 -1
- package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
- package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
- package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +117 -241
- package/templates/tanstack-start/src/questpie/server/.generated/index.ts +129 -81
- package/templates/tanstack-start/src/questpie/server/app.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
- package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
- package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
- package/templates/tanstack-start/src/routes/__root.tsx +0 -2
- package/templates/tanstack-start/src/routes/admin.tsx +8 -1
- package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
- package/templates/tanstack-start/src/vite-env.d.ts +1 -0
- package/templates/tanstack-start/vite.config.ts +1 -3
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Field Types Reference
|
|
2
|
+
|
|
3
|
+
Complete configuration patterns for built-in QUESTPIE field types. Fields use fluent builders: pass only constructor arguments to the field factory, then chain `.required()`, `.default()`, `.label()`, `.localized()`, `.inputOptional()`, `.admin()`, and related methods.
|
|
4
|
+
|
|
5
|
+
## Common Fluent Methods (All Fields)
|
|
6
|
+
|
|
7
|
+
| Method | Description |
|
|
8
|
+
| -------------------- | ------------------------------------------- |
|
|
9
|
+
| `.required()` | Field must have a value |
|
|
10
|
+
| `.default(value)` | Default value |
|
|
11
|
+
| `.label(text)` | Display label (supports i18n) |
|
|
12
|
+
| `.description(text)` | Help text |
|
|
13
|
+
| `.localized()` | Per-locale values |
|
|
14
|
+
| `.inputOptional()` | Optional in API input but required in DB |
|
|
15
|
+
| `.admin(config)` | Admin UI rendering hints |
|
|
16
|
+
| `.virtual(sql)` | SQL expression for computed read-only field |
|
|
17
|
+
|
|
18
|
+
## `f.text(options?)`
|
|
19
|
+
|
|
20
|
+
Short strings, titles, slugs. DB type: `varchar` or `text`.
|
|
21
|
+
|
|
22
|
+
| Option | Type | Default | Description |
|
|
23
|
+
| ------------- | ---------------- | ------- | ----------------------------------------- |
|
|
24
|
+
| `maxLength` | `number` | -- | Max string length (uses varchar when set) |
|
|
25
|
+
| `required` | `boolean` | `false` | Required field |
|
|
26
|
+
| `default` | `string` | -- | Default value |
|
|
27
|
+
| `localized` | `boolean` | `false` | Per-locale values |
|
|
28
|
+
| `input` | `"optional"` | -- | Optional in API input |
|
|
29
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
30
|
+
| `description` | `string \| i18n` | -- | Help text |
|
|
31
|
+
| `virtual` | `SQL` | -- | SQL computed value |
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
name: f.text(255).required(),
|
|
35
|
+
slug: f.text(255).required().inputOptional(),
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## `f.textarea(options?)`
|
|
39
|
+
|
|
40
|
+
Long text, descriptions. DB type: `text`. Same options as `f.text()`. Renders as textarea in admin.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
bio: f.textarea().localized(),
|
|
44
|
+
description: f.textarea().label({ en: "Description", sk: "Popis" }),
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## `f.richText(options?)`
|
|
48
|
+
|
|
49
|
+
Rich formatted content stored as HTML. DB type: `text`. Same options as `f.text()`. Renders as rich text editor in admin.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
content: f.richText().localized(),
|
|
53
|
+
body: f.richText().required(),
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## `f.email(options?)`
|
|
57
|
+
|
|
58
|
+
Email addresses with format validation. Same options as `f.text()`.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
email: f.email().required(),
|
|
62
|
+
contactEmail: f.email().label("Contact Email"),
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## `f.url(options?)`
|
|
66
|
+
|
|
67
|
+
URLs with format validation. Same options as `f.text()`.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
website: f.url(),
|
|
71
|
+
profileUrl: f.url().label("Profile URL"),
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## `f.number(options?)`
|
|
75
|
+
|
|
76
|
+
Numeric values. DB type: `integer` or `numeric`.
|
|
77
|
+
|
|
78
|
+
| Option | Type | Default | Description |
|
|
79
|
+
| ------------- | ---------------- | ------- | ------------- |
|
|
80
|
+
| `required` | `boolean` | `false` | Required |
|
|
81
|
+
| `default` | `number` | -- | Default value |
|
|
82
|
+
| `min` | `number` | -- | Minimum value |
|
|
83
|
+
| `max` | `number` | -- | Maximum value |
|
|
84
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
85
|
+
| `description` | `string \| i18n` | -- | Help text |
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
price: f.number().required().min(0),
|
|
89
|
+
duration: f.number().required().label("Duration (minutes)"),
|
|
90
|
+
sortOrder: f.number().default(0),
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## `f.boolean(options?)`
|
|
94
|
+
|
|
95
|
+
Boolean flags. DB type: `boolean`.
|
|
96
|
+
|
|
97
|
+
| Option | Type | Default | Description |
|
|
98
|
+
| ------------- | ---------------- | ------- | ------------- |
|
|
99
|
+
| `required` | `boolean` | `false` | Required |
|
|
100
|
+
| `default` | `boolean` | -- | Default value |
|
|
101
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
102
|
+
| `description` | `string \| i18n` | -- | Help text |
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
isActive: f.boolean().default(true).required(),
|
|
106
|
+
isFeatured: f.boolean().default(false),
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Admin meta hint to render as switch:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
isActive: f.boolean().default(true).admin({ displayAs: "switch" }),
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## `f.date(options?)`
|
|
116
|
+
|
|
117
|
+
Calendar dates. DB type: `date`.
|
|
118
|
+
|
|
119
|
+
| Option | Type | Default | Description |
|
|
120
|
+
| ---------- | ---------------- | ------- | ------------- |
|
|
121
|
+
| `required` | `boolean` | `false` | Required |
|
|
122
|
+
| `default` | `Date \| string` | -- | Default value |
|
|
123
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
publishedAt: f.date(),
|
|
127
|
+
birthDate: f.date().required(),
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## `f.time(options?)`
|
|
131
|
+
|
|
132
|
+
Time of day. DB type: `time`.
|
|
133
|
+
|
|
134
|
+
| Option | Type | Default | Description |
|
|
135
|
+
| ---------- | ---------------- | ------- | ------------- |
|
|
136
|
+
| `required` | `boolean` | `false` | Required |
|
|
137
|
+
| `default` | `string` | -- | Default value |
|
|
138
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
startTime: f.time().label("Start"),
|
|
142
|
+
endTime: f.time().label("End"),
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## `f.datetime(options?)`
|
|
146
|
+
|
|
147
|
+
Date + time. DB type: `timestamp`.
|
|
148
|
+
|
|
149
|
+
| Option | Type | Default | Description |
|
|
150
|
+
| ---------- | ---------------- | ------- | ------------- |
|
|
151
|
+
| `required` | `boolean` | `false` | Required |
|
|
152
|
+
| `default` | `Date \| string` | -- | Default value |
|
|
153
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
scheduledAt: f.datetime().required(),
|
|
157
|
+
expiresAt: f.datetime(),
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## `f.select(options)`
|
|
161
|
+
|
|
162
|
+
Single value from a predefined list. DB type: `varchar`.
|
|
163
|
+
|
|
164
|
+
| Option | Type | Default | Description |
|
|
165
|
+
| ---------- | -------------------------------- | ------- | ---------------------------- |
|
|
166
|
+
| `options` | `string[] \| { value, label }[]` | -- | Available choices (REQUIRED) |
|
|
167
|
+
| `required` | `boolean` | `false` | Required |
|
|
168
|
+
| `default` | `string` | -- | Default value |
|
|
169
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
170
|
+
|
|
171
|
+
Simple string options:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
status: f.select([
|
|
175
|
+
{ value: "draft", label: "Draft" },
|
|
176
|
+
{ value: "published", label: "Published" },
|
|
177
|
+
{ value: "archived", label: "Archived" },
|
|
178
|
+
]).default("draft").required(),
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Options with i18n labels:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
status: f.select([
|
|
185
|
+
{ value: "pending", label: { en: "Pending", sk: "Cakajuce" } },
|
|
186
|
+
{ value: "confirmed", label: { en: "Confirmed", sk: "Potvrdene" } },
|
|
187
|
+
{ value: "completed", label: { en: "Completed", sk: "Dokoncene" } },
|
|
188
|
+
]).required().default("pending"),
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## `f.relation(options)`
|
|
192
|
+
|
|
193
|
+
Reference to another collection.
|
|
194
|
+
|
|
195
|
+
| Option | Type | Default | Description |
|
|
196
|
+
| ------------- | --------------------------------------- | ------- | ----------------------------------------------------------- |
|
|
197
|
+
| `to` | `string` | -- | Target collection name (REQUIRED) |
|
|
198
|
+
| `required` | `boolean` | `false` | Required |
|
|
199
|
+
| `hasMany` | `boolean` | `false` | Has-many relation |
|
|
200
|
+
| `through` | `string` | -- | Junction collection (required with hasMany) |
|
|
201
|
+
| `sourceField` | `string` | -- | FK in junction -> this collection (required with through) |
|
|
202
|
+
| `targetField` | `string` | -- | FK in junction -> target collection (required with through) |
|
|
203
|
+
| `onDelete` | `"cascade" \| "set null" \| "restrict"` | -- | Foreign key behavior |
|
|
204
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
205
|
+
|
|
206
|
+
Belongs-to (single):
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
author: f.relation("users").required(),
|
|
210
|
+
category: f.relation("categories").onDelete("set null"),
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Many-to-many (through junction):
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
tags: f.relation("tags").manyToMany({
|
|
217
|
+
through: "postTags",
|
|
218
|
+
sourceField: "post",
|
|
219
|
+
targetField: "tag",
|
|
220
|
+
}),
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Dynamic options (admin):
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
city: f.relation("cities").admin({
|
|
227
|
+
options: {
|
|
228
|
+
handler: async ({ data, search, ctx }) => {
|
|
229
|
+
const cities = await ctx.db.query.cities.findMany({
|
|
230
|
+
where: { countryId: data.country },
|
|
231
|
+
});
|
|
232
|
+
return { options: cities.map((c) => ({ value: c.id, label: c.name })) };
|
|
233
|
+
},
|
|
234
|
+
deps: ({ data }) => [data.country],
|
|
235
|
+
},
|
|
236
|
+
}),
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## `f.upload(options)`
|
|
240
|
+
|
|
241
|
+
File upload linked to a storage collection.
|
|
242
|
+
|
|
243
|
+
| Option | Type | Default | Description |
|
|
244
|
+
| ----------- | ---------------- | ------- | ----------------------------------------- |
|
|
245
|
+
| `to` | `string` | -- | Upload/storage collection name (REQUIRED) |
|
|
246
|
+
| `mimeTypes` | `string[]` | -- | Allowed MIME types (e.g., `["image/*"]`) |
|
|
247
|
+
| `maxSize` | `number` | -- | Max file size in bytes |
|
|
248
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
avatar: f.upload({ to: "assets", mimeTypes: ["image/*"], maxSize: 5_000_000 }),
|
|
252
|
+
document: f.upload({ to: "assets", mimeTypes: ["application/pdf"] }),
|
|
253
|
+
cover: f.upload({ to: "assets" }),
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## `f.object(options)`
|
|
257
|
+
|
|
258
|
+
Nested structured data stored as JSONB.
|
|
259
|
+
|
|
260
|
+
| Option | Type | Default | Description |
|
|
261
|
+
| --------- | ------------------------ | ------- | ----------------------------------- |
|
|
262
|
+
| `fields` | `Record \| () => Record` | -- | Nested field definitions (REQUIRED) |
|
|
263
|
+
| `default` | `object` | -- | Default value |
|
|
264
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
265
|
+
|
|
266
|
+
Plain object form:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
address: f.object({
|
|
270
|
+
street: f.text().required(),
|
|
271
|
+
city: f.text().required(),
|
|
272
|
+
zip: f.text(),
|
|
273
|
+
}),
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Function form (for helpers/reuse):
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
.fields(({ f }) => {
|
|
280
|
+
const daySchedule = () => ({
|
|
281
|
+
isOpen: f.boolean().default(true),
|
|
282
|
+
start: f.time(),
|
|
283
|
+
end: f.time(),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
workingHours: f.object({
|
|
288
|
+
monday: f.object(daySchedule()),
|
|
289
|
+
tuesday: f.object(daySchedule()),
|
|
290
|
+
}),
|
|
291
|
+
};
|
|
292
|
+
})
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## `.array()`
|
|
296
|
+
|
|
297
|
+
Repeatable items stored as JSONB.
|
|
298
|
+
|
|
299
|
+
| Option | Type | Default | Description |
|
|
300
|
+
| ----------- | ---------------- | ------- | -------------------------- |
|
|
301
|
+
| `of` | `Field` | -- | Item field type (REQUIRED) |
|
|
302
|
+
| `maxItems` | `number` | -- | Maximum number of items |
|
|
303
|
+
| `default` | `any[]` | -- | Default value |
|
|
304
|
+
| `localized` | `boolean` | `false` | Per-locale array |
|
|
305
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
306
|
+
|
|
307
|
+
Array of primitives:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
tags: f.text().array(),
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Array of objects:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
socialLinks: f.object({
|
|
317
|
+
platform: f.select([
|
|
318
|
+
{ value: "instagram", label: "Instagram" },
|
|
319
|
+
{ value: "facebook", label: "Facebook" },
|
|
320
|
+
{ value: "twitter", label: "Twitter" },
|
|
321
|
+
]),
|
|
322
|
+
url: f.url(),
|
|
323
|
+
}).array().maxItems(5),
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Localized array (each locale has its own array):
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
navigation: f.object({
|
|
330
|
+
label: f.text().required(),
|
|
331
|
+
href: f.text().required(),
|
|
332
|
+
isExternal: f.boolean().default(false),
|
|
333
|
+
}).array().localized(),
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Admin meta for ordering:
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
items: f.object({ name: f.text() }).array().admin({ orderable: true, mode: "inline" }),
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## `f.blocks(options?)`
|
|
343
|
+
|
|
344
|
+
Content blocks for page builders. Stored as JSONB.
|
|
345
|
+
|
|
346
|
+
| Option | Type | Default | Description |
|
|
347
|
+
| ----------- | ---------------- | ------- | ----------------- |
|
|
348
|
+
| `localized` | `boolean` | `false` | Per-locale blocks |
|
|
349
|
+
| `label` | `string \| i18n` | -- | Display label |
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
content: f.blocks().localized(),
|
|
353
|
+
pageContent: f.blocks(),
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## `f.json(options?)`
|
|
357
|
+
|
|
358
|
+
Raw JSON data. No schema validation.
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
metadata: f.json(),
|
|
362
|
+
rawConfig: f.json().label("Configuration"),
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Admin Meta Options
|
|
366
|
+
|
|
367
|
+
The `meta.admin` object controls field rendering in the admin panel:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
isActive: f.boolean().default(true).admin({ displayAs: "switch" }),
|
|
371
|
+
slug: f.text().admin({ placeholder: "auto-generated" }),
|
|
372
|
+
socialLinks: f.object({ url: f.url() }).array().admin({ orderable: true, mode: "inline" }),
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Reactive Field Behaviors
|
|
376
|
+
|
|
377
|
+
Fields support reactive behaviors in `meta.admin`:
|
|
378
|
+
|
|
379
|
+
| Behavior | Description |
|
|
380
|
+
| ---------- | ---------------------------------------------------- |
|
|
381
|
+
| `hidden` | `({ data }) => boolean` -- conditionally hide |
|
|
382
|
+
| `readOnly` | `({ data }) => boolean` -- conditionally read-only |
|
|
383
|
+
| `disabled` | `({ data }) => boolean` -- conditionally disable |
|
|
384
|
+
| `compute` | `{ handler, deps, debounce }` -- auto-compute values |
|
|
385
|
+
|
|
386
|
+
All reactive handlers run server-side with access to `ctx.db`, `ctx.user`, `ctx.req`.
|