@questpie/admin 0.0.1 → 1.0.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 +439 -424
- package/dist/auth-layout-M8K8_q5R.mjs +181 -0
- package/dist/auth-layout-M8K8_q5R.mjs.map +1 -0
- package/dist/bulk-upload-dialog-D7w7W1Hl.mjs +273 -0
- package/dist/bulk-upload-dialog-D7w7W1Hl.mjs.map +1 -0
- package/dist/{components/ui/card.mjs → card-BKHjBQfw.mjs} +8 -8
- package/dist/card-BKHjBQfw.mjs.map +1 -0
- package/dist/client/styles/index.css +434 -0
- package/dist/client-DbpZKSgH.d.mts +13585 -0
- package/dist/client-DbpZKSgH.d.mts.map +1 -0
- package/dist/client-njX1rZmi.mjs +22612 -0
- package/dist/client-njX1rZmi.mjs.map +1 -0
- package/dist/client.d.mts +3 -0
- package/dist/client.mjs +13 -0
- package/dist/content-locales-provider-BXvuIgfg.mjs +1650 -0
- package/dist/content-locales-provider-BXvuIgfg.mjs.map +1 -0
- package/dist/dashboard-page-B4PGEdc2.mjs +2500 -0
- package/dist/dashboard-page-B4PGEdc2.mjs.map +1 -0
- package/dist/dashboard-page-mCY0pgZv.mjs +3 -0
- package/dist/dropzone-Do3awXKd.mjs +634 -0
- package/dist/dropzone-Do3awXKd.mjs.map +1 -0
- package/dist/{views/auth/forgot-password-form.mjs → forgot-password-page-Bcp-An4Y.mjs} +87 -14
- package/dist/forgot-password-page-Bcp-An4Y.mjs.map +1 -0
- package/dist/forgot-password-page-CEwsdLwn.mjs +3 -0
- package/dist/index-B9Xwk4hi.d.mts +2753 -0
- package/dist/index-B9Xwk4hi.d.mts.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +13 -0
- package/dist/login-page-BUnpCbCa.mjs +3 -0
- package/dist/login-page-CP4gA-dl.mjs +298 -0
- package/dist/login-page-CP4gA-dl.mjs.map +1 -0
- package/dist/preview-utils-BKQ9-TMa.mjs +65 -0
- package/dist/preview-utils-BKQ9-TMa.mjs.map +1 -0
- package/dist/{views/auth/reset-password-form.mjs → reset-password-page-BqfDmLxA.mjs} +111 -14
- package/dist/reset-password-page-BqfDmLxA.mjs.map +1 -0
- package/dist/reset-password-page-CufHz3h3.mjs +3 -0
- package/dist/runtime-6VZM878K.mjs +69 -0
- package/dist/runtime-6VZM878K.mjs.map +1 -0
- package/dist/saved-views.types-BMsz5mCy.d.mts +42 -0
- package/dist/saved-views.types-BMsz5mCy.d.mts.map +1 -0
- package/dist/server.d.mts +250 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +832 -0
- package/dist/server.mjs.map +1 -0
- package/dist/setup-page-BNNzt_Z6.mjs +3 -0
- package/dist/setup-page-YAP_fzqh.mjs +264 -0
- package/dist/setup-page-YAP_fzqh.mjs.map +1 -0
- package/dist/shared.d.mts +57 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +3 -0
- package/dist/{hooks/use-auth.mjs → use-auth-BoLmWtmU.mjs} +42 -30
- package/dist/use-auth-BoLmWtmU.mjs.map +1 -0
- package/package.json +48 -198
- package/.turbo/turbo-build.log +0 -108
- package/CHANGELOG.md +0 -10
- package/STATUS.md +0 -917
- package/VALIDATION.md +0 -602
- package/components.json +0 -24
- package/dist/__tests__/setup.mjs +0 -38
- package/dist/__tests__/test-utils.mjs +0 -45
- package/dist/__tests__/vitest.d.mjs +0 -3
- package/dist/components/admin-app.mjs +0 -69
- package/dist/components/fields/array-field.mjs +0 -190
- package/dist/components/fields/checkbox-field.mjs +0 -34
- package/dist/components/fields/custom-field.mjs +0 -32
- package/dist/components/fields/date-field.mjs +0 -41
- package/dist/components/fields/datetime-field.mjs +0 -42
- package/dist/components/fields/email-field.mjs +0 -37
- package/dist/components/fields/embedded-collection.mjs +0 -253
- package/dist/components/fields/field-types.mjs +0 -1
- package/dist/components/fields/field-utils.mjs +0 -10
- package/dist/components/fields/field-wrapper.mjs +0 -34
- package/dist/components/fields/index.mjs +0 -23
- package/dist/components/fields/json-field.mjs +0 -243
- package/dist/components/fields/locale-badge.mjs +0 -16
- package/dist/components/fields/number-field.mjs +0 -39
- package/dist/components/fields/password-field.mjs +0 -37
- package/dist/components/fields/relation-field.mjs +0 -104
- package/dist/components/fields/relation-picker.mjs +0 -229
- package/dist/components/fields/relation-select.mjs +0 -188
- package/dist/components/fields/rich-text-editor/index.mjs +0 -897
- package/dist/components/fields/select-field.mjs +0 -41
- package/dist/components/fields/switch-field.mjs +0 -34
- package/dist/components/fields/text-field.mjs +0 -38
- package/dist/components/fields/textarea-field.mjs +0 -38
- package/dist/components/index.mjs +0 -59
- package/dist/components/primitives/checkbox-input.mjs +0 -127
- package/dist/components/primitives/date-input.mjs +0 -303
- package/dist/components/primitives/index.mjs +0 -12
- package/dist/components/primitives/number-input.mjs +0 -104
- package/dist/components/primitives/select-input.mjs +0 -177
- package/dist/components/primitives/tag-input.mjs +0 -135
- package/dist/components/primitives/text-input.mjs +0 -39
- package/dist/components/primitives/textarea-input.mjs +0 -37
- package/dist/components/primitives/toggle-input.mjs +0 -31
- package/dist/components/primitives/types.mjs +0 -12
- package/dist/components/ui/accordion.mjs +0 -55
- package/dist/components/ui/avatar.mjs +0 -54
- package/dist/components/ui/badge.mjs +0 -34
- package/dist/components/ui/button.mjs +0 -48
- package/dist/components/ui/checkbox.mjs +0 -21
- package/dist/components/ui/combobox.mjs +0 -163
- package/dist/components/ui/dialog.mjs +0 -95
- package/dist/components/ui/dropdown-menu.mjs +0 -138
- package/dist/components/ui/field.mjs +0 -113
- package/dist/components/ui/input-group.mjs +0 -82
- package/dist/components/ui/input.mjs +0 -17
- package/dist/components/ui/label.mjs +0 -15
- package/dist/components/ui/popover.mjs +0 -56
- package/dist/components/ui/scroll-area.mjs +0 -38
- package/dist/components/ui/select.mjs +0 -100
- package/dist/components/ui/separator.mjs +0 -16
- package/dist/components/ui/sheet.mjs +0 -90
- package/dist/components/ui/sidebar.mjs +0 -387
- package/dist/components/ui/skeleton.mjs +0 -14
- package/dist/components/ui/spinner.mjs +0 -16
- package/dist/components/ui/switch.mjs +0 -22
- package/dist/components/ui/table.mjs +0 -68
- package/dist/components/ui/tabs.mjs +0 -48
- package/dist/components/ui/textarea.mjs +0 -15
- package/dist/components/ui/tooltip.mjs +0 -44
- package/dist/config/component-registry.mjs +0 -38
- package/dist/config/index.mjs +0 -129
- package/dist/hooks/admin-provider.mjs +0 -70
- package/dist/hooks/index.mjs +0 -7
- package/dist/hooks/store.mjs +0 -178
- package/dist/hooks/use-collection-db.mjs +0 -146
- package/dist/hooks/use-collection.mjs +0 -112
- package/dist/hooks/use-global.mjs +0 -46
- package/dist/hooks/use-mobile.mjs +0 -20
- package/dist/lib/utils.mjs +0 -10
- package/dist/styles/index.css +0 -336
- package/dist/styles/index.mjs +0 -1
- package/dist/utils/index.mjs +0 -9
- package/dist/views/auth/auth-layout.mjs +0 -52
- package/dist/views/auth/index.mjs +0 -6
- package/dist/views/auth/login-form.mjs +0 -156
- package/dist/views/collection/auto-form-fields.mjs +0 -525
- package/dist/views/collection/collection-form.mjs +0 -91
- package/dist/views/collection/collection-list.mjs +0 -76
- package/dist/views/collection/form-field.mjs +0 -42
- package/dist/views/collection/index.mjs +0 -6
- package/dist/views/common/index.mjs +0 -4
- package/dist/views/common/locale-switcher.mjs +0 -39
- package/dist/views/common/version-history.mjs +0 -272
- package/dist/views/index.mjs +0 -9
- package/dist/views/layout/admin-layout.mjs +0 -40
- package/dist/views/layout/admin-router.mjs +0 -95
- package/dist/views/layout/admin-sidebar.mjs +0 -63
- package/dist/views/layout/index.mjs +0 -5
- package/src/__tests__/setup.ts +0 -44
- package/src/__tests__/test-utils.tsx +0 -49
- package/src/__tests__/vitest.d.ts +0 -9
- package/src/components/admin-app.tsx +0 -221
- package/src/components/fields/array-field.tsx +0 -237
- package/src/components/fields/checkbox-field.tsx +0 -47
- package/src/components/fields/custom-field.tsx +0 -50
- package/src/components/fields/date-field.tsx +0 -65
- package/src/components/fields/datetime-field.tsx +0 -67
- package/src/components/fields/email-field.tsx +0 -51
- package/src/components/fields/embedded-collection.tsx +0 -315
- package/src/components/fields/field-types.ts +0 -162
- package/src/components/fields/field-utils.ts +0 -6
- package/src/components/fields/field-wrapper.tsx +0 -52
- package/src/components/fields/index.ts +0 -66
- package/src/components/fields/json-field.tsx +0 -440
- package/src/components/fields/locale-badge.tsx +0 -15
- package/src/components/fields/number-field.tsx +0 -57
- package/src/components/fields/password-field.tsx +0 -51
- package/src/components/fields/relation-field.tsx +0 -243
- package/src/components/fields/relation-picker.tsx +0 -402
- package/src/components/fields/relation-select.tsx +0 -327
- package/src/components/fields/rich-text-editor/index.tsx +0 -1337
- package/src/components/fields/select-field.tsx +0 -61
- package/src/components/fields/switch-field.tsx +0 -47
- package/src/components/fields/text-field.tsx +0 -55
- package/src/components/fields/textarea-field.tsx +0 -55
- package/src/components/index.ts +0 -40
- package/src/components/primitives/checkbox-input.tsx +0 -193
- package/src/components/primitives/date-input.tsx +0 -401
- package/src/components/primitives/index.ts +0 -24
- package/src/components/primitives/number-input.tsx +0 -132
- package/src/components/primitives/select-input.tsx +0 -296
- package/src/components/primitives/tag-input.tsx +0 -200
- package/src/components/primitives/text-input.tsx +0 -49
- package/src/components/primitives/textarea-input.tsx +0 -46
- package/src/components/primitives/toggle-input.tsx +0 -36
- package/src/components/primitives/types.ts +0 -235
- package/src/components/ui/accordion.tsx +0 -72
- package/src/components/ui/avatar.tsx +0 -106
- package/src/components/ui/badge.tsx +0 -48
- package/src/components/ui/button.tsx +0 -53
- package/src/components/ui/card.tsx +0 -94
- package/src/components/ui/checkbox.tsx +0 -27
- package/src/components/ui/combobox.tsx +0 -290
- package/src/components/ui/dialog.tsx +0 -151
- package/src/components/ui/dropdown-menu.tsx +0 -254
- package/src/components/ui/field.tsx +0 -227
- package/src/components/ui/input-group.tsx +0 -149
- package/src/components/ui/input.tsx +0 -20
- package/src/components/ui/label.tsx +0 -18
- package/src/components/ui/popover.tsx +0 -88
- package/src/components/ui/scroll-area.tsx +0 -53
- package/src/components/ui/select.tsx +0 -192
- package/src/components/ui/separator.tsx +0 -23
- package/src/components/ui/sheet.tsx +0 -127
- package/src/components/ui/sidebar.tsx +0 -723
- package/src/components/ui/skeleton.tsx +0 -13
- package/src/components/ui/spinner.tsx +0 -10
- package/src/components/ui/switch.tsx +0 -32
- package/src/components/ui/table.tsx +0 -99
- package/src/components/ui/tabs.tsx +0 -82
- package/src/components/ui/textarea.tsx +0 -18
- package/src/components/ui/tooltip.tsx +0 -70
- package/src/config/component-registry.ts +0 -190
- package/src/config/index.ts +0 -1099
- package/src/hooks/README.md +0 -269
- package/src/hooks/admin-provider.tsx +0 -110
- package/src/hooks/index.ts +0 -41
- package/src/hooks/store.ts +0 -248
- package/src/hooks/use-auth.ts +0 -168
- package/src/hooks/use-collection-db.ts +0 -209
- package/src/hooks/use-collection.ts +0 -156
- package/src/hooks/use-global.ts +0 -69
- package/src/hooks/use-mobile.ts +0 -21
- package/src/lib/utils.ts +0 -6
- package/src/styles/index.css +0 -340
- package/src/utils/index.ts +0 -6
- package/src/views/auth/auth-layout.tsx +0 -77
- package/src/views/auth/forgot-password-form.tsx +0 -192
- package/src/views/auth/index.ts +0 -21
- package/src/views/auth/login-form.tsx +0 -229
- package/src/views/auth/reset-password-form.tsx +0 -232
- package/src/views/collection/auto-form-fields.tsx +0 -982
- package/src/views/collection/collection-form.tsx +0 -186
- package/src/views/collection/collection-list.tsx +0 -223
- package/src/views/collection/form-field.tsx +0 -52
- package/src/views/collection/index.ts +0 -15
- package/src/views/common/index.ts +0 -8
- package/src/views/common/locale-switcher.tsx +0 -45
- package/src/views/common/version-history.tsx +0 -406
- package/src/views/index.ts +0 -25
- package/src/views/layout/admin-layout.tsx +0 -117
- package/src/views/layout/admin-router.tsx +0 -206
- package/src/views/layout/admin-sidebar.tsx +0 -185
- package/src/views/layout/index.ts +0 -12
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
- package/tsdown.config.ts +0 -13
- package/vitest.config.ts +0 -29
package/README.md
CHANGED
|
@@ -2,554 +2,569 @@
|
|
|
2
2
|
|
|
3
3
|
**Config-driven, batteries-included admin UI for QUESTPIE CMS**
|
|
4
4
|
|
|
5
|
-
A fully-featured admin interface
|
|
5
|
+
A fully-featured admin interface using the `qa()` builder pattern for type-safe configuration.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
- **Config-Driven** -
|
|
11
|
-
- **Type-Safe** - Full TypeScript inference
|
|
9
|
+
- **Builder Pattern** - Type-safe `qa()` builder with autocomplete
|
|
10
|
+
- **Config-Driven** - UI auto-generated from builder configuration
|
|
11
|
+
- **Type-Safe** - Full TypeScript inference with module augmentation
|
|
12
12
|
- **Tailwind v4** - Built with Tailwind CSS v4 + shadcn/ui components
|
|
13
|
-
- **Rich Text** - Tiptap editor with
|
|
14
|
-
- **Relations** -
|
|
13
|
+
- **Rich Text** - Tiptap editor with toolbar controls
|
|
14
|
+
- **Relations** - Single select, multi-picker with drag-and-drop
|
|
15
15
|
- **Layouts** - Sections, tabs, columns, grids, sidebars
|
|
16
16
|
- **Realtime** - SSE-powered live updates
|
|
17
|
-
- **Versioning** - Built-in version history
|
|
17
|
+
- **Versioning** - Built-in version history
|
|
18
18
|
- **i18n Ready** - Localization support for content
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
bun add @questpie/admin
|
|
24
|
-
bun add @tanstack/react-query
|
|
23
|
+
bun add @questpie/admin questpie @questpie/tanstack-query
|
|
24
|
+
bun add @tanstack/react-query
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### 1. Create Admin Builder
|
|
30
30
|
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
Link,
|
|
36
|
-
useLocation,
|
|
37
|
-
useNavigate,
|
|
38
|
-
useParams,
|
|
39
|
-
} from "@tanstack/react-router";
|
|
31
|
+
```typescript
|
|
32
|
+
// src/admin/builder.ts
|
|
33
|
+
import { qa, adminModule } from "@questpie/admin/client";
|
|
34
|
+
import type { AppCMS } from "./server/cms";
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const params = useParams();
|
|
45
|
-
const segments = params._.split("/").filter(Boolean);
|
|
36
|
+
// Create typed builder with admin fields/views
|
|
37
|
+
export const qab = qa<AppCMS>().use(adminModule).toNamespace();
|
|
38
|
+
```
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
### 2. Define Collection Configs
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// src/admin/collections/posts.ts
|
|
44
|
+
import { qab } from "../builder";
|
|
45
|
+
|
|
46
|
+
export const postsAdmin = qab
|
|
47
|
+
.collection("posts")
|
|
48
|
+
.meta({
|
|
49
|
+
label: "Blog Posts",
|
|
50
|
+
icon: FileTextIcon, // React component
|
|
51
|
+
})
|
|
52
|
+
// Fields with registry callback - ({ r }) gives autocomplete!
|
|
53
|
+
.fields(({ r }) => ({
|
|
54
|
+
title: r.text({
|
|
55
|
+
label: "Title",
|
|
56
|
+
placeholder: "Post title...",
|
|
57
|
+
maxLength: 200,
|
|
58
|
+
}),
|
|
59
|
+
slug: r.text({
|
|
60
|
+
label: "Slug",
|
|
61
|
+
}),
|
|
62
|
+
content: r.textarea({
|
|
63
|
+
label: "Content",
|
|
64
|
+
}),
|
|
65
|
+
status: r.select({
|
|
66
|
+
label: "Status",
|
|
67
|
+
options: [
|
|
68
|
+
{ label: "Draft", value: "draft" },
|
|
69
|
+
{ label: "Published", value: "published" },
|
|
70
|
+
],
|
|
71
|
+
}),
|
|
72
|
+
publishedAt: r.datetime({
|
|
73
|
+
label: "Publish Date",
|
|
74
|
+
}),
|
|
75
|
+
authorId: r.relation({
|
|
76
|
+
label: "Author",
|
|
77
|
+
targetCollection: "users",
|
|
78
|
+
}),
|
|
79
|
+
}))
|
|
80
|
+
// List view with field proxy - ({ v, f }) gives autocomplete!
|
|
81
|
+
.list(({ v, f }) =>
|
|
82
|
+
v.table({
|
|
83
|
+
columns: [f.title, f.status, f.authorId, f.publishedAt],
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
86
|
+
// Form view
|
|
87
|
+
.form(({ v, f }) =>
|
|
88
|
+
v.form({
|
|
89
|
+
sections: [
|
|
90
|
+
{
|
|
91
|
+
title: "Content",
|
|
92
|
+
fields: [f.title, f.slug, f.content],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
title: "Publishing",
|
|
96
|
+
fields: [f.status, f.publishedAt, f.authorId],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
}),
|
|
57
100
|
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3. Build Admin Configuration
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// src/admin/admin.ts
|
|
107
|
+
import { qa, adminModule } from "@questpie/admin/client";
|
|
108
|
+
import { postsAdmin } from "./collections/posts";
|
|
109
|
+
import { sidebarConfig } from "./sidebar";
|
|
110
|
+
|
|
111
|
+
export const admin = qa()
|
|
112
|
+
.use(adminModule)
|
|
113
|
+
.branding({
|
|
114
|
+
name: "My Admin",
|
|
115
|
+
})
|
|
116
|
+
.collections({
|
|
117
|
+
posts: postsAdmin,
|
|
118
|
+
})
|
|
119
|
+
.sidebar(sidebarConfig);
|
|
120
|
+
|
|
121
|
+
// Module augmentation for global type inference
|
|
122
|
+
declare module "@questpie/admin/client" {
|
|
123
|
+
interface AdminTypeRegistry {
|
|
124
|
+
cms: AppCMS;
|
|
125
|
+
admin: typeof admin;
|
|
126
|
+
}
|
|
58
127
|
}
|
|
59
128
|
```
|
|
60
129
|
|
|
61
|
-
###
|
|
130
|
+
### 4. Configure Sidebar
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// src/admin/sidebar.ts
|
|
134
|
+
import { qa } from "@questpie/admin/client";
|
|
135
|
+
import { HouseIcon, FileTextIcon } from "@phosphor-icons/react";
|
|
136
|
+
|
|
137
|
+
export const sidebarConfig = qa
|
|
138
|
+
.sidebar()
|
|
139
|
+
.section("main", (s) =>
|
|
140
|
+
s.items([
|
|
141
|
+
{ type: "link", label: "Dashboard", href: "/admin", icon: HouseIcon },
|
|
142
|
+
]),
|
|
143
|
+
)
|
|
144
|
+
.section("content", (s) =>
|
|
145
|
+
s
|
|
146
|
+
.title("Content")
|
|
147
|
+
.icon(FileTextIcon)
|
|
148
|
+
.items([{ type: "collection", collection: "posts", icon: FileTextIcon }]),
|
|
149
|
+
);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 5. Setup Tailwind CSS
|
|
153
|
+
|
|
154
|
+
In your main CSS file, import admin styles and configure Tailwind to scan the admin package:
|
|
155
|
+
|
|
156
|
+
```css
|
|
157
|
+
/* src/styles.css */
|
|
158
|
+
@import "tailwindcss";
|
|
159
|
+
@import "tw-animate-css";
|
|
160
|
+
@import "shadcn/tailwind.css";
|
|
161
|
+
@import "@questpie/admin/styles/index.css";
|
|
162
|
+
|
|
163
|
+
/* IMPORTANT: Tell Tailwind to scan admin package for utility classes */
|
|
164
|
+
@source "../node_modules/@questpie/admin/dist";
|
|
165
|
+
|
|
166
|
+
/* Your app's theme variables... */
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 6. Mount in React
|
|
62
170
|
|
|
63
171
|
```tsx
|
|
64
|
-
|
|
65
|
-
import {
|
|
66
|
-
import {
|
|
172
|
+
// routes/admin.tsx (TanStack Router example)
|
|
173
|
+
import { Admin, AdminLayoutProvider } from "@questpie/admin/client";
|
|
174
|
+
import { Link, Outlet, useLocation } from "@tanstack/react-router";
|
|
175
|
+
import { admin } from "~/admin/admin";
|
|
176
|
+
import { cmsClient } from "~/lib/cms-client";
|
|
177
|
+
import { queryClient } from "~/lib/query-client";
|
|
178
|
+
|
|
179
|
+
// Create runtime instance
|
|
180
|
+
const adminInstance = Admin.from(admin);
|
|
181
|
+
|
|
182
|
+
function AdminLayout() {
|
|
183
|
+
const location = useLocation();
|
|
67
184
|
|
|
68
|
-
function App() {
|
|
69
185
|
return (
|
|
70
|
-
<
|
|
186
|
+
<AdminLayoutProvider
|
|
187
|
+
admin={adminInstance}
|
|
71
188
|
client={cmsClient}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
189
|
+
queryClient={queryClient}
|
|
190
|
+
LinkComponent={Link}
|
|
191
|
+
activeRoute={location.pathname}
|
|
192
|
+
basePath="/admin"
|
|
193
|
+
>
|
|
194
|
+
<Outlet />
|
|
195
|
+
</AdminLayoutProvider>
|
|
196
|
+
);
|
|
76
197
|
}
|
|
77
198
|
```
|
|
78
199
|
|
|
79
|
-
##
|
|
200
|
+
## Builder API
|
|
80
201
|
|
|
81
|
-
|
|
202
|
+
### Main Entry Point
|
|
82
203
|
|
|
83
204
|
```typescript
|
|
84
|
-
import {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// List view
|
|
96
|
-
list: {
|
|
97
|
-
defaultColumns: ["title", "author", "publishedAt"],
|
|
98
|
-
defaultSort: { field: "publishedAt", direction: "desc" },
|
|
99
|
-
with: ["author"], // Auto-load relations
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
// Edit form
|
|
103
|
-
edit: {
|
|
104
|
-
// Optional: explicit order (recommended)
|
|
105
|
-
fields: ["title", "content", "status", "publishedAt"],
|
|
106
|
-
|
|
107
|
-
// Organize into sections
|
|
108
|
-
sections: [
|
|
109
|
-
{
|
|
110
|
-
title: "Content",
|
|
111
|
-
fields: ["title", "content"],
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
title: "Publishing",
|
|
115
|
-
fields: ["status", "publishedAt"],
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
// Field-level overrides
|
|
121
|
-
fields: {
|
|
122
|
-
title: {
|
|
123
|
-
label: "Post Title",
|
|
124
|
-
required: true,
|
|
125
|
-
},
|
|
126
|
-
content: {
|
|
127
|
-
type: "richText", // Custom field type
|
|
128
|
-
placeholder: "Write your post...",
|
|
129
|
-
},
|
|
130
|
-
status: {
|
|
131
|
-
type: "select",
|
|
132
|
-
options: [
|
|
133
|
-
{ label: "Draft", value: "draft" },
|
|
134
|
-
{ label: "Published", value: "published" },
|
|
135
|
-
],
|
|
136
|
-
},
|
|
137
|
-
publishedAt: {
|
|
138
|
-
label: "Publish Date",
|
|
139
|
-
type: "datetime",
|
|
140
|
-
// Conditional: readonly unless draft
|
|
141
|
-
readOnly: (values) => values.status !== "draft",
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
});
|
|
205
|
+
import { qa, adminModule } from "@questpie/admin/client";
|
|
206
|
+
|
|
207
|
+
// Start from admin module (includes built-in fields/views + user management)
|
|
208
|
+
const admin = qa()
|
|
209
|
+
.use(adminModule)
|
|
210
|
+
.branding({ name: "My Admin" })
|
|
211
|
+
.collections({ posts: postsAdmin })
|
|
212
|
+
.globals({ settings: settingsAdmin })
|
|
213
|
+
.sidebar(sidebarConfig)
|
|
214
|
+
.dashboard(dashboardConfig);
|
|
147
215
|
```
|
|
148
216
|
|
|
149
|
-
|
|
217
|
+
### Collection Builder
|
|
150
218
|
|
|
151
|
-
|
|
219
|
+
```typescript
|
|
220
|
+
const postsAdmin = qa
|
|
221
|
+
.collection("posts")
|
|
222
|
+
.meta({ label: "Posts", icon: PostIcon })
|
|
223
|
+
.fields(({ r }) => ({
|
|
224
|
+
// r = FieldRegistryProxy with autocomplete for registered fields
|
|
225
|
+
title: r.text({ maxLength: 200 }),
|
|
226
|
+
content: r.richText(),
|
|
227
|
+
status: r.select({ options: [...] }),
|
|
228
|
+
}))
|
|
229
|
+
.list(({ v, f }) => v.table({
|
|
230
|
+
// v = ViewRegistryProxy, f = FieldProxy
|
|
231
|
+
columns: [f.title, f.status],
|
|
232
|
+
}))
|
|
233
|
+
.form(({ v, f }) => v.form({
|
|
234
|
+
fields: [f.title, f.content, f.status],
|
|
235
|
+
}));
|
|
236
|
+
```
|
|
152
237
|
|
|
153
|
-
|
|
238
|
+
### Global Builder
|
|
154
239
|
|
|
155
|
-
|
|
240
|
+
```typescript
|
|
241
|
+
const settingsAdmin = qa
|
|
242
|
+
.global("settings")
|
|
243
|
+
.meta({ label: "Site Settings", icon: SettingsIcon })
|
|
244
|
+
.fields(({ r }) => ({
|
|
245
|
+
siteName: r.text({ maxLength: 100 }),
|
|
246
|
+
logo: r.text(), // TODO: image field
|
|
247
|
+
}))
|
|
248
|
+
.form(({ v, f }) =>
|
|
249
|
+
v.form({
|
|
250
|
+
fields: [f.siteName, f.logo],
|
|
251
|
+
}),
|
|
252
|
+
);
|
|
253
|
+
```
|
|
156
254
|
|
|
157
|
-
|
|
158
|
-
- ✅ **Routing** - `/admin/:collection/:id` patterns
|
|
159
|
-
- ✅ **List views** - columns, sorting, filtering
|
|
160
|
-
- ✅ **Form views** - create/edit with validation
|
|
161
|
-
- ✅ **Realtime sync** - SSE enabled by default
|
|
162
|
-
- ✅ **Relations loading** - auto-load with `with` config
|
|
255
|
+
### Sidebar Builder
|
|
163
256
|
|
|
164
|
-
|
|
257
|
+
```typescript
|
|
258
|
+
const sidebarConfig = qa
|
|
259
|
+
.sidebar()
|
|
260
|
+
.section("main", (s) =>
|
|
261
|
+
s.items([
|
|
262
|
+
{ type: "link", label: "Dashboard", href: "/admin", icon: HomeIcon },
|
|
263
|
+
]),
|
|
264
|
+
)
|
|
265
|
+
.section("content", (s) =>
|
|
266
|
+
s
|
|
267
|
+
.title("Content")
|
|
268
|
+
.items([
|
|
269
|
+
{ type: "collection", collection: "posts" },
|
|
270
|
+
{ type: "collection", collection: "pages" },
|
|
271
|
+
{ type: "divider" },
|
|
272
|
+
{ type: "global", global: "settings" },
|
|
273
|
+
]),
|
|
274
|
+
);
|
|
275
|
+
```
|
|
165
276
|
|
|
166
|
-
|
|
277
|
+
**Typed Sidebar (Recommended)**
|
|
167
278
|
|
|
168
279
|
```typescript
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
280
|
+
import type { AppCMS } from "./server/cms";
|
|
281
|
+
import type { SidebarItemForApp } from "@questpie/admin/client";
|
|
282
|
+
|
|
283
|
+
const sidebarConfig = qa.sidebar<AppCMS>().section("content", (s) =>
|
|
284
|
+
s.title("Content").items([
|
|
285
|
+
{ type: "collection", collection: "posts" },
|
|
286
|
+
{ type: "global", global: "settings" },
|
|
287
|
+
]),
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Standalone typed items
|
|
291
|
+
const items: SidebarItemForApp<AppCMS>[] = [
|
|
292
|
+
{ type: "collection", collection: "posts" },
|
|
293
|
+
{ type: "global", global: "settings" },
|
|
294
|
+
];
|
|
295
|
+
```
|
|
174
296
|
|
|
175
|
-
|
|
176
|
-
cancellationReason: {
|
|
177
|
-
type: "textarea",
|
|
178
|
-
visible: (values) => values.status === "cancelled",
|
|
179
|
-
required: (values) => values.status === "cancelled"
|
|
180
|
-
},
|
|
297
|
+
## Built-in Field Types
|
|
181
298
|
|
|
182
|
-
|
|
183
|
-
publishedAt: {
|
|
184
|
-
readOnly: (values) => values.status !== "draft"
|
|
185
|
-
},
|
|
299
|
+
The `adminModule` provides these field types:
|
|
186
300
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
301
|
+
| Field | Usage | Description |
|
|
302
|
+
| ---------- | ---------------------------------- | --------------------------- |
|
|
303
|
+
| `text` | `r.text({ maxLength })` | Single-line text input |
|
|
304
|
+
| `email` | `r.email()` | Email input with validation |
|
|
305
|
+
| `password` | `r.password()` | Password input |
|
|
306
|
+
| `textarea` | `r.textarea({ rows })` | Multi-line text |
|
|
307
|
+
| `number` | `r.number({ min, max })` | Numeric input |
|
|
308
|
+
| `checkbox` | `r.checkbox()` | Boolean checkbox |
|
|
309
|
+
| `switch` | `r.switch()` | Toggle switch |
|
|
310
|
+
| `select` | `r.select({ options })` | Dropdown select |
|
|
311
|
+
| `date` | `r.date()` | Date picker |
|
|
312
|
+
| `datetime` | `r.datetime()` | Date + time picker |
|
|
313
|
+
| `relation` | `r.relation({ targetCollection })` | Relation field |
|
|
314
|
+
| `json` | `r.json()` | JSON editor |
|
|
315
|
+
| `richText` | `r.richText()` | Rich text editor (Tiptap) |
|
|
316
|
+
|
|
317
|
+
## Form Layouts
|
|
199
318
|
|
|
200
|
-
### Sections
|
|
319
|
+
### Sections
|
|
201
320
|
|
|
202
321
|
```typescript
|
|
203
|
-
|
|
322
|
+
.form(({ v, f }) => v.form({
|
|
204
323
|
sections: [
|
|
205
324
|
{
|
|
206
325
|
title: "Basic Info",
|
|
207
|
-
description: "Main
|
|
208
|
-
fields: [
|
|
326
|
+
description: "Main details",
|
|
327
|
+
fields: [f.title, f.slug],
|
|
209
328
|
collapsible: true,
|
|
210
329
|
defaultOpen: true,
|
|
211
330
|
},
|
|
212
331
|
{
|
|
213
|
-
title: "
|
|
214
|
-
fields: [
|
|
332
|
+
title: "Content",
|
|
333
|
+
fields: [f.content],
|
|
215
334
|
},
|
|
216
|
-
]
|
|
217
|
-
}
|
|
335
|
+
],
|
|
336
|
+
}))
|
|
218
337
|
```
|
|
219
338
|
|
|
220
|
-
###
|
|
221
|
-
|
|
222
|
-
Columns, grids, and inline layouts are supported:
|
|
339
|
+
### Columns Layout
|
|
223
340
|
|
|
224
341
|
```typescript
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
{
|
|
234
|
-
title: "Pricing",
|
|
235
|
-
layout: "grid",
|
|
236
|
-
grid: { columns: 4, gap: 4 },
|
|
237
|
-
fields: [
|
|
238
|
-
{ field: "price", span: 1 },
|
|
239
|
-
{ field: "currency", span: 1 },
|
|
240
|
-
{ field: "stock", span: 2 },
|
|
241
|
-
],
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
title: "Dates",
|
|
245
|
-
layout: "inline",
|
|
246
|
-
fields: ["startDate", "endDate"],
|
|
247
|
-
},
|
|
248
|
-
];
|
|
249
|
-
}
|
|
342
|
+
sections: [
|
|
343
|
+
{
|
|
344
|
+
title: "Contact",
|
|
345
|
+
layout: "columns",
|
|
346
|
+
columns: 2,
|
|
347
|
+
fields: [f.firstName, f.lastName, f.email, f.phone],
|
|
348
|
+
},
|
|
349
|
+
];
|
|
250
350
|
```
|
|
251
351
|
|
|
252
|
-
|
|
352
|
+
### Grid Layout
|
|
253
353
|
|
|
254
354
|
```typescript
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
{
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
355
|
+
sections: [
|
|
356
|
+
{
|
|
357
|
+
layout: "grid",
|
|
358
|
+
grid: { columns: 4, gap: 4 },
|
|
359
|
+
fields: [
|
|
360
|
+
{ field: f.title, span: 4 }, // full width
|
|
361
|
+
{ field: f.price, span: 1 }, // 1/4
|
|
362
|
+
{ field: f.currency, span: 1 }, // 1/4
|
|
363
|
+
{ field: f.stock, span: 2 }, // 2/4
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
];
|
|
266
367
|
```
|
|
267
368
|
|
|
268
369
|
### Tabs
|
|
269
370
|
|
|
270
371
|
```typescript
|
|
271
|
-
|
|
372
|
+
.form(({ v, f }) => v.form({
|
|
272
373
|
tabs: [
|
|
273
374
|
{
|
|
274
375
|
id: "content",
|
|
275
376
|
label: "Content",
|
|
276
|
-
fields: [
|
|
377
|
+
fields: [f.title, f.content],
|
|
277
378
|
},
|
|
278
379
|
{
|
|
279
380
|
id: "meta",
|
|
280
381
|
label: "Metadata",
|
|
281
|
-
fields: [
|
|
382
|
+
fields: [f.seo, f.tags],
|
|
282
383
|
},
|
|
283
|
-
]
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### Localization (i18n)
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
app: {
|
|
291
|
-
locales: {
|
|
292
|
-
default: "en",
|
|
293
|
-
available: ["en", "sk", "de"]
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
collections: {
|
|
297
|
-
posts: {
|
|
298
|
-
fields: {
|
|
299
|
-
title: { type: "text", localized: true },
|
|
300
|
-
content: { type: "richText", localized: true }
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
384
|
+
],
|
|
385
|
+
}))
|
|
304
386
|
```
|
|
305
387
|
|
|
306
|
-
###
|
|
307
|
-
|
|
308
|
-
Automatic relation field support with create/edit capabilities:
|
|
309
|
-
|
|
310
|
-
Display label uses the target record’s `_title`.
|
|
388
|
+
### Sidebar Layout
|
|
311
389
|
|
|
312
390
|
```typescript
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
391
|
+
.form(({ v, f }) => v.form({
|
|
392
|
+
layout: "with-sidebar",
|
|
393
|
+
sections: [
|
|
394
|
+
{ title: "Content", fields: [f.title, f.content] },
|
|
395
|
+
],
|
|
396
|
+
sidebar: {
|
|
397
|
+
position: "right",
|
|
398
|
+
width: "300px",
|
|
399
|
+
fields: [f.status, f.publishedAt],
|
|
321
400
|
},
|
|
322
|
-
|
|
323
|
-
// Multiple relations (one-to-many, many-to-many)
|
|
324
|
-
tags: {
|
|
325
|
-
relation: {
|
|
326
|
-
targetCollection: "tags",
|
|
327
|
-
mode: "picker", // Enables multi-select
|
|
328
|
-
orderable: true, // Enable drag-and-drop reordering
|
|
329
|
-
|
|
330
|
-
// Filter options based on current form values
|
|
331
|
-
filter: (values) => ({
|
|
332
|
-
where: { type: { eq: values.postType } }
|
|
333
|
-
})
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
401
|
+
}))
|
|
337
402
|
```
|
|
338
403
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
- ✅ **Plus button** - Create new related item (opens side sheet)
|
|
342
|
-
- ✅ **Edit button** - Modify selected item (opens side sheet)
|
|
343
|
-
- ✅ **Auto-complete** - Search and filter options
|
|
344
|
-
- ✅ **Drag-and-drop** - Reorder multiple relations (when `orderable: true`)
|
|
345
|
-
- ✅ **Conditional filtering** - Filter options based on form state
|
|
346
|
-
|
|
347
|
-
### Rich Text Editor (Tiptap)
|
|
348
|
-
|
|
349
|
-
Built-in rich text editing with toolbar controls:
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
fields: {
|
|
353
|
-
content: {
|
|
354
|
-
type: "richText",
|
|
355
|
-
placeholder: "Start writing...",
|
|
356
|
-
richText: {
|
|
357
|
-
outputFormat: "html", // "json" (default) or "markdown"
|
|
358
|
-
enableImages: true,
|
|
359
|
-
showCharacterCount: true,
|
|
360
|
-
maxCharacters: 10000,
|
|
361
|
-
features: {
|
|
362
|
-
slashCommands: true,
|
|
363
|
-
tableControls: true,
|
|
364
|
-
bubbleMenu: true
|
|
365
|
-
},
|
|
366
|
-
onImageUpload: async (file) => {
|
|
367
|
-
// Return a public URL after upload
|
|
368
|
-
return await uploadImage(file)
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Note: Markdown output requires a Markdown extension in the `extensions` array.
|
|
376
|
-
Slash commands, table tools, and the bubble menu can be toggled via `features`.
|
|
377
|
-
|
|
378
|
-
### Embedded Collections
|
|
404
|
+
## Conditional Fields
|
|
379
405
|
|
|
380
406
|
```typescript
|
|
381
|
-
fields
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
407
|
+
.fields(({ r }) => ({
|
|
408
|
+
status: r.select({
|
|
409
|
+
options: [
|
|
410
|
+
{ label: "Active", value: "active" },
|
|
411
|
+
{ label: "Cancelled", value: "cancelled" },
|
|
412
|
+
],
|
|
413
|
+
}),
|
|
414
|
+
// Show only when status is "cancelled"
|
|
415
|
+
cancellationReason: r.textarea({
|
|
416
|
+
visible: (values) => values.status === "cancelled",
|
|
417
|
+
required: (values) => values.status === "cancelled",
|
|
418
|
+
}),
|
|
419
|
+
// Readonly unless draft
|
|
420
|
+
publishedAt: r.datetime({
|
|
421
|
+
readOnly: (values) => values.status !== "draft",
|
|
422
|
+
}),
|
|
423
|
+
}))
|
|
391
424
|
```
|
|
392
425
|
|
|
393
|
-
|
|
426
|
+
## Relation Fields
|
|
394
427
|
|
|
395
428
|
```typescript
|
|
396
|
-
fields
|
|
397
|
-
|
|
398
|
-
|
|
429
|
+
.fields(({ r }) => ({
|
|
430
|
+
// Single relation
|
|
431
|
+
authorId: r.relation({
|
|
432
|
+
label: "Author",
|
|
433
|
+
targetCollection: "users",
|
|
434
|
+
}),
|
|
435
|
+
// Multiple relations with ordering
|
|
436
|
+
tags: r.relation({
|
|
399
437
|
label: "Tags",
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
},
|
|
406
|
-
|
|
407
|
-
ratings: {
|
|
408
|
-
type: "array",
|
|
409
|
-
label: "Ratings",
|
|
410
|
-
array: {
|
|
411
|
-
itemType: "number",
|
|
412
|
-
minItems: 1
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
438
|
+
targetCollection: "tags",
|
|
439
|
+
type: "multiple",
|
|
440
|
+
orderable: true,
|
|
441
|
+
}),
|
|
442
|
+
}))
|
|
416
443
|
```
|
|
417
444
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
Track all changes to collection items with built-in versioning:
|
|
445
|
+
## Hooks
|
|
421
446
|
|
|
422
447
|
```typescript
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
448
|
+
import {
|
|
449
|
+
useAdminStore,
|
|
450
|
+
useAdminContext,
|
|
451
|
+
useCollectionList,
|
|
452
|
+
useCollectionItem,
|
|
453
|
+
useCollectionCreate,
|
|
454
|
+
useCollectionUpdate,
|
|
455
|
+
useCollectionDelete,
|
|
456
|
+
useGlobal,
|
|
457
|
+
useGlobalUpdate,
|
|
458
|
+
useAuthClient,
|
|
459
|
+
useAdminRoutes,
|
|
460
|
+
} from "@questpie/admin/client";
|
|
461
|
+
|
|
462
|
+
// Collection list with pagination
|
|
463
|
+
const { data, isLoading } = useCollectionList("posts", {
|
|
464
|
+
limit: 10,
|
|
465
|
+
offset: 0,
|
|
466
|
+
});
|
|
434
467
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
showVersionHistory: true
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
```
|
|
468
|
+
// Single item
|
|
469
|
+
const { data: post } = useCollectionItem("posts", id);
|
|
442
470
|
|
|
443
|
-
|
|
471
|
+
// CRUD mutations
|
|
472
|
+
const createPost = useCollectionCreate("posts");
|
|
473
|
+
const updatePost = useCollectionUpdate("posts");
|
|
474
|
+
const deletePost = useCollectionDelete("posts");
|
|
444
475
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
- ✅ **Restore versions** - Roll back to previous versions
|
|
449
|
-
- ✅ **User tracking** - Know who made each change
|
|
450
|
-
- ✅ **Configurable retention** - Control how long to keep history
|
|
476
|
+
// Global data
|
|
477
|
+
const { data: settings } = useGlobal("settings");
|
|
478
|
+
const updateSettings = useGlobalUpdate("settings");
|
|
451
479
|
|
|
452
|
-
|
|
480
|
+
// Admin store
|
|
481
|
+
const { admin, client, basePath } = useAdminStore((s) => ({
|
|
482
|
+
admin: s.admin,
|
|
483
|
+
client: s.client,
|
|
484
|
+
basePath: s.basePath,
|
|
485
|
+
}));
|
|
453
486
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
tree: {
|
|
458
|
-
parentField: "parentId",
|
|
459
|
-
labelField: "name",
|
|
460
|
-
collapsible: true
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
```
|
|
487
|
+
// Auth
|
|
488
|
+
const authClient = useAuthClient();
|
|
489
|
+
const session = authClient.useSession();
|
|
464
490
|
|
|
465
|
-
|
|
491
|
+
// Routes
|
|
492
|
+
const { routes, navigate } = useAdminRoutes();
|
|
493
|
+
navigate({ collection: "posts", action: "create" });
|
|
494
|
+
```
|
|
466
495
|
|
|
467
|
-
|
|
496
|
+
## Components
|
|
468
497
|
|
|
469
498
|
```typescript
|
|
470
|
-
import {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
499
|
+
import {
|
|
500
|
+
// Navigation
|
|
501
|
+
AdminLink,
|
|
502
|
+
CollectionLink,
|
|
503
|
+
CollectionCreateLink,
|
|
504
|
+
CollectionEditLink,
|
|
505
|
+
GlobalLink,
|
|
506
|
+
DashboardLink,
|
|
507
|
+
|
|
508
|
+
// Fields
|
|
509
|
+
TextField,
|
|
510
|
+
EmailField,
|
|
511
|
+
NumberField,
|
|
512
|
+
SelectField,
|
|
513
|
+
CheckboxField,
|
|
514
|
+
SwitchField,
|
|
515
|
+
DateField,
|
|
516
|
+
RelationField,
|
|
517
|
+
JsonField,
|
|
518
|
+
|
|
519
|
+
// UI (shadcn)
|
|
520
|
+
Button,
|
|
521
|
+
Card,
|
|
522
|
+
Dialog,
|
|
523
|
+
Sheet,
|
|
524
|
+
Tabs,
|
|
525
|
+
Table,
|
|
526
|
+
// ... 27+ shadcn components
|
|
527
|
+
} from "@questpie/admin/client";
|
|
484
528
|
```
|
|
485
529
|
|
|
486
|
-
##
|
|
530
|
+
## Package Exports
|
|
487
531
|
|
|
488
532
|
```typescript
|
|
489
|
-
// Main
|
|
490
|
-
import { AdminApp } from "@questpie/admin";
|
|
491
|
-
|
|
492
|
-
// Config helper
|
|
493
|
-
import { defineAdminConfig } from "@questpie/admin/config";
|
|
494
|
-
|
|
495
|
-
// Hooks
|
|
496
|
-
import { AdminProvider, useCollection } from "@questpie/admin/hooks";
|
|
497
|
-
|
|
498
|
-
// Components
|
|
533
|
+
// Main client exports (recommended)
|
|
499
534
|
import {
|
|
535
|
+
qa,
|
|
536
|
+
Admin,
|
|
537
|
+
AdminBuilder,
|
|
538
|
+
adminModule,
|
|
539
|
+
AdminProvider,
|
|
540
|
+
useAdminStore,
|
|
541
|
+
useCollectionList,
|
|
542
|
+
useAdminRoutes,
|
|
543
|
+
AdminLayoutProvider,
|
|
500
544
|
CollectionList,
|
|
501
545
|
CollectionForm,
|
|
502
|
-
|
|
503
|
-
RelationPicker,
|
|
504
|
-
VersionHistory,
|
|
505
|
-
} from "@questpie/admin/components";
|
|
546
|
+
} from "@questpie/admin/client";
|
|
506
547
|
|
|
507
|
-
//
|
|
508
|
-
import {
|
|
548
|
+
// Server exports (for backend CMS setup)
|
|
549
|
+
import { adminModule } from "@questpie/admin/server";
|
|
509
550
|
|
|
510
551
|
// Styles
|
|
511
|
-
import "@questpie/admin/styles";
|
|
552
|
+
import "@questpie/admin/client/styles/index.css";
|
|
512
553
|
```
|
|
513
554
|
|
|
514
|
-
##
|
|
555
|
+
## Styling
|
|
515
556
|
|
|
516
|
-
|
|
557
|
+
Built with Tailwind CSS v4 + shadcn/ui:
|
|
517
558
|
|
|
518
|
-
-
|
|
559
|
+
- 27+ pre-built components
|
|
519
560
|
- Light/dark theme support
|
|
520
561
|
- oklch color space
|
|
521
562
|
- Customizable via Tailwind tokens
|
|
563
|
+
- Styles scoped under `.questpie-admin`
|
|
564
|
+
|
|
565
|
+
## Examples
|
|
522
566
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
See [tanstack-barbershop](../../examples/tanstack-barbershop) for complete example.
|
|
526
|
-
|
|
527
|
-
## 🚀 Roadmap
|
|
528
|
-
|
|
529
|
-
- [ ] Schema introspection (auto field type inference) - not planned
|
|
530
|
-
- [x] Conditional visibility/readonly/disabled
|
|
531
|
-
- [x] Sections organization
|
|
532
|
-
- [x] Advanced layouts (columns, grid, inline, sidebar)
|
|
533
|
-
- [x] Component registry
|
|
534
|
-
- [x] Relation fields (RelationSelect, RelationPicker)
|
|
535
|
-
- [x] Version history & audit logging
|
|
536
|
-
- [x] Embedded collections (inline, modal, drawer)
|
|
537
|
-
- [x] Array fields
|
|
538
|
-
- [x] Tabs layout
|
|
539
|
-
- [ ] Tree views (hierarchical data)
|
|
540
|
-
- [ ] Bulk actions
|
|
541
|
-
- [ ] Saved views/filters
|
|
542
|
-
- [ ] Custom dashboard
|
|
543
|
-
- [ ] Permissions UI
|
|
544
|
-
|
|
545
|
-
## Related Packages
|
|
546
|
-
|
|
547
|
-
- [`@questpie/cms`](../cms) - Core CMS engine
|
|
548
|
-
- [`@questpie/tanstack-query`](../tanstack-query) - TanStack Query integration
|
|
549
|
-
- [`@questpie/hono`](../hono) - Hono adapter
|
|
550
|
-
- [`@questpie/elysia`](../elysia) - Elysia adapter
|
|
551
|
-
- [`@questpie/next`](../next) - Next.js adapter
|
|
552
|
-
- [`@questpie/tanstack-start`](../tanstack-start) - TanStack Start adapter
|
|
567
|
+
See [tanstack-barbershop](../../examples/tanstack-barbershop) for a complete working example.
|
|
553
568
|
|
|
554
569
|
## License
|
|
555
570
|
|