@questpie/admin 3.0.0 → 3.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 (35) hide show
  1. package/README.md +76 -43
  2. package/dist/client/blocks/block-renderer.d.mts +2 -2
  3. package/dist/client/preview/block-scope-context.d.mts +2 -2
  4. package/dist/client/preview/preview-banner.d.mts +2 -2
  5. package/dist/client/preview/preview-field.d.mts +4 -4
  6. package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
  7. package/dist/client/views/pages/invite-page.d.mts +2 -2
  8. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  9. package/dist/server/modules/admin/block/prefetch.d.mts +4 -0
  10. package/dist/server/modules/admin/collections/account.d.mts +23 -23
  11. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  12. package/dist/server/modules/admin/collections/admin-preferences.d.mts +35 -35
  13. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  14. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  15. package/dist/server/modules/admin/collections/assets.d.mts +20 -20
  16. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  17. package/dist/server/modules/admin/collections/user.d.mts +28 -28
  18. package/dist/server/modules/admin/collections/verification.d.mts +36 -36
  19. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  20. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  21. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  22. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  23. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  24. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  25. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  26. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  27. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +23 -23
  28. package/dist/server/modules/audit/.generated/module.d.mts +6 -6
  29. package/dist/server/modules/audit/collections/audit-log.d.mts +37 -37
  30. package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
  31. package/package.json +4 -3
  32. package/skills/questpie-admin/SKILL.md +0 -397
  33. package/skills/questpie-admin/blocks/SKILL.md +0 -305
  34. package/skills/questpie-admin/custom-ui/SKILL.md +0 -307
  35. package/skills/questpie-admin/views/SKILL.md +0 -442
@@ -1,442 +0,0 @@
1
- ---
2
- name: questpie-admin/views
3
- description: QUESTPIE admin views list-view table form-view sections sidebar dashboard widgets filters bulk-actions visibility history versioning sorting search
4
- type: skill
5
- ---
6
-
7
- # QUESTPIE Admin Views
8
-
9
- This skill builds on questpie-admin.
10
-
11
- Views control how data appears in the QUESTPIE admin panel. They are configured **server-side** on collections and globals, then rendered by the admin client via registries.
12
-
13
- ```text
14
- Server Config Admin UI
15
- .list(({ v }) => v.collectionTable({})) -> Table with columns, sort, search
16
- .form(({ v, f }) => v.collectionForm({})) -> Form with sections, sidebar, tabs
17
- sidebar({...}) -> Navigation sidebar
18
- dashboard({...}) -> Dashboard with widgets
19
- ```
20
-
21
- ## List Views
22
-
23
- Configure table views with `.list()` on a collection.
24
-
25
- ### Basic Table
26
-
27
- ```ts
28
- .list(({ v }) => v.collectionTable({}))
29
- ```
30
-
31
- Shows all fields as columns with default rendering.
32
-
33
- ### Custom Columns and Search
34
-
35
- ```ts
36
- .list(({ v, f }) =>
37
- v.collectionTable({
38
- columns: [f.name, f.email, f.isActive, f.createdAt],
39
- searchableFields: [f.name, f.email],
40
- defaultSort: { field: f.createdAt, direction: "desc" },
41
- }),
42
- )
43
- ```
44
-
45
- | Option | Type | Description |
46
- | ------------------ | ---------------------- | ------------------------------ |
47
- | `columns` | `Field[]` | Fields to show as columns |
48
- | `searchableFields` | `Field[]` | Fields included in text search |
49
- | `defaultSort` | `{ field, direction }` | Default sort order |
50
-
51
- ## Form Views
52
-
53
- Configure edit forms with `.form()`.
54
-
55
- ### Basic Form
56
-
57
- ```ts
58
- .form(({ v, f }) => v.collectionForm({}))
59
- ```
60
-
61
- Renders all fields in a single column.
62
-
63
- ### Sections
64
-
65
- Group fields into labeled sections with optional grid layout:
66
-
67
- ```ts
68
- .form(({ v, f }) =>
69
- v.collectionForm({
70
- fields: [
71
- {
72
- type: "section",
73
- label: { en: "Contact Information" },
74
- layout: "grid",
75
- columns: 2,
76
- fields: [f.name, f.email, f.phone],
77
- },
78
- {
79
- type: "section",
80
- label: { en: "Profile" },
81
- fields: [f.bio],
82
- },
83
- ],
84
- }),
85
- )
86
- ```
87
-
88
- | Option | Type | Description |
89
- | ------------- | ------------------- | ------------------------------------ |
90
- | `type` | `"section"` | Required |
91
- | `label` | `string \| i18n` | Section heading |
92
- | `description` | `string \| i18n` | Section description |
93
- | `layout` | `"grid" \| "stack"` | Field layout |
94
- | `columns` | `number` | Grid columns (with `layout: "grid"`) |
95
- | `fields` | `Field[]` | Fields in this section |
96
-
97
- ### Form Sidebar
98
-
99
- Place fields in a right sidebar panel:
100
-
101
- ```ts
102
- .form(({ v, f }) =>
103
- v.collectionForm({
104
- sidebar: {
105
- position: "right",
106
- fields: [f.isActive, f.avatar, f.status],
107
- },
108
- fields: [ /* main content sections */ ],
109
- }),
110
- )
111
- ```
112
-
113
- ### Computed Fields
114
-
115
- Auto-compute values from other fields:
116
-
117
- ```ts
118
- {
119
- field: f.slug,
120
- compute: {
121
- handler: ({ data }) => {
122
- if (data.name && !data.slug?.trim()) {
123
- return slugify(data.name);
124
- }
125
- return undefined;
126
- },
127
- deps: ({ data }) => [data.name, data.slug],
128
- debounce: 300,
129
- },
130
- }
131
- ```
132
-
133
- | Option | Type | Description |
134
- | ---------- | ---------------- | ------------------------ |
135
- | `handler` | `(ctx) => value` | Compute function |
136
- | `deps` | `(ctx) => any[]` | Reactive dependencies |
137
- | `debounce` | `number` | Debounce in milliseconds |
138
-
139
- ### Conditional Visibility
140
-
141
- Show or hide fields based on other field values:
142
-
143
- ```ts
144
- {
145
- field: f.cancellationReason,
146
- hidden: ({ data }) => data.status !== "cancelled",
147
- }
148
- ```
149
-
150
- Read-only fields:
151
-
152
- ```ts
153
- {
154
- field: f.customerName,
155
- readOnly: ({ data }) => !!data.customer,
156
- }
157
- ```
158
-
159
- Section-level visibility:
160
-
161
- ```ts
162
- {
163
- type: "section",
164
- label: { en: "SEO" },
165
- hidden: ({ data }) => !data.isPublished,
166
- fields: [f.metaTitle, f.metaDescription],
167
- }
168
- ```
169
-
170
- ## Dashboard
171
-
172
- Configure with `dashboard.ts`:
173
-
174
- ```ts title="dashboard.ts"
175
- import { dashboard } from "#questpie";
176
-
177
- export default dashboard({
178
- title: { en: "Dashboard" },
179
- description: { en: "Overview of your app" },
180
- columns: 4,
181
- actions: [
182
- {
183
- id: "new-post",
184
- href: "/admin/collections/posts?create=true",
185
- label: { en: "New Post" },
186
- icon: { type: "icon", props: { name: "ph:plus" } },
187
- variant: "primary",
188
- },
189
- ],
190
- sections: [
191
- { id: "today", label: { en: "Today" }, layout: "grid", columns: 4 },
192
- { id: "business", label: { en: "Business" }, layout: "grid", columns: 4 },
193
- ],
194
- items: [
195
- /* widget items — see widget types below */
196
- ],
197
- });
198
- ```
199
-
200
- ### Widget Types
201
-
202
- **Stats** — count records with optional filter:
203
-
204
- ```ts
205
- {
206
- sectionId: "today",
207
- id: "pending",
208
- type: "stats",
209
- collection: "appointments",
210
- label: { en: "Pending" },
211
- filter: { status: "pending" },
212
- span: 1,
213
- }
214
- ```
215
-
216
- **Value** — custom-loaded value with trend:
217
-
218
- ```ts
219
- {
220
- sectionId: "business",
221
- id: "revenue",
222
- type: "value",
223
- span: 2,
224
- refreshInterval: 1000 * 60 * 5,
225
- loader: async ({ app }) => ({
226
- value: 42000,
227
- formatted: "42,000 EUR",
228
- label: { en: "Monthly Revenue" },
229
- trend: { value: "+12%" },
230
- }),
231
- }
232
- ```
233
-
234
- **Progress** — progress bar toward a goal:
235
-
236
- ```ts
237
- {
238
- sectionId: "business",
239
- id: "goal",
240
- type: "progress",
241
- span: 1,
242
- showPercentage: true,
243
- label: { en: "Monthly Goal" },
244
- loader: async ({ app }) => ({ current: 350, target: 500 }),
245
- }
246
- ```
247
-
248
- **Chart** — chart from field values:
249
-
250
- ```ts
251
- {
252
- sectionId: "business",
253
- id: "by-status",
254
- type: "chart",
255
- collection: "appointments",
256
- field: "status",
257
- chartType: "pie",
258
- label: { en: "By Status" },
259
- span: 1,
260
- }
261
- ```
262
-
263
- **Recent Items** — list recent records:
264
-
265
- ```ts
266
- {
267
- sectionId: "ops",
268
- id: "recent",
269
- type: "recentItems",
270
- collection: "appointments",
271
- label: { en: "Recent" },
272
- limit: 6,
273
- dateField: "scheduledAt",
274
- span: 2,
275
- }
276
- ```
277
-
278
- **Timeline** — activity stream:
279
-
280
- ```ts
281
- {
282
- sectionId: "ops",
283
- id: "activity",
284
- type: "timeline",
285
- label: { en: "Activity" },
286
- maxItems: 8,
287
- showTimestamps: true,
288
- timestampFormat: "relative",
289
- loader: async ({ app }) => {
290
- const res = await app.collections.appointments.find({
291
- limit: 8,
292
- orderBy: { updatedAt: "desc" },
293
- });
294
- return res.docs.map((apt) => ({
295
- id: apt.id,
296
- title: apt.displayTitle,
297
- description: `Status: ${apt.status}`,
298
- timestamp: apt.updatedAt,
299
- variant: apt.status === "completed" ? "success" : "warning",
300
- href: `/admin/collections/appointments/${apt.id}`,
301
- }));
302
- },
303
- span: 2,
304
- }
305
- ```
306
-
307
- ## Sidebar
308
-
309
- Configure with `sidebar.ts`:
310
-
311
- ```ts title="sidebar.ts"
312
- import { sidebar } from "#questpie";
313
-
314
- export default sidebar({
315
- sections: [
316
- { id: "overview", title: { en: "Overview" } },
317
- { id: "content", title: { en: "Content" } },
318
- { id: "external", title: { en: "External" } },
319
- ],
320
- items: [
321
- // Dashboard link
322
- {
323
- sectionId: "overview",
324
- type: "link",
325
- label: { en: "Dashboard" },
326
- href: "/admin",
327
- icon: { type: "icon", props: { name: "ph:house" } },
328
- },
329
- // Global settings
330
- { sectionId: "overview", type: "global", global: "siteSettings" },
331
- // Collection — label and icon from .admin() config
332
- { sectionId: "content", type: "collection", collection: "posts" },
333
- // External link
334
- {
335
- sectionId: "external",
336
- type: "link",
337
- label: { en: "Open Website" },
338
- href: "/",
339
- external: true,
340
- icon: { type: "icon", props: { name: "ph:arrow-square-out" } },
341
- },
342
- ],
343
- });
344
- ```
345
-
346
- Items appear in definition order within their section.
347
-
348
- Modules contribute sidebar items automatically. `adminModule` adds "Administration" with user management. `auditModule` adds an audit log item.
349
-
350
- ## Filters & Saved Views
351
-
352
- Filters are auto-generated from field definitions:
353
-
354
- | Field Type | Filter Operators |
355
- | ------------------- | ---------------------------------------- |
356
- | `text` | Contains, equals, starts with, ends with |
357
- | `number` | Equals, greater than, less than, between |
358
- | `boolean` | Is true, is false |
359
- | `select` | Is, is not, in |
360
- | `date` / `datetime` | Before, after, between |
361
- | `relation` | Is (picker) |
362
-
363
- Users can save filter + sort + column combinations as named views.
364
-
365
- ## Bulk Actions
366
-
367
- List views support multi-select. Check rows, then use the floating toolbar. Built-in: **Delete** (with confirmation). Soft-delete collections soft-delete instead of permanent removal.
368
-
369
- ## History & Versions
370
-
371
- Click the clock icon in the form toolbar. Two tabs:
372
-
373
- | Tab | Shows | Requires |
374
- | ------------ | ---------------------------------------------- | ----------------------------- |
375
- | **Activity** | Audit log (create, update, delete, transition) | `auditModule` |
376
- | **Versions** | Full document snapshots with restore | `.versioning()` on collection |
377
-
378
- Enable versioning:
379
-
380
- ```ts
381
- export const pages = collection("pages")
382
- .versioning({ drafts: true, maxVersions: 20 })
383
- .fields(({ f }) => ({ ... }));
384
- ```
385
-
386
- Disable audit for a specific collection:
387
-
388
- ```ts
389
- export const logs = collection("logs")
390
- .admin(() => ({ audit: false }))
391
- .fields(({ f }) => ({ ... }));
392
- ```
393
-
394
- ## Common Mistakes
395
-
396
- 1. **HIGH: Defining columns that don't match field names** — `columns: [f.name]` requires a `name` field in the collection's `.fields()`. Mismatches cause empty columns.
397
-
398
- 2. **MEDIUM: Not specifying `searchableFields`** — table search bar won't work unless you explicitly list which fields to search.
399
-
400
- 3. **MEDIUM: Forgetting sidebar section ordering** — items appear in definition order. If you want "Dashboard" at the top, define it first in the `items` array.
401
-
402
- 4. **MEDIUM: Missing `sectionId` on sidebar items** — every item must reference an existing section ID.
403
-
404
- 5. **LOW: Not setting `defaultSort`** — records appear in database insertion order which is usually not what users expect.
405
-
406
- ## Form Views and Live Preview
407
-
408
- Form views connect to the Live Preview V2 system when the collection has `.preview()` configured. The form editor becomes the source of `postMessage` patches — every field change emits a patch through the bus, giving the preview iframe instant updates.
409
-
410
- ### Enabling Preview on a Collection
411
-
412
- Add `.preview()` to the collection definition:
413
-
414
- ```ts
415
- export const pages = collection("pages")
416
- .fields(({ f }) => ({
417
- title: f.text({ required: true, localized: true }),
418
- slug: f.text({ required: true }),
419
- content: f.blocks({ localized: true }),
420
- }))
421
- .preview({
422
- url: ({ record }) => `/${record.slug}?preview=true`,
423
- watch: ["title", "slug", "content"],
424
- strategy: "hybrid",
425
- });
426
- ```
427
-
428
- ### Preview Strategy Options
429
-
430
- | Strategy | Use case |
431
- | ----------- | --------------------------------------------------------------------- |
432
- | `"instant"` | Simple pages with no derived fields — fastest, pure client patches |
433
- | `"server"` | Complex forms where every change needs server validation/computation |
434
- | `"hybrid"` | Default recommendation — instant local patches + server reconcile for slugs, relations, computed fields |
435
-
436
- ### How It Works
437
-
438
- 1. The form view detects `.preview()` config and opens a split-screen layout
439
- 2. Field edits emit patches via `postMessage` to the preview iframe
440
- 3. The preview page receives patches through `useQuestpiePreview` and updates in place
441
- 4. Save/autosave persists to the database but is NOT the live transport
442
- 5. In `"hybrid"` mode, derived data (slugs, expanded relations) reconciles via server round-trip