@questpie/admin 0.0.1 → 1.0.0

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 (250) hide show
  1. package/README.md +439 -424
  2. package/dist/auth-layout-M8K8_q5R.mjs +181 -0
  3. package/dist/auth-layout-M8K8_q5R.mjs.map +1 -0
  4. package/dist/bulk-upload-dialog-h7zXD78Y.mjs +274 -0
  5. package/dist/bulk-upload-dialog-h7zXD78Y.mjs.map +1 -0
  6. package/dist/{components/ui/card.mjs → card-BKHjBQfw.mjs} +8 -8
  7. package/dist/card-BKHjBQfw.mjs.map +1 -0
  8. package/dist/client/styles/index.css +434 -0
  9. package/dist/client-BCGpkAz6.mjs +22635 -0
  10. package/dist/client-BCGpkAz6.mjs.map +1 -0
  11. package/dist/client-CcWZbkBP.d.mts +13585 -0
  12. package/dist/client-CcWZbkBP.d.mts.map +1 -0
  13. package/dist/client.d.mts +3 -0
  14. package/dist/client.mjs +14 -0
  15. package/dist/content-locales-provider-BXvuIgfg.mjs +1650 -0
  16. package/dist/content-locales-provider-BXvuIgfg.mjs.map +1 -0
  17. package/dist/dashboard-page-B4PGEdc2.mjs +2500 -0
  18. package/dist/dashboard-page-B4PGEdc2.mjs.map +1 -0
  19. package/dist/dashboard-page-CVlyR40m.mjs +6 -0
  20. package/dist/dropzone-Do3awXKd.mjs +634 -0
  21. package/dist/dropzone-Do3awXKd.mjs.map +1 -0
  22. package/dist/{views/auth/forgot-password-form.mjs → forgot-password-page-Bcp-An4Y.mjs} +87 -14
  23. package/dist/forgot-password-page-Bcp-An4Y.mjs.map +1 -0
  24. package/dist/forgot-password-page-CIILVhfo.mjs +7 -0
  25. package/dist/index-B9Xwk4hi.d.mts +2753 -0
  26. package/dist/index-B9Xwk4hi.d.mts.map +1 -0
  27. package/dist/index.d.mts +3 -0
  28. package/dist/index.mjs +14 -0
  29. package/dist/login-page-8K7fo0qK.mjs +7 -0
  30. package/dist/login-page-CP4gA-dl.mjs +298 -0
  31. package/dist/login-page-CP4gA-dl.mjs.map +1 -0
  32. package/dist/preview-utils-BKQ9-TMa.mjs +65 -0
  33. package/dist/preview-utils-BKQ9-TMa.mjs.map +1 -0
  34. package/dist/{views/auth/reset-password-form.mjs → reset-password-page-BqfDmLxA.mjs} +111 -14
  35. package/dist/reset-password-page-BqfDmLxA.mjs.map +1 -0
  36. package/dist/reset-password-page-DLATv0xQ.mjs +7 -0
  37. package/dist/runtime-6VZM878K.mjs +69 -0
  38. package/dist/runtime-6VZM878K.mjs.map +1 -0
  39. package/dist/saved-views.types-BMsz5mCy.d.mts +42 -0
  40. package/dist/saved-views.types-BMsz5mCy.d.mts.map +1 -0
  41. package/dist/server.d.mts +250 -0
  42. package/dist/server.d.mts.map +1 -0
  43. package/dist/server.mjs +832 -0
  44. package/dist/server.mjs.map +1 -0
  45. package/dist/setup-page-CMZ5P_OE.mjs +6 -0
  46. package/dist/setup-page-YAP_fzqh.mjs +264 -0
  47. package/dist/setup-page-YAP_fzqh.mjs.map +1 -0
  48. package/dist/shared.d.mts +57 -0
  49. package/dist/shared.d.mts.map +1 -0
  50. package/dist/shared.mjs +3 -0
  51. package/dist/{hooks/use-auth.mjs → use-auth-BoLmWtmU.mjs} +42 -30
  52. package/dist/use-auth-BoLmWtmU.mjs.map +1 -0
  53. package/package.json +48 -197
  54. package/.turbo/turbo-build.log +0 -108
  55. package/CHANGELOG.md +0 -10
  56. package/STATUS.md +0 -917
  57. package/VALIDATION.md +0 -602
  58. package/components.json +0 -24
  59. package/dist/__tests__/setup.mjs +0 -38
  60. package/dist/__tests__/test-utils.mjs +0 -45
  61. package/dist/__tests__/vitest.d.mjs +0 -3
  62. package/dist/components/admin-app.mjs +0 -69
  63. package/dist/components/fields/array-field.mjs +0 -190
  64. package/dist/components/fields/checkbox-field.mjs +0 -34
  65. package/dist/components/fields/custom-field.mjs +0 -32
  66. package/dist/components/fields/date-field.mjs +0 -41
  67. package/dist/components/fields/datetime-field.mjs +0 -42
  68. package/dist/components/fields/email-field.mjs +0 -37
  69. package/dist/components/fields/embedded-collection.mjs +0 -253
  70. package/dist/components/fields/field-types.mjs +0 -1
  71. package/dist/components/fields/field-utils.mjs +0 -10
  72. package/dist/components/fields/field-wrapper.mjs +0 -34
  73. package/dist/components/fields/index.mjs +0 -23
  74. package/dist/components/fields/json-field.mjs +0 -243
  75. package/dist/components/fields/locale-badge.mjs +0 -16
  76. package/dist/components/fields/number-field.mjs +0 -39
  77. package/dist/components/fields/password-field.mjs +0 -37
  78. package/dist/components/fields/relation-field.mjs +0 -104
  79. package/dist/components/fields/relation-picker.mjs +0 -229
  80. package/dist/components/fields/relation-select.mjs +0 -188
  81. package/dist/components/fields/rich-text-editor/index.mjs +0 -897
  82. package/dist/components/fields/select-field.mjs +0 -41
  83. package/dist/components/fields/switch-field.mjs +0 -34
  84. package/dist/components/fields/text-field.mjs +0 -38
  85. package/dist/components/fields/textarea-field.mjs +0 -38
  86. package/dist/components/index.mjs +0 -59
  87. package/dist/components/primitives/checkbox-input.mjs +0 -127
  88. package/dist/components/primitives/date-input.mjs +0 -303
  89. package/dist/components/primitives/index.mjs +0 -12
  90. package/dist/components/primitives/number-input.mjs +0 -104
  91. package/dist/components/primitives/select-input.mjs +0 -177
  92. package/dist/components/primitives/tag-input.mjs +0 -135
  93. package/dist/components/primitives/text-input.mjs +0 -39
  94. package/dist/components/primitives/textarea-input.mjs +0 -37
  95. package/dist/components/primitives/toggle-input.mjs +0 -31
  96. package/dist/components/primitives/types.mjs +0 -12
  97. package/dist/components/ui/accordion.mjs +0 -55
  98. package/dist/components/ui/avatar.mjs +0 -54
  99. package/dist/components/ui/badge.mjs +0 -34
  100. package/dist/components/ui/button.mjs +0 -48
  101. package/dist/components/ui/checkbox.mjs +0 -21
  102. package/dist/components/ui/combobox.mjs +0 -163
  103. package/dist/components/ui/dialog.mjs +0 -95
  104. package/dist/components/ui/dropdown-menu.mjs +0 -138
  105. package/dist/components/ui/field.mjs +0 -113
  106. package/dist/components/ui/input-group.mjs +0 -82
  107. package/dist/components/ui/input.mjs +0 -17
  108. package/dist/components/ui/label.mjs +0 -15
  109. package/dist/components/ui/popover.mjs +0 -56
  110. package/dist/components/ui/scroll-area.mjs +0 -38
  111. package/dist/components/ui/select.mjs +0 -100
  112. package/dist/components/ui/separator.mjs +0 -16
  113. package/dist/components/ui/sheet.mjs +0 -90
  114. package/dist/components/ui/sidebar.mjs +0 -387
  115. package/dist/components/ui/skeleton.mjs +0 -14
  116. package/dist/components/ui/spinner.mjs +0 -16
  117. package/dist/components/ui/switch.mjs +0 -22
  118. package/dist/components/ui/table.mjs +0 -68
  119. package/dist/components/ui/tabs.mjs +0 -48
  120. package/dist/components/ui/textarea.mjs +0 -15
  121. package/dist/components/ui/tooltip.mjs +0 -44
  122. package/dist/config/component-registry.mjs +0 -38
  123. package/dist/config/index.mjs +0 -129
  124. package/dist/hooks/admin-provider.mjs +0 -70
  125. package/dist/hooks/index.mjs +0 -7
  126. package/dist/hooks/store.mjs +0 -178
  127. package/dist/hooks/use-collection-db.mjs +0 -146
  128. package/dist/hooks/use-collection.mjs +0 -112
  129. package/dist/hooks/use-global.mjs +0 -46
  130. package/dist/hooks/use-mobile.mjs +0 -20
  131. package/dist/lib/utils.mjs +0 -10
  132. package/dist/styles/index.css +0 -336
  133. package/dist/styles/index.mjs +0 -1
  134. package/dist/utils/index.mjs +0 -9
  135. package/dist/views/auth/auth-layout.mjs +0 -52
  136. package/dist/views/auth/index.mjs +0 -6
  137. package/dist/views/auth/login-form.mjs +0 -156
  138. package/dist/views/collection/auto-form-fields.mjs +0 -525
  139. package/dist/views/collection/collection-form.mjs +0 -91
  140. package/dist/views/collection/collection-list.mjs +0 -76
  141. package/dist/views/collection/form-field.mjs +0 -42
  142. package/dist/views/collection/index.mjs +0 -6
  143. package/dist/views/common/index.mjs +0 -4
  144. package/dist/views/common/locale-switcher.mjs +0 -39
  145. package/dist/views/common/version-history.mjs +0 -272
  146. package/dist/views/index.mjs +0 -9
  147. package/dist/views/layout/admin-layout.mjs +0 -40
  148. package/dist/views/layout/admin-router.mjs +0 -95
  149. package/dist/views/layout/admin-sidebar.mjs +0 -63
  150. package/dist/views/layout/index.mjs +0 -5
  151. package/src/__tests__/setup.ts +0 -44
  152. package/src/__tests__/test-utils.tsx +0 -49
  153. package/src/__tests__/vitest.d.ts +0 -9
  154. package/src/components/admin-app.tsx +0 -221
  155. package/src/components/fields/array-field.tsx +0 -237
  156. package/src/components/fields/checkbox-field.tsx +0 -47
  157. package/src/components/fields/custom-field.tsx +0 -50
  158. package/src/components/fields/date-field.tsx +0 -65
  159. package/src/components/fields/datetime-field.tsx +0 -67
  160. package/src/components/fields/email-field.tsx +0 -51
  161. package/src/components/fields/embedded-collection.tsx +0 -315
  162. package/src/components/fields/field-types.ts +0 -162
  163. package/src/components/fields/field-utils.ts +0 -6
  164. package/src/components/fields/field-wrapper.tsx +0 -52
  165. package/src/components/fields/index.ts +0 -66
  166. package/src/components/fields/json-field.tsx +0 -440
  167. package/src/components/fields/locale-badge.tsx +0 -15
  168. package/src/components/fields/number-field.tsx +0 -57
  169. package/src/components/fields/password-field.tsx +0 -51
  170. package/src/components/fields/relation-field.tsx +0 -243
  171. package/src/components/fields/relation-picker.tsx +0 -402
  172. package/src/components/fields/relation-select.tsx +0 -327
  173. package/src/components/fields/rich-text-editor/index.tsx +0 -1337
  174. package/src/components/fields/select-field.tsx +0 -61
  175. package/src/components/fields/switch-field.tsx +0 -47
  176. package/src/components/fields/text-field.tsx +0 -55
  177. package/src/components/fields/textarea-field.tsx +0 -55
  178. package/src/components/index.ts +0 -40
  179. package/src/components/primitives/checkbox-input.tsx +0 -193
  180. package/src/components/primitives/date-input.tsx +0 -401
  181. package/src/components/primitives/index.ts +0 -24
  182. package/src/components/primitives/number-input.tsx +0 -132
  183. package/src/components/primitives/select-input.tsx +0 -296
  184. package/src/components/primitives/tag-input.tsx +0 -200
  185. package/src/components/primitives/text-input.tsx +0 -49
  186. package/src/components/primitives/textarea-input.tsx +0 -46
  187. package/src/components/primitives/toggle-input.tsx +0 -36
  188. package/src/components/primitives/types.ts +0 -235
  189. package/src/components/ui/accordion.tsx +0 -72
  190. package/src/components/ui/avatar.tsx +0 -106
  191. package/src/components/ui/badge.tsx +0 -48
  192. package/src/components/ui/button.tsx +0 -53
  193. package/src/components/ui/card.tsx +0 -94
  194. package/src/components/ui/checkbox.tsx +0 -27
  195. package/src/components/ui/combobox.tsx +0 -290
  196. package/src/components/ui/dialog.tsx +0 -151
  197. package/src/components/ui/dropdown-menu.tsx +0 -254
  198. package/src/components/ui/field.tsx +0 -227
  199. package/src/components/ui/input-group.tsx +0 -149
  200. package/src/components/ui/input.tsx +0 -20
  201. package/src/components/ui/label.tsx +0 -18
  202. package/src/components/ui/popover.tsx +0 -88
  203. package/src/components/ui/scroll-area.tsx +0 -53
  204. package/src/components/ui/select.tsx +0 -192
  205. package/src/components/ui/separator.tsx +0 -23
  206. package/src/components/ui/sheet.tsx +0 -127
  207. package/src/components/ui/sidebar.tsx +0 -723
  208. package/src/components/ui/skeleton.tsx +0 -13
  209. package/src/components/ui/spinner.tsx +0 -10
  210. package/src/components/ui/switch.tsx +0 -32
  211. package/src/components/ui/table.tsx +0 -99
  212. package/src/components/ui/tabs.tsx +0 -82
  213. package/src/components/ui/textarea.tsx +0 -18
  214. package/src/components/ui/tooltip.tsx +0 -70
  215. package/src/config/component-registry.ts +0 -190
  216. package/src/config/index.ts +0 -1099
  217. package/src/hooks/README.md +0 -269
  218. package/src/hooks/admin-provider.tsx +0 -110
  219. package/src/hooks/index.ts +0 -41
  220. package/src/hooks/store.ts +0 -248
  221. package/src/hooks/use-auth.ts +0 -168
  222. package/src/hooks/use-collection-db.ts +0 -209
  223. package/src/hooks/use-collection.ts +0 -156
  224. package/src/hooks/use-global.ts +0 -69
  225. package/src/hooks/use-mobile.ts +0 -21
  226. package/src/lib/utils.ts +0 -6
  227. package/src/styles/index.css +0 -340
  228. package/src/utils/index.ts +0 -6
  229. package/src/views/auth/auth-layout.tsx +0 -77
  230. package/src/views/auth/forgot-password-form.tsx +0 -192
  231. package/src/views/auth/index.ts +0 -21
  232. package/src/views/auth/login-form.tsx +0 -229
  233. package/src/views/auth/reset-password-form.tsx +0 -232
  234. package/src/views/collection/auto-form-fields.tsx +0 -982
  235. package/src/views/collection/collection-form.tsx +0 -186
  236. package/src/views/collection/collection-list.tsx +0 -223
  237. package/src/views/collection/form-field.tsx +0 -52
  238. package/src/views/collection/index.ts +0 -15
  239. package/src/views/common/index.ts +0 -8
  240. package/src/views/common/locale-switcher.tsx +0 -45
  241. package/src/views/common/version-history.tsx +0 -406
  242. package/src/views/index.ts +0 -25
  243. package/src/views/layout/admin-layout.tsx +0 -117
  244. package/src/views/layout/admin-router.tsx +0 -206
  245. package/src/views/layout/admin-sidebar.tsx +0 -185
  246. package/src/views/layout/index.ts +0 -12
  247. package/tsconfig.json +0 -13
  248. package/tsconfig.tsbuildinfo +0 -1
  249. package/tsdown.config.ts +0 -13
  250. 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 that auto-generates from your CMS schema with extensive customization options.
5
+ A fully-featured admin interface using the `qa()` builder pattern for type-safe configuration.
6
6
 
7
7
  ## Features
8
8
 
9
- - **Zero Config** - Works out of the box with just a CMS client
10
- - **Config-Driven** - Customize everything via `defineAdminConfig`
11
- - **Type-Safe** - Full TypeScript inference from your CMS schema
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 slash commands, tables, images
14
- - **Relations** - Inline, picker, and create modes for related content
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 and audit logging
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 @questpie/cms @questpie/tanstack-query
24
- bun add @tanstack/react-query @tanstack/react-table
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
- ### Minimal Setup (One Component!)
29
+ ### 1. Create Admin Builder
30
30
 
31
- ```tsx
32
- import { AdminApp } from "@questpie/admin";
33
- import { cmsClient } from "./lib/cms-client";
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
- function App() {
42
- const location = useLocation();
43
- const navigate = useNavigate();
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
- return (
48
- <AdminApp
49
- client={cmsClient} // That's it! Everything else is auto-generated
50
- router={{
51
- LinkComponent: Link,
52
- currentPath: location.pathname,
53
- segments,
54
- navigate,
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
- ### With Custom Config
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
- import { AdminApp } from '@questpie/admin'
65
- import { cmsClient } from './lib/cms-client'
66
- import { adminConfig } from './configs/admin'
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
- <AdminApp
186
+ <AdminLayoutProvider
187
+ admin={adminInstance}
71
188
  client={cmsClient}
72
- config={adminConfig} // Optional customization
73
- router={{ ... }}
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
- ## 🎨 Configuration
200
+ ## Builder API
80
201
 
81
- Most UI is auto-generated from CMS collections. Field types and relation targets must be explicit in admin config.
202
+ ### Main Entry Point
82
203
 
83
204
  ```typescript
84
- import { defineAdminConfig } from "@questpie/admin/config";
85
- import type { AppCMS } from "./server/cms";
86
-
87
- export const adminConfig = defineAdminConfig<AppCMS>()({
88
- app: {
89
- brand: { name: "My Admin" },
90
- },
91
- collections: {
92
- posts: {
93
- label: "Blog Posts", // Override label
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
- ## What's Automatic
217
+ ### Collection Builder
150
218
 
151
- ### From CMS Schema
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
- - 🚫 Schema introspection (field types, required, defaults, relations) - not planned (explicit config)
238
+ ### Global Builder
154
239
 
155
- ### From Admin Config
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
- - ✅ **Sidebar navigation** - auto-generated from collections
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
- ## 🎯 Features
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
- ### Conditional Fields
277
+ **Typed Sidebar (Recommended)**
167
278
 
168
279
  ```typescript
169
- fields: {
170
- status: {
171
- type: "select",
172
- options: [...]
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
- // Show only when status is "cancelled"
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
- // Readonly unless draft
183
- publishedAt: {
184
- readOnly: (values) => values.status !== "draft"
185
- },
299
+ The `adminModule` provides these field types:
186
300
 
187
- // Dynamic options based on other fields
188
- category: {
189
- type: "select",
190
- options: (values) => {
191
- if (values.type === "tech") {
192
- return [{ label: "Frontend", value: "frontend" }, ...]
193
- }
194
- return [{ label: "Design", value: "design" }, ...]
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 & Organization
319
+ ### Sections
201
320
 
202
321
  ```typescript
203
- edit: {
322
+ .form(({ v, f }) => v.form({
204
323
  sections: [
205
324
  {
206
325
  title: "Basic Info",
207
- description: "Main appointment details",
208
- fields: ["customer", "barber", "service", "scheduledAt"],
326
+ description: "Main details",
327
+ fields: [f.title, f.slug],
209
328
  collapsible: true,
210
329
  defaultOpen: true,
211
330
  },
212
331
  {
213
- title: "Status",
214
- fields: ["status", "notes"],
332
+ title: "Content",
333
+ fields: [f.content],
215
334
  },
216
- ];
217
- }
335
+ ],
336
+ }))
218
337
  ```
219
338
 
220
- ### Advanced Layouts
221
-
222
- Columns, grids, and inline layouts are supported:
339
+ ### Columns Layout
223
340
 
224
341
  ```typescript
225
- edit: {
226
- sections: [
227
- {
228
- title: "Basic",
229
- layout: "columns",
230
- columns: 2,
231
- fields: ["title", "slug", "author", "category"],
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
- Sidebar layouts:
352
+ ### Grid Layout
253
353
 
254
354
  ```typescript
255
- edit: {
256
- layout: "with-sidebar",
257
- sections: [
258
- { title: "Content", fields: ["title", "content"] }
259
- ],
260
- sidebar: {
261
- position: "right",
262
- width: "320px",
263
- fields: ["status", "publishedAt"]
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
- edit: {
372
+ .form(({ v, f }) => v.form({
272
373
  tabs: [
273
374
  {
274
375
  id: "content",
275
376
  label: "Content",
276
- fields: ["title", "content"],
377
+ fields: [f.title, f.content],
277
378
  },
278
379
  {
279
380
  id: "meta",
280
381
  label: "Metadata",
281
- fields: ["seo", "tags", "category"],
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
- ### Relation Fields
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
- fields: {
314
- // Single relation (one-to-one) - explicit config
315
- barberId: {
316
- label: "Barber",
317
- relation: {
318
- targetCollection: "barbers", // Required target collection
319
- mode: "inline" // or "picker" or "create"
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
- **Features:**
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
- gallery: {
383
- embedded: {
384
- collection: "post_images",
385
- orderable: true, // Drag-and-drop reordering
386
- mode: "inline", // or "modal" or "drawer"
387
- rowLabel: (item) => item.caption || item.id
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
- ### Array Fields
426
+ ## Relation Fields
394
427
 
395
428
  ```typescript
396
- fields: {
397
- tags: {
398
- type: "array",
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
- array: {
401
- itemType: "text",
402
- orderable: true,
403
- maxItems: 10
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
- ### Version History & Audit Logging
419
-
420
- Track all changes to collection items with built-in versioning:
445
+ ## Hooks
421
446
 
422
447
  ```typescript
423
- collections: {
424
- posts: {
425
- // Enable versioning for this collection
426
- versioned: true,
427
-
428
- // Configure audit logging
429
- auditLog: {
430
- fields: ["title", "content", "status"], // Fields to track
431
- trackUser: true, // Track who made changes
432
- retentionDays: 365 // Keep logs for 1 year
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
- edit: {
436
- // Show version history in edit form
437
- showVersionHistory: true
438
- }
439
- }
440
- }
441
- ```
468
+ // Single item
469
+ const { data: post } = useCollectionItem("posts", id);
442
470
 
443
- **Features:**
471
+ // CRUD mutations
472
+ const createPost = useCollectionCreate("posts");
473
+ const updatePost = useCollectionUpdate("posts");
474
+ const deletePost = useCollectionDelete("posts");
444
475
 
445
- - **Version tracking** - Every change creates a new version
446
- - **Change history** - See what changed, when, and by whom
447
- - **Diff view** - Compare old vs new values
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
- ### Tree Views (Coming Soon)
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
- ```typescript
455
- list: {
456
- view: "tree", // Enable tree view
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
- ## 🧩 Component Registry
491
+ // Routes
492
+ const { routes, navigate } = useAdminRoutes();
493
+ navigate({ collection: "posts", action: "create" });
494
+ ```
466
495
 
467
- Override default field components:
496
+ ## Components
468
497
 
469
498
  ```typescript
470
- import { RichTextEditor } from './components/RichTextEditor'
471
- import { ImagePicker } from './components/ImagePicker'
472
-
473
- <AdminApp
474
- client={cmsClient}
475
- config={adminConfig}
476
- registry={{
477
- fields: {
478
- richText: RichTextEditor,
479
- image: ImagePicker
480
- }
481
- }}
482
- router={{ ... }}
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
- ## 📦 Package Exports
530
+ ## Package Exports
487
531
 
488
532
  ```typescript
489
- // Main component
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
- RelationSelect,
503
- RelationPicker,
504
- VersionHistory,
505
- } from "@questpie/admin/components";
546
+ } from "@questpie/admin/client";
506
547
 
507
- // Utilities
508
- import { cn } from "@questpie/admin/utils";
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
- ## 🎨 Styling
555
+ ## Styling
515
556
 
516
- Complete shadcn/ui setup with Tailwind CSS v4:
557
+ Built with Tailwind CSS v4 + shadcn/ui:
517
558
 
518
- - 53+ pre-built components (base-lyra style)
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
- ## 📚 Examples
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