@btst/plugin-form-builder 0.0.0-stage → 1.0.0-beta.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 (213) hide show
  1. package/LICENSE.md +17 -0
  2. package/dist/api/getters.cjs +154 -0
  3. package/dist/api/getters.mjs +145 -0
  4. package/dist/api/index.cjs +25 -0
  5. package/dist/api/index.d.cts +119 -0
  6. package/dist/api/index.d.mts +119 -0
  7. package/dist/api/index.d.ts +119 -0
  8. package/dist/api/index.mjs +6 -0
  9. package/dist/api/lifecycle-migrations.cjs +15 -0
  10. package/dist/api/lifecycle-migrations.mjs +13 -0
  11. package/dist/api/operations.cjs +1093 -0
  12. package/dist/api/operations.mjs +1082 -0
  13. package/dist/api/plugin.cjs +201 -0
  14. package/dist/api/plugin.mjs +192 -0
  15. package/dist/api/query-key-defs.cjs +43 -0
  16. package/dist/api/query-key-defs.mjs +39 -0
  17. package/dist/client/components/forms/form-renderer.cjs +193 -0
  18. package/dist/client/components/forms/form-renderer.mjs +191 -0
  19. package/dist/client/components/index.cjs +29 -0
  20. package/dist/client/components/index.d.cts +97 -0
  21. package/dist/client/components/index.d.mts +97 -0
  22. package/dist/client/components/index.d.ts +97 -0
  23. package/dist/client/components/index.mjs +18 -0
  24. package/dist/client/components/loading/form-builder-skeleton.cjs +32 -0
  25. package/dist/client/components/loading/form-builder-skeleton.mjs +30 -0
  26. package/dist/client/components/loading/form-list-skeleton.cjs +21 -0
  27. package/dist/client/components/loading/form-list-skeleton.mjs +19 -0
  28. package/dist/client/components/loading/submissions-skeleton.cjs +34 -0
  29. package/dist/client/components/loading/submissions-skeleton.mjs +32 -0
  30. package/dist/client/components/pages/404-page.cjs +28 -0
  31. package/dist/client/components/pages/404-page.mjs +26 -0
  32. package/dist/client/components/pages/form-builder-page.cjs +77 -0
  33. package/dist/client/components/pages/form-builder-page.internal.cjs +217 -0
  34. package/dist/client/components/pages/form-builder-page.internal.mjs +215 -0
  35. package/dist/client/components/pages/form-builder-page.mjs +75 -0
  36. package/dist/client/components/pages/form-list-page.cjs +44 -0
  37. package/dist/client/components/pages/form-list-page.internal.cjs +282 -0
  38. package/dist/client/components/pages/form-list-page.internal.mjs +280 -0
  39. package/dist/client/components/pages/form-list-page.mjs +42 -0
  40. package/dist/client/components/pages/submissions-page.cjs +64 -0
  41. package/dist/client/components/pages/submissions-page.internal.cjs +255 -0
  42. package/dist/client/components/pages/submissions-page.internal.mjs +253 -0
  43. package/dist/client/components/pages/submissions-page.mjs +62 -0
  44. package/dist/client/components/shared/default-error.cjs +17 -0
  45. package/dist/client/components/shared/default-error.mjs +15 -0
  46. package/dist/client/components/shared/empty-state.cjs +16 -0
  47. package/dist/client/components/shared/empty-state.mjs +14 -0
  48. package/dist/client/components/shared/page-wrapper.cjs +29 -0
  49. package/dist/client/components/shared/page-wrapper.mjs +27 -0
  50. package/dist/client/components/shared/pagination.cjs +49 -0
  51. package/dist/client/components/shared/pagination.mjs +47 -0
  52. package/dist/client/constants.cjs +5 -0
  53. package/dist/client/constants.mjs +3 -0
  54. package/dist/client/hooks/form-builder-hooks.cjs +203 -0
  55. package/dist/client/hooks/form-builder-hooks.mjs +186 -0
  56. package/dist/client/hooks/form-builder-resource.cjs +13 -0
  57. package/dist/client/hooks/form-builder-resource.mjs +11 -0
  58. package/dist/client/hooks/index.cjs +22 -0
  59. package/dist/client/hooks/index.d.cts +378 -0
  60. package/dist/client/hooks/index.d.mts +378 -0
  61. package/dist/client/hooks/index.d.ts +378 -0
  62. package/dist/client/hooks/index.mjs +1 -0
  63. package/dist/client/index.cjs +13 -0
  64. package/dist/client/index.d.cts +410 -0
  65. package/dist/client/index.d.mts +410 -0
  66. package/dist/client/index.d.ts +410 -0
  67. package/dist/client/index.mjs +2 -0
  68. package/dist/client/plugin.cjs +311 -0
  69. package/dist/client/plugin.mjs +309 -0
  70. package/dist/client.css +3 -0
  71. package/dist/db.cjs +99 -0
  72. package/dist/db.mjs +97 -0
  73. package/dist/permissions.cjs +95 -0
  74. package/dist/permissions.d.cts +94 -0
  75. package/dist/permissions.d.mts +94 -0
  76. package/dist/permissions.d.ts +94 -0
  77. package/dist/permissions.mjs +93 -0
  78. package/dist/query-keys.cjs +140 -0
  79. package/dist/query-keys.d.cts +392 -0
  80. package/dist/query-keys.d.mts +392 -0
  81. package/dist/query-keys.d.ts +392 -0
  82. package/dist/query-keys.mjs +137 -0
  83. package/dist/schemas.cjs +91 -0
  84. package/dist/schemas.mjs +82 -0
  85. package/dist/shared/plugin-form-builder.BZYGyFgT.d.mts +351 -0
  86. package/dist/shared/plugin-form-builder.Bbl9NufB.d.cts +408 -0
  87. package/dist/shared/plugin-form-builder.CSP5HR2O.d.ts +408 -0
  88. package/dist/shared/plugin-form-builder.Cmk083dA.d.cts +351 -0
  89. package/dist/shared/plugin-form-builder.D258Qpb8.d.ts +351 -0
  90. package/dist/shared/plugin-form-builder.DWrQ3lRX.d.cts +118 -0
  91. package/dist/shared/plugin-form-builder.DWrQ3lRX.d.mts +118 -0
  92. package/dist/shared/plugin-form-builder.DWrQ3lRX.d.ts +118 -0
  93. package/dist/shared/plugin-form-builder.lp98rFNl.d.mts +408 -0
  94. package/dist/style.css +6 -0
  95. package/dist/types.cjs +2 -0
  96. package/dist/types.d.cts +6 -0
  97. package/dist/types.d.mts +6 -0
  98. package/dist/types.d.ts +6 -0
  99. package/dist/types.mjs +1 -0
  100. package/dist/ui/src/components/accordion.cjs +81 -0
  101. package/dist/ui/src/components/accordion.mjs +62 -0
  102. package/dist/ui/src/components/alert-dialog.cjs +157 -0
  103. package/dist/ui/src/components/alert-dialog.mjs +132 -0
  104. package/dist/ui/src/components/auto-form/common/label.cjs +18 -0
  105. package/dist/ui/src/components/auto-form/common/label.mjs +16 -0
  106. package/dist/ui/src/components/auto-form/common/tooltip.cjs +9 -0
  107. package/dist/ui/src/components/auto-form/common/tooltip.mjs +7 -0
  108. package/dist/ui/src/components/auto-form/config.cjs +40 -0
  109. package/dist/ui/src/components/auto-form/config.mjs +37 -0
  110. package/dist/ui/src/components/auto-form/dependencies.cjs +48 -0
  111. package/dist/ui/src/components/auto-form/dependencies.mjs +46 -0
  112. package/dist/ui/src/components/auto-form/fields/array.cjs +266 -0
  113. package/dist/ui/src/components/auto-form/fields/array.mjs +264 -0
  114. package/dist/ui/src/components/auto-form/fields/checkbox.cjs +38 -0
  115. package/dist/ui/src/components/auto-form/fields/checkbox.mjs +36 -0
  116. package/dist/ui/src/components/auto-form/fields/date.cjs +67 -0
  117. package/dist/ui/src/components/auto-form/fields/date.mjs +65 -0
  118. package/dist/ui/src/components/auto-form/fields/enum.cjs +73 -0
  119. package/dist/ui/src/components/auto-form/fields/enum.mjs +71 -0
  120. package/dist/ui/src/components/auto-form/fields/input.cjs +32 -0
  121. package/dist/ui/src/components/auto-form/fields/input.mjs +30 -0
  122. package/dist/ui/src/components/auto-form/fields/number.cjs +31 -0
  123. package/dist/ui/src/components/auto-form/fields/number.mjs +29 -0
  124. package/dist/ui/src/components/auto-form/fields/object.cjs +233 -0
  125. package/dist/ui/src/components/auto-form/fields/object.mjs +217 -0
  126. package/dist/ui/src/components/auto-form/fields/radio-group.cjs +67 -0
  127. package/dist/ui/src/components/auto-form/fields/radio-group.mjs +65 -0
  128. package/dist/ui/src/components/auto-form/fields/switch.cjs +38 -0
  129. package/dist/ui/src/components/auto-form/fields/switch.mjs +36 -0
  130. package/dist/ui/src/components/auto-form/fields/textarea.cjs +31 -0
  131. package/dist/ui/src/components/auto-form/fields/textarea.mjs +29 -0
  132. package/dist/ui/src/components/auto-form/helpers.cjs +270 -0
  133. package/dist/ui/src/components/auto-form/helpers.mjs +259 -0
  134. package/dist/ui/src/components/auto-form/index.cjs +92 -0
  135. package/dist/ui/src/components/auto-form/index.mjs +86 -0
  136. package/dist/ui/src/components/auto-form/stepped-auto-form.cjs +374 -0
  137. package/dist/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
  138. package/dist/ui/src/components/auto-form/types.cjs +11 -0
  139. package/dist/ui/src/components/auto-form/types.mjs +9 -0
  140. package/dist/ui/src/components/button.cjs +56 -0
  141. package/dist/ui/src/components/button.mjs +53 -0
  142. package/dist/ui/src/components/calendar.cjs +207 -0
  143. package/dist/ui/src/components/calendar.mjs +190 -0
  144. package/dist/ui/src/components/checkbox.cjs +48 -0
  145. package/dist/ui/src/components/checkbox.mjs +32 -0
  146. package/dist/ui/src/components/date-picker.cjs +41 -0
  147. package/dist/ui/src/components/date-picker.mjs +39 -0
  148. package/dist/ui/src/components/dialog.cjs +141 -0
  149. package/dist/ui/src/components/dialog.mjs +118 -0
  150. package/dist/ui/src/components/dropdown-menu.cjs +81 -0
  151. package/dist/ui/src/components/dropdown-menu.mjs +62 -0
  152. package/dist/ui/src/components/form-builder/canvas.cjs +111 -0
  153. package/dist/ui/src/components/form-builder/canvas.mjs +109 -0
  154. package/dist/ui/src/components/form-builder/components/index.cjs +571 -0
  155. package/dist/ui/src/components/form-builder/components/index.mjs +554 -0
  156. package/dist/ui/src/components/form-builder/edit-field-dialog.cjs +131 -0
  157. package/dist/ui/src/components/form-builder/edit-field-dialog.mjs +129 -0
  158. package/dist/ui/src/components/form-builder/form-preview.cjs +73 -0
  159. package/dist/ui/src/components/form-builder/form-preview.mjs +71 -0
  160. package/dist/ui/src/components/form-builder/index.cjs +350 -0
  161. package/dist/ui/src/components/form-builder/index.mjs +344 -0
  162. package/dist/ui/src/components/form-builder/nested-field-editor-dialog.cjs +263 -0
  163. package/dist/ui/src/components/form-builder/nested-field-editor-dialog.mjs +261 -0
  164. package/dist/ui/src/components/form-builder/palette.cjs +52 -0
  165. package/dist/ui/src/components/form-builder/palette.mjs +49 -0
  166. package/dist/ui/src/components/form-builder/schema-utils.cjs +128 -0
  167. package/dist/ui/src/components/form-builder/schema-utils.mjs +122 -0
  168. package/dist/ui/src/components/form-builder/sortable-field.cjs +151 -0
  169. package/dist/ui/src/components/form-builder/sortable-field.mjs +148 -0
  170. package/dist/ui/src/components/form-builder/step-tabs.cjs +180 -0
  171. package/dist/ui/src/components/form-builder/step-tabs.mjs +178 -0
  172. package/dist/ui/src/components/form-builder/types.cjs +7 -0
  173. package/dist/ui/src/components/form-builder/types.mjs +5 -0
  174. package/dist/ui/src/components/form-builder/validation-schemas.cjs +67 -0
  175. package/dist/ui/src/components/form-builder/validation-schemas.mjs +56 -0
  176. package/dist/ui/src/components/form.cjs +120 -0
  177. package/dist/ui/src/components/form.mjs +98 -0
  178. package/dist/ui/src/components/input.cjs +23 -0
  179. package/dist/ui/src/components/input.mjs +21 -0
  180. package/dist/ui/src/components/label.cjs +39 -0
  181. package/dist/ui/src/components/label.mjs +23 -0
  182. package/dist/ui/src/components/page-layout.cjs +25 -0
  183. package/dist/ui/src/components/page-layout.mjs +23 -0
  184. package/dist/ui/src/components/page-wrapper.cjs +20 -0
  185. package/dist/ui/src/components/page-wrapper.mjs +18 -0
  186. package/dist/ui/src/components/popover.cjs +60 -0
  187. package/dist/ui/src/components/popover.mjs +42 -0
  188. package/dist/ui/src/components/radio-group.cjs +62 -0
  189. package/dist/ui/src/components/radio-group.mjs +45 -0
  190. package/dist/ui/src/components/select.cjs +161 -0
  191. package/dist/ui/src/components/select.mjs +139 -0
  192. package/dist/ui/src/components/separator.cjs +43 -0
  193. package/dist/ui/src/components/separator.mjs +27 -0
  194. package/dist/ui/src/components/skeleton.cjs +17 -0
  195. package/dist/ui/src/components/skeleton.mjs +15 -0
  196. package/dist/ui/src/components/stack-attribution.cjs +24 -0
  197. package/dist/ui/src/components/stack-attribution.mjs +22 -0
  198. package/dist/ui/src/components/switch.cjs +48 -0
  199. package/dist/ui/src/components/switch.mjs +32 -0
  200. package/dist/ui/src/components/table.cjs +89 -0
  201. package/dist/ui/src/components/table.mjs +82 -0
  202. package/dist/ui/src/components/tabs.cjs +84 -0
  203. package/dist/ui/src/components/tabs.mjs +65 -0
  204. package/dist/ui/src/components/textarea.cjs +20 -0
  205. package/dist/ui/src/components/textarea.mjs +18 -0
  206. package/dist/ui/src/lib/schema-converter.cjs +133 -0
  207. package/dist/ui/src/lib/schema-converter.mjs +127 -0
  208. package/dist/ui/src/lib/utils.cjs +10 -0
  209. package/dist/ui/src/lib/utils.mjs +8 -0
  210. package/dist/utils.cjs +41 -0
  211. package/dist/utils.mjs +32 -0
  212. package/package.json +175 -3
  213. package/README.md +0 -3
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+
3
+ const api = require('@btst/stack/plugins/api');
4
+ const db = require('../db.cjs');
5
+ const schemas = require('../schemas.cjs');
6
+ const getters = require('./getters.cjs');
7
+ const operations = require('./operations.cjs');
8
+ const queryKeyDefs = require('./query-key-defs.cjs');
9
+
10
+ function createFormBuilderPrefetchForRoute(adapter) {
11
+ return async function prefetchForRoute(key, queryClient, params) {
12
+ switch (key) {
13
+ case "formList": {
14
+ const result = await getters.getAllForms(adapter, { limit: 20, offset: 0 });
15
+ queryClient.setQueryData(
16
+ queryKeyDefs.FORM_QUERY_KEYS.formsList({ limit: 20, offset: 0 }),
17
+ {
18
+ pages: [
19
+ {
20
+ items: result.items,
21
+ total: result.total,
22
+ limit: result.limit ?? 20,
23
+ offset: result.offset ?? 0
24
+ }
25
+ ],
26
+ pageParams: [0]
27
+ }
28
+ );
29
+ break;
30
+ }
31
+ case "editForm": {
32
+ const id = params?.id ?? "";
33
+ if (id) {
34
+ queryClient.setQueryData(
35
+ queryKeyDefs.FORM_QUERY_KEYS.formForUpdate(id),
36
+ await getters.getFormById(adapter, id)
37
+ );
38
+ }
39
+ break;
40
+ }
41
+ case "submissions": {
42
+ const id = params?.id ?? "";
43
+ if (id) {
44
+ const [form, result] = await Promise.all([
45
+ getters.getFormById(adapter, id),
46
+ getters.getFormSubmissions(adapter, id, { limit: 20, offset: 0 })
47
+ ]);
48
+ const formContext = form ? {
49
+ id: form.id,
50
+ name: form.name,
51
+ ...form.createdBy ? { createdBy: form.createdBy } : {}
52
+ } : null;
53
+ queryClient.setQueryData(
54
+ queryKeyDefs.FORM_QUERY_KEYS.submissionsList({
55
+ formId: id,
56
+ limit: 20,
57
+ offset: 0
58
+ }),
59
+ {
60
+ pages: [
61
+ {
62
+ form: formContext,
63
+ items: result.items.map(getters.serializeFormSubmissionSummary),
64
+ total: result.total,
65
+ limit: result.limit ?? 20,
66
+ offset: result.offset ?? 0
67
+ }
68
+ ],
69
+ pageParams: [0]
70
+ }
71
+ );
72
+ }
73
+ break;
74
+ }
75
+ }
76
+ };
77
+ }
78
+ const formBuilderBackendPlugin = (config = {}) => api.defineBackendPlugin({
79
+ id: "formBuilder",
80
+ dbPlugin: db.formBuilderSchema,
81
+ operations: (adapter) => operations.createFormBuilderOperations(adapter, config.hooks),
82
+ /** Lower-level SSG helper that intentionally bypasses auth and hooks. */
83
+ raw: (adapter) => ({
84
+ prefetchForRoute: createFormBuilderPrefetchForRoute(adapter)
85
+ }),
86
+ routes: (_adapter, _context, operations$1) => {
87
+ const listForms = api.createEndpoint(
88
+ "/forms",
89
+ { method: "GET", query: schemas.listFormsQuerySchema, requireRequest: true },
90
+ operations$1.listForms.route((ctx) => ctx.query)
91
+ );
92
+ const getFormBySlugEndpoint = api.createEndpoint(
93
+ "/forms/:slug",
94
+ {
95
+ method: "GET",
96
+ params: operations.GetFormBySlugOperationInputSchema,
97
+ requireRequest: true
98
+ },
99
+ operations$1.getFormBySlug.route((ctx) => ctx.params)
100
+ );
101
+ const getFormByIdEndpoint = api.createEndpoint(
102
+ "/forms/id/:id",
103
+ {
104
+ method: "GET",
105
+ params: operations.GetFormByIdOperationInputSchema,
106
+ requireRequest: true
107
+ },
108
+ operations$1.getFormById.route((ctx) => ctx.params)
109
+ );
110
+ const getFormForUpdateEndpoint = api.createEndpoint(
111
+ "/forms/id/:id/edit",
112
+ {
113
+ method: "GET",
114
+ params: operations.GetFormForUpdateOperationInputSchema,
115
+ requireRequest: true
116
+ },
117
+ operations$1.getFormForUpdate.route((ctx) => ctx.params)
118
+ );
119
+ const createForm = api.createEndpoint(
120
+ "/forms",
121
+ { method: "POST", body: schemas.createFormSchema, requireRequest: true },
122
+ operations$1.createForm.route((ctx) => ctx.body)
123
+ );
124
+ const updateForm = api.createEndpoint(
125
+ "/forms/:id",
126
+ { method: "PUT", body: schemas.updateFormSchema, requireRequest: true },
127
+ operations$1.updateForm.route((ctx) => ({
128
+ id: ctx.params.id,
129
+ data: ctx.body
130
+ }))
131
+ );
132
+ const deleteForm = api.createEndpoint(
133
+ "/forms/:id",
134
+ { method: "DELETE", requireRequest: true },
135
+ operations$1.deleteForm.route((ctx) => ({ id: ctx.params.id }))
136
+ );
137
+ const submitForm = api.createEndpoint(
138
+ "/forms/:slug/submit",
139
+ {
140
+ method: "POST",
141
+ body: operations.SubmitFormOperationInputSchema.pick({ data: true }),
142
+ requireRequest: true
143
+ },
144
+ operations$1.submitForm.route((ctx) => ({
145
+ slug: ctx.params.slug,
146
+ data: ctx.body.data
147
+ }))
148
+ );
149
+ const listSubmissions = api.createEndpoint(
150
+ "/forms/:formId/submissions",
151
+ {
152
+ method: "GET",
153
+ query: schemas.listSubmissionsQuerySchema,
154
+ requireRequest: true
155
+ },
156
+ operations$1.listSubmissions.route((ctx) => ({
157
+ formId: ctx.params.formId,
158
+ query: ctx.query
159
+ }))
160
+ );
161
+ const getSubmission = api.createEndpoint(
162
+ "/forms/:formId/submissions/:subId",
163
+ { method: "GET", requireRequest: true },
164
+ operations$1.getSubmission.route((ctx) => ({
165
+ formId: ctx.params.formId,
166
+ submissionId: ctx.params.subId
167
+ }))
168
+ );
169
+ const deleteSubmission = api.createEndpoint(
170
+ "/forms/:formId/submissions/:subId",
171
+ { method: "DELETE", requireRequest: true },
172
+ operations$1.deleteSubmission.route((ctx) => ({
173
+ formId: ctx.params.formId,
174
+ submissionId: ctx.params.subId
175
+ }))
176
+ );
177
+ return {
178
+ listForms,
179
+ getFormBySlug: getFormBySlugEndpoint,
180
+ getFormById: getFormByIdEndpoint,
181
+ getFormForUpdate: getFormForUpdateEndpoint,
182
+ createForm,
183
+ updateForm,
184
+ deleteForm,
185
+ submitForm,
186
+ listSubmissions,
187
+ getSubmission,
188
+ deleteSubmission
189
+ };
190
+ }
191
+ });
192
+
193
+ exports.DeleteFormOperationInputSchema = operations.DeleteFormOperationInputSchema;
194
+ exports.GetFormByIdOperationInputSchema = operations.GetFormByIdOperationInputSchema;
195
+ exports.GetFormBySlugOperationInputSchema = operations.GetFormBySlugOperationInputSchema;
196
+ exports.GetFormForUpdateOperationInputSchema = operations.GetFormForUpdateOperationInputSchema;
197
+ exports.GetSubmissionOperationInputSchema = operations.GetSubmissionOperationInputSchema;
198
+ exports.ListSubmissionsOperationInputSchema = operations.ListSubmissionsOperationInputSchema;
199
+ exports.SubmitFormOperationInputSchema = operations.SubmitFormOperationInputSchema;
200
+ exports.UpdateFormOperationInputSchema = operations.UpdateFormOperationInputSchema;
201
+ exports.formBuilderBackendPlugin = formBuilderBackendPlugin;
@@ -0,0 +1,192 @@
1
+ import { defineBackendPlugin, createEndpoint } from '@btst/stack/plugins/api';
2
+ import { formBuilderSchema } from '../db.mjs';
3
+ import { listFormsQuerySchema, createFormSchema, updateFormSchema, listSubmissionsQuerySchema } from '../schemas.mjs';
4
+ import { getFormById, getFormSubmissions, serializeFormSubmissionSummary, getAllForms } from './getters.mjs';
5
+ import { GetFormBySlugOperationInputSchema, GetFormByIdOperationInputSchema, GetFormForUpdateOperationInputSchema, SubmitFormOperationInputSchema, createFormBuilderOperations } from './operations.mjs';
6
+ export { DeleteFormOperationInputSchema, GetSubmissionOperationInputSchema, ListSubmissionsOperationInputSchema, UpdateFormOperationInputSchema } from './operations.mjs';
7
+ import { FORM_QUERY_KEYS } from './query-key-defs.mjs';
8
+
9
+ function createFormBuilderPrefetchForRoute(adapter) {
10
+ return async function prefetchForRoute(key, queryClient, params) {
11
+ switch (key) {
12
+ case "formList": {
13
+ const result = await getAllForms(adapter, { limit: 20, offset: 0 });
14
+ queryClient.setQueryData(
15
+ FORM_QUERY_KEYS.formsList({ limit: 20, offset: 0 }),
16
+ {
17
+ pages: [
18
+ {
19
+ items: result.items,
20
+ total: result.total,
21
+ limit: result.limit ?? 20,
22
+ offset: result.offset ?? 0
23
+ }
24
+ ],
25
+ pageParams: [0]
26
+ }
27
+ );
28
+ break;
29
+ }
30
+ case "editForm": {
31
+ const id = params?.id ?? "";
32
+ if (id) {
33
+ queryClient.setQueryData(
34
+ FORM_QUERY_KEYS.formForUpdate(id),
35
+ await getFormById(adapter, id)
36
+ );
37
+ }
38
+ break;
39
+ }
40
+ case "submissions": {
41
+ const id = params?.id ?? "";
42
+ if (id) {
43
+ const [form, result] = await Promise.all([
44
+ getFormById(adapter, id),
45
+ getFormSubmissions(adapter, id, { limit: 20, offset: 0 })
46
+ ]);
47
+ const formContext = form ? {
48
+ id: form.id,
49
+ name: form.name,
50
+ ...form.createdBy ? { createdBy: form.createdBy } : {}
51
+ } : null;
52
+ queryClient.setQueryData(
53
+ FORM_QUERY_KEYS.submissionsList({
54
+ formId: id,
55
+ limit: 20,
56
+ offset: 0
57
+ }),
58
+ {
59
+ pages: [
60
+ {
61
+ form: formContext,
62
+ items: result.items.map(serializeFormSubmissionSummary),
63
+ total: result.total,
64
+ limit: result.limit ?? 20,
65
+ offset: result.offset ?? 0
66
+ }
67
+ ],
68
+ pageParams: [0]
69
+ }
70
+ );
71
+ }
72
+ break;
73
+ }
74
+ }
75
+ };
76
+ }
77
+ const formBuilderBackendPlugin = (config = {}) => defineBackendPlugin({
78
+ id: "formBuilder",
79
+ dbPlugin: formBuilderSchema,
80
+ operations: (adapter) => createFormBuilderOperations(adapter, config.hooks),
81
+ /** Lower-level SSG helper that intentionally bypasses auth and hooks. */
82
+ raw: (adapter) => ({
83
+ prefetchForRoute: createFormBuilderPrefetchForRoute(adapter)
84
+ }),
85
+ routes: (_adapter, _context, operations) => {
86
+ const listForms = createEndpoint(
87
+ "/forms",
88
+ { method: "GET", query: listFormsQuerySchema, requireRequest: true },
89
+ operations.listForms.route((ctx) => ctx.query)
90
+ );
91
+ const getFormBySlugEndpoint = createEndpoint(
92
+ "/forms/:slug",
93
+ {
94
+ method: "GET",
95
+ params: GetFormBySlugOperationInputSchema,
96
+ requireRequest: true
97
+ },
98
+ operations.getFormBySlug.route((ctx) => ctx.params)
99
+ );
100
+ const getFormByIdEndpoint = createEndpoint(
101
+ "/forms/id/:id",
102
+ {
103
+ method: "GET",
104
+ params: GetFormByIdOperationInputSchema,
105
+ requireRequest: true
106
+ },
107
+ operations.getFormById.route((ctx) => ctx.params)
108
+ );
109
+ const getFormForUpdateEndpoint = createEndpoint(
110
+ "/forms/id/:id/edit",
111
+ {
112
+ method: "GET",
113
+ params: GetFormForUpdateOperationInputSchema,
114
+ requireRequest: true
115
+ },
116
+ operations.getFormForUpdate.route((ctx) => ctx.params)
117
+ );
118
+ const createForm = createEndpoint(
119
+ "/forms",
120
+ { method: "POST", body: createFormSchema, requireRequest: true },
121
+ operations.createForm.route((ctx) => ctx.body)
122
+ );
123
+ const updateForm = createEndpoint(
124
+ "/forms/:id",
125
+ { method: "PUT", body: updateFormSchema, requireRequest: true },
126
+ operations.updateForm.route((ctx) => ({
127
+ id: ctx.params.id,
128
+ data: ctx.body
129
+ }))
130
+ );
131
+ const deleteForm = createEndpoint(
132
+ "/forms/:id",
133
+ { method: "DELETE", requireRequest: true },
134
+ operations.deleteForm.route((ctx) => ({ id: ctx.params.id }))
135
+ );
136
+ const submitForm = createEndpoint(
137
+ "/forms/:slug/submit",
138
+ {
139
+ method: "POST",
140
+ body: SubmitFormOperationInputSchema.pick({ data: true }),
141
+ requireRequest: true
142
+ },
143
+ operations.submitForm.route((ctx) => ({
144
+ slug: ctx.params.slug,
145
+ data: ctx.body.data
146
+ }))
147
+ );
148
+ const listSubmissions = createEndpoint(
149
+ "/forms/:formId/submissions",
150
+ {
151
+ method: "GET",
152
+ query: listSubmissionsQuerySchema,
153
+ requireRequest: true
154
+ },
155
+ operations.listSubmissions.route((ctx) => ({
156
+ formId: ctx.params.formId,
157
+ query: ctx.query
158
+ }))
159
+ );
160
+ const getSubmission = createEndpoint(
161
+ "/forms/:formId/submissions/:subId",
162
+ { method: "GET", requireRequest: true },
163
+ operations.getSubmission.route((ctx) => ({
164
+ formId: ctx.params.formId,
165
+ submissionId: ctx.params.subId
166
+ }))
167
+ );
168
+ const deleteSubmission = createEndpoint(
169
+ "/forms/:formId/submissions/:subId",
170
+ { method: "DELETE", requireRequest: true },
171
+ operations.deleteSubmission.route((ctx) => ({
172
+ formId: ctx.params.formId,
173
+ submissionId: ctx.params.subId
174
+ }))
175
+ );
176
+ return {
177
+ listForms,
178
+ getFormBySlug: getFormBySlugEndpoint,
179
+ getFormById: getFormByIdEndpoint,
180
+ getFormForUpdate: getFormForUpdateEndpoint,
181
+ createForm,
182
+ updateForm,
183
+ deleteForm,
184
+ submitForm,
185
+ listSubmissions,
186
+ getSubmission,
187
+ deleteSubmission
188
+ };
189
+ }
190
+ });
191
+
192
+ export { GetFormByIdOperationInputSchema, GetFormBySlugOperationInputSchema, GetFormForUpdateOperationInputSchema, SubmitFormOperationInputSchema, formBuilderBackendPlugin };
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ function formsListDiscriminator(params) {
4
+ return {
5
+ status: params?.status,
6
+ limit: params?.limit ?? 20,
7
+ offset: params?.offset ?? 0,
8
+ search: params?.search !== void 0 && params.search.trim() === "" ? void 0 : params?.search
9
+ };
10
+ }
11
+ function submissionsListDiscriminator(params) {
12
+ return {
13
+ formId: params.formId,
14
+ limit: params.limit ?? 20,
15
+ offset: params.offset ?? 0
16
+ };
17
+ }
18
+ const FORM_QUERY_KEYS = {
19
+ /**
20
+ * Key for forms.list(params) query.
21
+ * Full key: ["forms", "list", "list", { status, limit, offset }]
22
+ */
23
+ formsList: (params) => ["forms", "list", "list", formsListDiscriminator(params)],
24
+ /**
25
+ * Key for forms.byId(id) query.
26
+ * Full key: ["forms", "byId", "byId", id]
27
+ */
28
+ formById: (id) => ["forms", "byId", "byId", id],
29
+ /**
30
+ * Key for forms.forUpdate(id) query.
31
+ * Full key: ["forms", "forUpdate", "forUpdate", id]
32
+ */
33
+ formForUpdate: (id) => ["forms", "forUpdate", "forUpdate", id],
34
+ /**
35
+ * Key for formSubmissions.list(params) query.
36
+ * Full key: ["formSubmissions", "list", { formId, limit, offset }]
37
+ */
38
+ submissionsList: (params) => ["formSubmissions", "list", submissionsListDiscriminator(params)]
39
+ };
40
+
41
+ exports.FORM_QUERY_KEYS = FORM_QUERY_KEYS;
42
+ exports.formsListDiscriminator = formsListDiscriminator;
43
+ exports.submissionsListDiscriminator = submissionsListDiscriminator;
@@ -0,0 +1,39 @@
1
+ function formsListDiscriminator(params) {
2
+ return {
3
+ status: params?.status,
4
+ limit: params?.limit ?? 20,
5
+ offset: params?.offset ?? 0,
6
+ search: params?.search !== void 0 && params.search.trim() === "" ? void 0 : params?.search
7
+ };
8
+ }
9
+ function submissionsListDiscriminator(params) {
10
+ return {
11
+ formId: params.formId,
12
+ limit: params.limit ?? 20,
13
+ offset: params.offset ?? 0
14
+ };
15
+ }
16
+ const FORM_QUERY_KEYS = {
17
+ /**
18
+ * Key for forms.list(params) query.
19
+ * Full key: ["forms", "list", "list", { status, limit, offset }]
20
+ */
21
+ formsList: (params) => ["forms", "list", "list", formsListDiscriminator(params)],
22
+ /**
23
+ * Key for forms.byId(id) query.
24
+ * Full key: ["forms", "byId", "byId", id]
25
+ */
26
+ formById: (id) => ["forms", "byId", "byId", id],
27
+ /**
28
+ * Key for forms.forUpdate(id) query.
29
+ * Full key: ["forms", "forUpdate", "forUpdate", id]
30
+ */
31
+ formForUpdate: (id) => ["forms", "forUpdate", "forUpdate", id],
32
+ /**
33
+ * Key for formSubmissions.list(params) query.
34
+ * Full key: ["formSubmissions", "list", { formId, limit, offset }]
35
+ */
36
+ submissionsList: (params) => ["formSubmissions", "list", submissionsListDiscriminator(params)]
37
+ };
38
+
39
+ export { FORM_QUERY_KEYS, formsListDiscriminator, submissionsListDiscriminator };
@@ -0,0 +1,193 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ const jsxRuntime = require('react/jsx-runtime');
5
+ const React = require('react');
6
+ const context = require('@btst/stack/context');
7
+ const steppedAutoForm = require('../../../ui/src/components/auto-form/stepped-auto-form.cjs');
8
+ const helpers = require('../../../ui/src/components/auto-form/helpers.cjs');
9
+ const schemaConverter = require('../../../ui/src/lib/schema-converter.cjs');
10
+ const skeleton = require('../../../ui/src/components/skeleton.cjs');
11
+ const lucideReact = require('lucide-react');
12
+ const formBuilderHooks = require('../../hooks/form-builder-hooks.cjs');
13
+ const permissions = require('../../../permissions.cjs');
14
+ const constants = require('../../constants.cjs');
15
+ const utils = require('../../../utils.cjs');
16
+
17
+ function DefaultLoadingComponent() {
18
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
19
+ /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-10 w-full" }),
20
+ /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-10 w-full" }),
21
+ /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-10 w-full" }),
22
+ /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-10 w-32" })
23
+ ] });
24
+ }
25
+ function DefaultErrorComponent({ error }) {
26
+ const t = context.useTranslate();
27
+ const { localization } = context.usePluginOverrides(
28
+ constants.FORM_BUILDER_PLUGIN_ID
29
+ );
30
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
31
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-destructive/10 p-3 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-6 w-6 text-destructive" }) }),
32
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-medium text-foreground mb-2", children: localization?.FORM_BUILDER_RENDERER_LOAD_FAILED ?? t("formBuilder.renderer.loadFailed", "Failed to load form") }),
33
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: error.message || (localization?.FORM_BUILDER_RENDERER_UNEXPECTED_ERROR ?? t(
34
+ "formBuilder.renderer.unexpectedError",
35
+ "An unexpected error occurred"
36
+ )) })
37
+ ] });
38
+ }
39
+ function DefaultSuccessComponent({ message }) {
40
+ const t = context.useTranslate();
41
+ const { localization } = context.usePluginOverrides(
42
+ constants.FORM_BUILDER_PLUGIN_ID
43
+ );
44
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
45
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-green-100 dark:bg-green-900 p-3 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-6 w-6 text-green-600 dark:text-green-400" }) }),
46
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-medium text-foreground mb-2", children: localization?.FORM_BUILDER_RENDERER_SUBMITTED_TITLE ?? t("formBuilder.renderer.submittedTitle", "Form Submitted") }),
47
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: message })
48
+ ] });
49
+ }
50
+ function FormRenderer({
51
+ slug,
52
+ onSuccess,
53
+ onError,
54
+ fieldComponents: propFieldComponents,
55
+ successMessage: propSuccessMessage,
56
+ submitButtonText,
57
+ LoadingComponent = DefaultLoadingComponent,
58
+ ErrorComponent = DefaultErrorComponent,
59
+ className
60
+ }) {
61
+ const t = context.useTranslate();
62
+ const {
63
+ fieldComponents: overrideFieldComponents,
64
+ localization,
65
+ classNames
66
+ } = context.usePluginOverrides(constants.FORM_BUILDER_PLUGIN_ID);
67
+ const formClassName = utils.cn(className, classNames?.form);
68
+ const { form, isLoading, error } = formBuilderHooks.useFormBySlug(slug);
69
+ const submitMutation = formBuilderHooks.useSubmitForm(slug);
70
+ const [submitted, setSubmitted] = React.useState(false);
71
+ const [finalSuccessMessage, setFinalSuccessMessage] = React.useState(
72
+ null
73
+ );
74
+ const mergedFieldComponents = React.useMemo(
75
+ () => ({
76
+ ...overrideFieldComponents,
77
+ ...propFieldComponents
78
+ }),
79
+ [overrideFieldComponents, propFieldComponents]
80
+ );
81
+ const { zodSchema, fieldConfig } = React.useMemo(() => {
82
+ if (!form?.schema) {
83
+ return { zodSchema: null, fieldConfig: {} };
84
+ }
85
+ try {
86
+ const parsedSchema = JSON.parse(form.schema);
87
+ const zod = schemaConverter.formSchemaToZod(parsedSchema);
88
+ const config = helpers.buildFieldConfigFromJsonSchema(
89
+ parsedSchema,
90
+ mergedFieldComponents
91
+ );
92
+ return { zodSchema: zod, fieldConfig: config };
93
+ } catch {
94
+ return { zodSchema: null, fieldConfig: {} };
95
+ }
96
+ }, [form?.schema, mergedFieldComponents]);
97
+ const handleSubmit = async (data) => {
98
+ try {
99
+ const result = await submitMutation.mutateAsync({ data });
100
+ const message = propSuccessMessage || result.form.successMessage || (localization?.FORM_BUILDER_RENDERER_THANK_YOU ?? t("formBuilder.renderer.thankYou", "Thank you for your submission!"));
101
+ setFinalSuccessMessage(message);
102
+ setSubmitted(true);
103
+ onSuccess?.(result);
104
+ if (result.form.redirectUrl) {
105
+ window.location.href = result.form.redirectUrl;
106
+ return;
107
+ }
108
+ } catch (err) {
109
+ onError?.(err);
110
+ }
111
+ };
112
+ if (isLoading) {
113
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, children: /* @__PURE__ */ jsxRuntime.jsx(LoadingComponent, {}) });
114
+ }
115
+ if (error) {
116
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, children: /* @__PURE__ */ jsxRuntime.jsx(ErrorComponent, { error }) });
117
+ }
118
+ if (!form) {
119
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
120
+ ErrorComponent,
121
+ {
122
+ error: new Error(
123
+ localization?.FORM_BUILDER_RENDERER_NOT_FOUND ?? t("formBuilder.renderer.notFound", "Form not found")
124
+ )
125
+ }
126
+ ) });
127
+ }
128
+ if (form.status !== "active") {
129
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
130
+ ErrorComponent,
131
+ {
132
+ error: new Error(
133
+ localization?.FORM_BUILDER_RENDERER_INACTIVE ?? t(
134
+ "formBuilder.renderer.inactive",
135
+ "This form is not currently accepting submissions"
136
+ )
137
+ )
138
+ }
139
+ ) });
140
+ }
141
+ if (!zodSchema) {
142
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
143
+ ErrorComponent,
144
+ {
145
+ error: new Error(
146
+ localization?.FORM_BUILDER_RENDERER_SCHEMA_ERROR ?? t(
147
+ "formBuilder.renderer.schemaError",
148
+ "Failed to parse form schema"
149
+ )
150
+ )
151
+ }
152
+ ) });
153
+ }
154
+ if (submitted && finalSuccessMessage) {
155
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, children: /* @__PURE__ */ jsxRuntime.jsx(DefaultSuccessComponent, { message: finalSuccessMessage }) });
156
+ }
157
+ return /* @__PURE__ */ jsxRuntime.jsx(
158
+ context.PermissionAccess,
159
+ {
160
+ permission: permissions.formBuilderPermissions.form.render({
161
+ slug: form.slug,
162
+ exists: true,
163
+ formId: form.id,
164
+ ...form.createdBy ? { ownerId: form.createdBy } : {},
165
+ status: form.status
166
+ }),
167
+ children: /* @__PURE__ */ jsxRuntime.jsx(
168
+ context.PermissionAccess,
169
+ {
170
+ permission: permissions.formBuilderPermissions.submission.create({
171
+ slug: form.slug,
172
+ exists: true,
173
+ formId: form.id,
174
+ ...form.createdBy ? { ownerId: form.createdBy } : {},
175
+ status: form.status
176
+ }),
177
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: formClassName, "data-testid": "form-renderer", children: /* @__PURE__ */ jsxRuntime.jsx(
178
+ steppedAutoForm.SteppedAutoForm,
179
+ {
180
+ formSchema: zodSchema,
181
+ fieldConfig,
182
+ onSubmit: (values) => handleSubmit(values),
183
+ isSubmitting: submitMutation.isPending,
184
+ submitButtonText: submitButtonText || (localization?.FORM_BUILDER_BUTTON_SUBMIT ?? t("formBuilder.common.buttonSubmit", "Submit"))
185
+ }
186
+ ) })
187
+ }
188
+ )
189
+ }
190
+ );
191
+ }
192
+
193
+ exports.FormRenderer = FormRenderer;