@btst/stack 1.5.2 → 1.7.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 (241) hide show
  1. package/dist/api/index.cjs +7 -1
  2. package/dist/api/index.d.cts +2 -2
  3. package/dist/api/index.d.mts +2 -2
  4. package/dist/api/index.d.ts +2 -2
  5. package/dist/api/index.mjs +7 -1
  6. package/dist/client/index.d.cts +1 -1
  7. package/dist/client/index.d.mts +1 -1
  8. package/dist/client/index.d.ts +1 -1
  9. package/dist/index.d.cts +1 -1
  10. package/dist/index.d.mts +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.2.0/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.cjs +68 -0
  13. package/dist/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.2.0/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.mjs +60 -0
  14. package/dist/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.cjs +3937 -0
  15. package/dist/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.mjs +3907 -0
  16. package/dist/node_modules/.pnpm/@dnd-kit_modifiers@9.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/modifiers/dist/modifiers.esm.cjs +30 -0
  17. package/dist/node_modules/.pnpm/@dnd-kit_modifiers@9.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/modifiers/dist/modifiers.esm.mjs +28 -0
  18. package/dist/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/sortable/dist/sortable.esm.cjs +675 -0
  19. package/dist/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/sortable/dist/sortable.esm.mjs +661 -0
  20. package/dist/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.2.0/node_modules/@dnd-kit/utilities/dist/utilities.esm.cjs +358 -0
  21. package/dist/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.2.0/node_modules/@dnd-kit/utilities/dist/utilities.esm.mjs +332 -0
  22. package/dist/node_modules/.pnpm/@radix-ui_react-tabs@1.1.13_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_865f042350eb43f3338b0fffb33f6246/node_modules/@radix-ui/react-tabs/dist/index.cjs +211 -0
  23. package/dist/node_modules/.pnpm/@radix-ui_react-tabs@1.1.13_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_865f042350eb43f3338b0fffb33f6246/node_modules/@radix-ui/react-tabs/dist/index.mjs +188 -0
  24. package/dist/packages/better-stack/src/plugins/cms/api/plugin.cjs +3 -2
  25. package/dist/packages/better-stack/src/plugins/cms/api/plugin.mjs +3 -2
  26. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.cjs +15 -15
  27. package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.mjs +16 -16
  28. package/dist/packages/better-stack/src/plugins/form-builder/api/plugin.cjs +588 -0
  29. package/dist/packages/better-stack/src/plugins/form-builder/api/plugin.mjs +586 -0
  30. package/dist/packages/better-stack/src/plugins/form-builder/client/components/forms/form-renderer.cjs +131 -0
  31. package/dist/packages/better-stack/src/plugins/form-builder/client/components/forms/form-renderer.mjs +129 -0
  32. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-builder-skeleton.cjs +32 -0
  33. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-builder-skeleton.mjs +30 -0
  34. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-list-skeleton.cjs +21 -0
  35. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-list-skeleton.mjs +19 -0
  36. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/submissions-skeleton.cjs +34 -0
  37. package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/submissions-skeleton.mjs +32 -0
  38. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/404-page.cjs +20 -0
  39. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/404-page.mjs +18 -0
  40. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.cjs +19 -0
  41. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.cjs +186 -0
  42. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.mjs +184 -0
  43. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.mjs +17 -0
  44. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.cjs +19 -0
  45. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.cjs +165 -0
  46. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.mjs +163 -0
  47. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.mjs +17 -0
  48. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.cjs +19 -0
  49. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +177 -0
  50. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +175 -0
  51. package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.mjs +17 -0
  52. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/default-error.cjs +17 -0
  53. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/default-error.mjs +15 -0
  54. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/empty-state.cjs +16 -0
  55. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/empty-state.mjs +14 -0
  56. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/page-wrapper.cjs +27 -0
  57. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/page-wrapper.mjs +25 -0
  58. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/pagination.cjs +39 -0
  59. package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/pagination.mjs +37 -0
  60. package/dist/packages/better-stack/src/plugins/form-builder/client/hooks/form-builder-hooks.cjs +551 -0
  61. package/dist/packages/better-stack/src/plugins/form-builder/client/hooks/form-builder-hooks.mjs +537 -0
  62. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-common.cjs +36 -0
  63. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-common.mjs +34 -0
  64. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-editor.cjs +19 -0
  65. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-editor.mjs +17 -0
  66. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-list.cjs +21 -0
  67. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-list.mjs +19 -0
  68. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-submissions.cjs +19 -0
  69. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-submissions.mjs +17 -0
  70. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-toasts.cjs +14 -0
  71. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-toasts.mjs +12 -0
  72. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/index.cjs +17 -0
  73. package/dist/packages/better-stack/src/plugins/form-builder/client/localization/index.mjs +15 -0
  74. package/dist/packages/better-stack/src/plugins/form-builder/client/plugin.cjs +278 -0
  75. package/dist/packages/better-stack/src/plugins/form-builder/client/plugin.mjs +276 -0
  76. package/dist/packages/better-stack/src/plugins/form-builder/db.cjs +99 -0
  77. package/dist/packages/better-stack/src/plugins/form-builder/db.mjs +97 -0
  78. package/dist/packages/better-stack/src/plugins/form-builder/schemas.cjs +82 -0
  79. package/dist/packages/better-stack/src/plugins/form-builder/schemas.mjs +74 -0
  80. package/dist/packages/better-stack/src/plugins/form-builder/utils.cjs +37 -0
  81. package/dist/packages/better-stack/src/plugins/form-builder/utils.mjs +29 -0
  82. package/dist/packages/better-stack/src/plugins/open-api/api/generator.cjs +300 -0
  83. package/dist/packages/better-stack/src/plugins/open-api/api/generator.mjs +284 -0
  84. package/dist/packages/better-stack/src/plugins/open-api/api/plugin.cjs +115 -0
  85. package/dist/packages/better-stack/src/plugins/open-api/api/plugin.mjs +113 -0
  86. package/dist/packages/better-stack/src/plugins/open-api/db.cjs +7 -0
  87. package/dist/packages/better-stack/src/plugins/open-api/db.mjs +5 -0
  88. package/dist/packages/better-stack/src/plugins/open-api/logo.cjs +8 -0
  89. package/dist/packages/better-stack/src/plugins/open-api/logo.mjs +6 -0
  90. package/dist/packages/ui/src/components/auto-form/index.cjs +2 -12
  91. package/dist/packages/ui/src/components/auto-form/index.mjs +2 -9
  92. package/dist/packages/ui/src/components/auto-form/stepped-auto-form.cjs +377 -0
  93. package/dist/packages/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
  94. package/dist/packages/ui/src/components/auto-form/utils.cjs +1 -56
  95. package/dist/packages/ui/src/components/auto-form/utils.mjs +2 -56
  96. package/dist/packages/ui/src/components/form-builder/canvas.cjs +111 -0
  97. package/dist/packages/ui/src/components/form-builder/canvas.mjs +109 -0
  98. package/dist/packages/ui/src/components/form-builder/components/index.cjs +570 -0
  99. package/dist/packages/ui/src/components/form-builder/components/index.mjs +553 -0
  100. package/dist/packages/ui/src/components/form-builder/edit-field-dialog.cjs +131 -0
  101. package/dist/packages/ui/src/components/form-builder/edit-field-dialog.mjs +129 -0
  102. package/dist/packages/ui/src/components/form-builder/form-preview.cjs +73 -0
  103. package/dist/packages/ui/src/components/form-builder/form-preview.mjs +71 -0
  104. package/dist/packages/ui/src/components/form-builder/index.cjs +353 -0
  105. package/dist/packages/ui/src/components/form-builder/index.mjs +344 -0
  106. package/dist/packages/ui/src/components/form-builder/nested-field-editor-dialog.cjs +263 -0
  107. package/dist/packages/ui/src/components/form-builder/nested-field-editor-dialog.mjs +261 -0
  108. package/dist/packages/ui/src/components/form-builder/palette.cjs +52 -0
  109. package/dist/packages/ui/src/components/form-builder/palette.mjs +49 -0
  110. package/dist/packages/ui/src/components/form-builder/schema-utils.cjs +120 -0
  111. package/dist/packages/ui/src/components/form-builder/schema-utils.mjs +114 -0
  112. package/dist/packages/ui/src/components/form-builder/sortable-field.cjs +151 -0
  113. package/dist/packages/ui/src/components/form-builder/sortable-field.mjs +148 -0
  114. package/dist/packages/ui/src/components/form-builder/step-tabs.cjs +180 -0
  115. package/dist/packages/ui/src/components/form-builder/step-tabs.mjs +178 -0
  116. package/dist/packages/ui/src/components/form-builder/types.cjs +7 -0
  117. package/dist/packages/ui/src/components/form-builder/types.mjs +5 -0
  118. package/dist/packages/ui/src/components/form-builder/validation-schemas.cjs +67 -0
  119. package/dist/packages/ui/src/components/form-builder/validation-schemas.mjs +56 -0
  120. package/dist/packages/ui/src/components/tabs.cjs +70 -0
  121. package/dist/packages/ui/src/components/tabs.mjs +65 -0
  122. package/dist/packages/ui/src/lib/schema-converter.cjs +130 -0
  123. package/dist/packages/ui/src/lib/schema-converter.mjs +124 -0
  124. package/dist/plugins/api/index.d.cts +2 -2
  125. package/dist/plugins/api/index.d.mts +2 -2
  126. package/dist/plugins/api/index.d.ts +2 -2
  127. package/dist/plugins/blog/api/index.d.cts +1 -1
  128. package/dist/plugins/blog/api/index.d.mts +1 -1
  129. package/dist/plugins/blog/api/index.d.ts +1 -1
  130. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  131. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  132. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  133. package/dist/plugins/blog/client/index.d.cts +1 -1
  134. package/dist/plugins/blog/client/index.d.mts +1 -1
  135. package/dist/plugins/blog/client/index.d.ts +1 -1
  136. package/dist/plugins/blog/query-keys.d.cts +2 -2
  137. package/dist/plugins/blog/query-keys.d.mts +2 -2
  138. package/dist/plugins/blog/query-keys.d.ts +2 -2
  139. package/dist/plugins/client/index.d.cts +2 -2
  140. package/dist/plugins/client/index.d.mts +2 -2
  141. package/dist/plugins/client/index.d.ts +2 -2
  142. package/dist/plugins/cms/client/index.cjs +6 -0
  143. package/dist/plugins/cms/client/index.d.cts +6 -113
  144. package/dist/plugins/cms/client/index.d.mts +6 -113
  145. package/dist/plugins/cms/client/index.d.ts +6 -113
  146. package/dist/plugins/cms/client/index.mjs +1 -0
  147. package/dist/plugins/form-builder/api/index.cjs +7 -0
  148. package/dist/plugins/form-builder/api/index.d.cts +141 -0
  149. package/dist/plugins/form-builder/api/index.d.mts +141 -0
  150. package/dist/plugins/form-builder/api/index.d.ts +141 -0
  151. package/dist/plugins/form-builder/api/index.mjs +1 -0
  152. package/dist/plugins/form-builder/client/components/index.cjs +29 -0
  153. package/dist/plugins/form-builder/client/components/index.d.cts +93 -0
  154. package/dist/plugins/form-builder/client/components/index.d.mts +93 -0
  155. package/dist/plugins/form-builder/client/components/index.d.ts +93 -0
  156. package/dist/plugins/form-builder/client/components/index.mjs +18 -0
  157. package/dist/plugins/form-builder/client/hooks/index.cjs +19 -0
  158. package/dist/plugins/form-builder/client/hooks/index.d.cts +154 -0
  159. package/dist/plugins/form-builder/client/hooks/index.d.mts +154 -0
  160. package/dist/plugins/form-builder/client/hooks/index.d.ts +154 -0
  161. package/dist/plugins/form-builder/client/hooks/index.mjs +1 -0
  162. package/dist/plugins/form-builder/client/index.cjs +13 -0
  163. package/dist/plugins/form-builder/client/index.d.cts +381 -0
  164. package/dist/plugins/form-builder/client/index.d.mts +381 -0
  165. package/dist/plugins/form-builder/client/index.d.ts +381 -0
  166. package/dist/plugins/form-builder/client/index.mjs +2 -0
  167. package/dist/plugins/form-builder/client.css +3 -0
  168. package/dist/plugins/form-builder/query-keys.cjs +143 -0
  169. package/dist/plugins/form-builder/query-keys.d.cts +74 -0
  170. package/dist/plugins/form-builder/query-keys.d.mts +74 -0
  171. package/dist/plugins/form-builder/query-keys.d.ts +74 -0
  172. package/dist/plugins/form-builder/query-keys.mjs +141 -0
  173. package/dist/plugins/form-builder/style.css +19 -0
  174. package/dist/plugins/open-api/api/index.cjs +9 -0
  175. package/dist/plugins/open-api/api/index.d.cts +95 -0
  176. package/dist/plugins/open-api/api/index.d.mts +95 -0
  177. package/dist/plugins/open-api/api/index.d.ts +95 -0
  178. package/dist/plugins/open-api/api/index.mjs +2 -0
  179. package/dist/shared/stack.AX5nZ6A3.d.cts +86 -0
  180. package/dist/shared/stack.AX5nZ6A3.d.mts +86 -0
  181. package/dist/shared/stack.AX5nZ6A3.d.ts +86 -0
  182. package/dist/shared/stack.BIh2AXaW.d.cts +123 -0
  183. package/dist/shared/stack.BIh2AXaW.d.mts +123 -0
  184. package/dist/shared/stack.BIh2AXaW.d.ts +123 -0
  185. package/dist/shared/{stack.ByOugz9d.d.cts → stack.CSce37mX.d.cts} +15 -2
  186. package/dist/shared/{stack.ByOugz9d.d.mts → stack.CSce37mX.d.mts} +15 -2
  187. package/dist/shared/{stack.ByOugz9d.d.ts → stack.CSce37mX.d.ts} +15 -2
  188. package/dist/shared/stack.DzH_wcvr.d.cts +195 -0
  189. package/dist/shared/stack.DzH_wcvr.d.mts +195 -0
  190. package/dist/shared/stack.DzH_wcvr.d.ts +195 -0
  191. package/package.json +67 -1
  192. package/src/api/index.ts +14 -2
  193. package/src/plugins/cms/api/plugin.ts +9 -4
  194. package/src/plugins/cms/client/components/forms/content-form.tsx +23 -25
  195. package/src/plugins/cms/client/index.ts +11 -0
  196. package/src/plugins/form-builder/api/index.ts +1 -0
  197. package/src/plugins/form-builder/api/plugin.ts +776 -0
  198. package/src/plugins/form-builder/client/components/forms/form-renderer.tsx +253 -0
  199. package/src/plugins/form-builder/client/components/index.tsx +24 -0
  200. package/src/plugins/form-builder/client/components/loading/form-builder-skeleton.tsx +42 -0
  201. package/src/plugins/form-builder/client/components/loading/form-list-skeleton.tsx +25 -0
  202. package/src/plugins/form-builder/client/components/loading/index.tsx +3 -0
  203. package/src/plugins/form-builder/client/components/loading/submissions-skeleton.tsx +40 -0
  204. package/src/plugins/form-builder/client/components/pages/404-page.tsx +28 -0
  205. package/src/plugins/form-builder/client/components/pages/form-builder-page.internal.tsx +253 -0
  206. package/src/plugins/form-builder/client/components/pages/form-builder-page.tsx +26 -0
  207. package/src/plugins/form-builder/client/components/pages/form-list-page.internal.tsx +231 -0
  208. package/src/plugins/form-builder/client/components/pages/form-list-page.tsx +22 -0
  209. package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +268 -0
  210. package/src/plugins/form-builder/client/components/pages/submissions-page.tsx +26 -0
  211. package/src/plugins/form-builder/client/components/shared/default-error.tsx +30 -0
  212. package/src/plugins/form-builder/client/components/shared/empty-state.tsx +26 -0
  213. package/src/plugins/form-builder/client/components/shared/page-wrapper.tsx +32 -0
  214. package/src/plugins/form-builder/client/components/shared/pagination.tsx +52 -0
  215. package/src/plugins/form-builder/client/hooks/form-builder-hooks.tsx +799 -0
  216. package/src/plugins/form-builder/client/hooks/index.tsx +1 -0
  217. package/src/plugins/form-builder/client/index.ts +22 -0
  218. package/src/plugins/form-builder/client/localization/form-builder-common.ts +36 -0
  219. package/src/plugins/form-builder/client/localization/form-builder-editor.ts +18 -0
  220. package/src/plugins/form-builder/client/localization/form-builder-list.ts +17 -0
  221. package/src/plugins/form-builder/client/localization/form-builder-submissions.ts +17 -0
  222. package/src/plugins/form-builder/client/localization/form-builder-toasts.ts +10 -0
  223. package/src/plugins/form-builder/client/localization/index.ts +15 -0
  224. package/src/plugins/form-builder/client/overrides.ts +146 -0
  225. package/src/plugins/form-builder/client/plugin.tsx +488 -0
  226. package/src/plugins/form-builder/client.css +3 -0
  227. package/src/plugins/form-builder/db.ts +99 -0
  228. package/src/plugins/form-builder/query-keys.ts +198 -0
  229. package/src/plugins/form-builder/schemas.ts +122 -0
  230. package/src/plugins/form-builder/style.css +19 -0
  231. package/src/plugins/form-builder/types.ts +317 -0
  232. package/src/plugins/form-builder/utils.ts +63 -0
  233. package/src/plugins/open-api/api/generator.ts +433 -0
  234. package/src/plugins/open-api/api/index.ts +8 -0
  235. package/src/plugins/open-api/api/plugin.ts +243 -0
  236. package/src/plugins/open-api/db.ts +7 -0
  237. package/src/plugins/open-api/logo.ts +7 -0
  238. package/src/types.ts +15 -1
  239. package/dist/shared/{stack.DLhzx1-D.d.mts → stack.CcI4sYJP.d.cts} +1 -1
  240. package/dist/shared/{stack.DLhzx1-D.d.ts → stack.CcI4sYJP.d.mts} +1 -1
  241. package/dist/shared/{stack.DLhzx1-D.d.cts → stack.CcI4sYJP.d.ts} +1 -1
@@ -0,0 +1,488 @@
1
+ // NO "use client" here! This file runs on both server and client.
2
+ import { lazy } from "react";
3
+ import {
4
+ defineClientPlugin,
5
+ createApiClient,
6
+ } from "@btst/stack/plugins/client";
7
+ import { createRoute } from "@btst/yar";
8
+ import type { QueryClient } from "@tanstack/react-query";
9
+ import type { FormBuilderApiRouter } from "../api";
10
+ import { createFormBuilderQueryKeys } from "../query-keys";
11
+
12
+ // Lazy load page components for code splitting
13
+ const FormListPageComponent = lazy(() =>
14
+ import("./components/pages/form-list-page").then((m) => ({
15
+ default: m.FormListPageComponent,
16
+ })),
17
+ );
18
+ const FormBuilderPageComponent = lazy(() =>
19
+ import("./components/pages/form-builder-page").then((m) => ({
20
+ default: m.FormBuilderPageComponent,
21
+ })),
22
+ );
23
+ const SubmissionsPageComponent = lazy(() =>
24
+ import("./components/pages/submissions-page").then((m) => ({
25
+ default: m.SubmissionsPageComponent,
26
+ })),
27
+ );
28
+
29
+ /**
30
+ * Context passed to loader hooks
31
+ */
32
+ export interface LoaderContext {
33
+ /** Current route path */
34
+ path: string;
35
+ /** Route parameters (e.g., { id: "123" }) */
36
+ params?: Record<string, string>;
37
+ /** Whether rendering on server (true) or client (false) */
38
+ isSSR: boolean;
39
+ /** Base URL for API calls */
40
+ apiBaseURL: string;
41
+ /** Path where the API is mounted */
42
+ apiBasePath: string;
43
+ /** Optional headers for the request */
44
+ headers?: Headers;
45
+ /** Additional context properties */
46
+ [key: string]: unknown;
47
+ }
48
+
49
+ /**
50
+ * Hooks for Form Builder client plugin
51
+ * All hooks are optional and allow consumers to customize behavior
52
+ */
53
+ export interface FormBuilderClientHooks {
54
+ /**
55
+ * Called before loading the form list page. Return false to cancel loading.
56
+ * @param context - Loader context with path, params, etc.
57
+ */
58
+ beforeLoadFormList?: (context: LoaderContext) => Promise<boolean> | boolean;
59
+ /**
60
+ * Called after the form list is loaded.
61
+ * @param context - Loader context
62
+ */
63
+ afterLoadFormList?: (context: LoaderContext) => Promise<void> | void;
64
+ /**
65
+ * Called before loading the form builder page. Return false to cancel loading.
66
+ * @param id - The form ID (undefined for new forms)
67
+ * @param context - Loader context
68
+ */
69
+ beforeLoadFormBuilder?: (
70
+ id: string | undefined,
71
+ context: LoaderContext,
72
+ ) => Promise<boolean> | boolean;
73
+ /**
74
+ * Called after the form builder is loaded.
75
+ * @param id - The form ID (undefined for new forms)
76
+ * @param context - Loader context
77
+ */
78
+ afterLoadFormBuilder?: (
79
+ id: string | undefined,
80
+ context: LoaderContext,
81
+ ) => Promise<void> | void;
82
+ /**
83
+ * Called before loading the submissions page. Return false to cancel loading.
84
+ * @param formId - The form ID
85
+ * @param context - Loader context
86
+ */
87
+ beforeLoadSubmissions?: (
88
+ formId: string,
89
+ context: LoaderContext,
90
+ ) => Promise<boolean> | boolean;
91
+ /**
92
+ * Called after the submissions page is loaded.
93
+ * @param formId - The form ID
94
+ * @param context - Loader context
95
+ */
96
+ afterLoadSubmissions?: (
97
+ formId: string,
98
+ context: LoaderContext,
99
+ ) => Promise<void> | void;
100
+ /**
101
+ * Called when a loading error occurs.
102
+ * Use this for redirects on authorization failures.
103
+ * @param error - The error that occurred
104
+ * @param context - Loader context
105
+ */
106
+ onLoadError?: (error: Error, context: LoaderContext) => Promise<void> | void;
107
+ }
108
+
109
+ /**
110
+ * Configuration for Form Builder client plugin
111
+ */
112
+ export interface FormBuilderClientConfig {
113
+ /** Base URL for API calls (e.g., "http://localhost:3000") */
114
+ apiBaseURL: string;
115
+ /** Path where the API is mounted (e.g., "/api/data") */
116
+ apiBasePath: string;
117
+ /** Base URL of your site */
118
+ siteBaseURL: string;
119
+ /** Path where pages are mounted (e.g., "/pages") */
120
+ siteBasePath: string;
121
+ /** React Query client instance for caching */
122
+ queryClient: QueryClient;
123
+ /** Optional headers for SSR (e.g., forwarding cookies) */
124
+ headers?: Headers;
125
+ /** Optional hooks for customizing behavior (authorization, redirects, etc.) */
126
+ hooks?: FormBuilderClientHooks;
127
+ }
128
+
129
+ /**
130
+ * Create form list loader for SSR
131
+ */
132
+ function createFormListLoader(config: FormBuilderClientConfig) {
133
+ return async () => {
134
+ if (typeof window === "undefined") {
135
+ const { queryClient, apiBasePath, apiBaseURL, headers, hooks } = config;
136
+
137
+ const context: LoaderContext = {
138
+ path: "/forms",
139
+ isSSR: true,
140
+ apiBaseURL,
141
+ apiBasePath,
142
+ headers,
143
+ };
144
+
145
+ try {
146
+ // Before hook - authorization check
147
+ if (hooks?.beforeLoadFormList) {
148
+ const canLoad = await hooks.beforeLoadFormList(context);
149
+ if (!canLoad) {
150
+ throw new Error("Load prevented by beforeLoadFormList hook");
151
+ }
152
+ }
153
+
154
+ const client = createApiClient<FormBuilderApiRouter>({
155
+ baseURL: apiBaseURL,
156
+ basePath: apiBasePath,
157
+ });
158
+ const queries = createFormBuilderQueryKeys(client, headers);
159
+ const limit = 20;
160
+
161
+ // Prefetch forms using infinite query
162
+ const listQuery = queries.forms.list({ limit, offset: 0 });
163
+ await queryClient.prefetchInfiniteQuery({
164
+ queryKey: listQuery.queryKey,
165
+ queryFn: async ({ pageParam = 0 }) => {
166
+ const response: unknown = await client("/forms", {
167
+ method: "GET",
168
+ query: { limit, offset: pageParam },
169
+ headers,
170
+ });
171
+ if (
172
+ typeof response === "object" &&
173
+ response !== null &&
174
+ "error" in response &&
175
+ response.error
176
+ ) {
177
+ throw new Error(String(response.error));
178
+ }
179
+ return (response as { data?: unknown }).data;
180
+ },
181
+ initialPageParam: 0,
182
+ });
183
+
184
+ // After hook
185
+ if (hooks?.afterLoadFormList) {
186
+ await hooks.afterLoadFormList(context);
187
+ }
188
+
189
+ // Check if there was an error
190
+ const queryState = queryClient.getQueryState(listQuery.queryKey);
191
+ if (queryState?.error && hooks?.onLoadError) {
192
+ const error =
193
+ queryState.error instanceof Error
194
+ ? queryState.error
195
+ : new Error(String(queryState.error));
196
+ await hooks.onLoadError(error, context);
197
+ }
198
+ } catch (error) {
199
+ // Error hook - log the error but don't throw during SSR
200
+ if (hooks?.onLoadError) {
201
+ await hooks.onLoadError(error as Error, context);
202
+ }
203
+ }
204
+ }
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Create form builder loader for SSR
210
+ */
211
+ function createFormBuilderLoader(
212
+ id: string | undefined,
213
+ config: FormBuilderClientConfig,
214
+ ) {
215
+ return async () => {
216
+ if (typeof window === "undefined") {
217
+ const { queryClient, apiBasePath, apiBaseURL, headers, hooks } = config;
218
+
219
+ const context: LoaderContext = {
220
+ path: id ? `/forms/${id}/edit` : "/forms/new",
221
+ params: id ? { id } : {},
222
+ isSSR: true,
223
+ apiBaseURL,
224
+ apiBasePath,
225
+ headers,
226
+ };
227
+
228
+ try {
229
+ // Before hook - authorization check
230
+ if (hooks?.beforeLoadFormBuilder) {
231
+ const canLoad = await hooks.beforeLoadFormBuilder(id, context);
232
+ if (!canLoad) {
233
+ throw new Error("Load prevented by beforeLoadFormBuilder hook");
234
+ }
235
+ }
236
+
237
+ const client = createApiClient<FormBuilderApiRouter>({
238
+ baseURL: apiBaseURL,
239
+ basePath: apiBasePath,
240
+ });
241
+ const queries = createFormBuilderQueryKeys(client, headers);
242
+
243
+ // Prefetch form if editing
244
+ if (id) {
245
+ await queryClient.prefetchQuery(queries.forms.byId(id));
246
+ }
247
+
248
+ // After hook
249
+ if (hooks?.afterLoadFormBuilder) {
250
+ await hooks.afterLoadFormBuilder(id, context);
251
+ }
252
+
253
+ // Check if there was an error
254
+ if (id) {
255
+ const queryState = queryClient.getQueryState(
256
+ queries.forms.byId(id).queryKey,
257
+ );
258
+ if (queryState?.error && hooks?.onLoadError) {
259
+ const error =
260
+ queryState.error instanceof Error
261
+ ? queryState.error
262
+ : new Error(String(queryState.error));
263
+ await hooks.onLoadError(error, context);
264
+ }
265
+ }
266
+ } catch (error) {
267
+ // Error hook - log the error but don't throw during SSR
268
+ if (hooks?.onLoadError) {
269
+ await hooks.onLoadError(error as Error, context);
270
+ }
271
+ }
272
+ }
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Create submissions loader for SSR
278
+ */
279
+ function createSubmissionsLoader(
280
+ formId: string,
281
+ config: FormBuilderClientConfig,
282
+ ) {
283
+ return async () => {
284
+ if (typeof window === "undefined") {
285
+ const { queryClient, apiBasePath, apiBaseURL, headers, hooks } = config;
286
+
287
+ const context: LoaderContext = {
288
+ path: `/forms/${formId}/submissions`,
289
+ params: { formId },
290
+ isSSR: true,
291
+ apiBaseURL,
292
+ apiBasePath,
293
+ headers,
294
+ };
295
+
296
+ try {
297
+ // Before hook - authorization check
298
+ if (hooks?.beforeLoadSubmissions) {
299
+ const canLoad = await hooks.beforeLoadSubmissions(formId, context);
300
+ if (!canLoad) {
301
+ throw new Error("Load prevented by beforeLoadSubmissions hook");
302
+ }
303
+ }
304
+
305
+ const client = createApiClient<FormBuilderApiRouter>({
306
+ baseURL: apiBaseURL,
307
+ basePath: apiBasePath,
308
+ });
309
+ const queries = createFormBuilderQueryKeys(client, headers);
310
+ const limit = 20;
311
+
312
+ // Prefetch form and submissions
313
+ await queryClient.prefetchQuery(queries.forms.byId(formId));
314
+
315
+ const submissionsQuery = queries.formSubmissions.list({
316
+ formId,
317
+ limit,
318
+ offset: 0,
319
+ });
320
+ await queryClient.prefetchInfiniteQuery({
321
+ queryKey: submissionsQuery.queryKey,
322
+ queryFn: async ({ pageParam = 0 }) => {
323
+ const response: unknown = await client(
324
+ "/forms/:formId/submissions",
325
+ {
326
+ method: "GET",
327
+ params: { formId },
328
+ query: { limit, offset: pageParam },
329
+ headers,
330
+ },
331
+ );
332
+ if (
333
+ typeof response === "object" &&
334
+ response !== null &&
335
+ "error" in response &&
336
+ response.error
337
+ ) {
338
+ throw new Error(String(response.error));
339
+ }
340
+ return (response as { data?: unknown }).data;
341
+ },
342
+ initialPageParam: 0,
343
+ });
344
+
345
+ // After hook
346
+ if (hooks?.afterLoadSubmissions) {
347
+ await hooks.afterLoadSubmissions(formId, context);
348
+ }
349
+
350
+ // Check if there was an error
351
+ const formState = queryClient.getQueryState(
352
+ queries.forms.byId(formId).queryKey,
353
+ );
354
+ const submissionsState = queryClient.getQueryState(
355
+ submissionsQuery.queryKey,
356
+ );
357
+ const queryError = formState?.error || submissionsState?.error;
358
+ if (queryError && hooks?.onLoadError) {
359
+ const error =
360
+ queryError instanceof Error
361
+ ? queryError
362
+ : new Error(String(queryError));
363
+ await hooks.onLoadError(error, context);
364
+ }
365
+ } catch (error) {
366
+ // Error hook - log the error but don't throw during SSR
367
+ if (hooks?.onLoadError) {
368
+ await hooks.onLoadError(error as Error, context);
369
+ }
370
+ }
371
+ }
372
+ };
373
+ }
374
+
375
+ /**
376
+ * Create form list meta generator
377
+ */
378
+ function createFormListMeta() {
379
+ return () => {
380
+ const title = "Forms";
381
+ return [
382
+ { title },
383
+ { name: "title", content: title },
384
+ { name: "robots", content: "noindex" },
385
+ ];
386
+ };
387
+ }
388
+
389
+ /**
390
+ * Create form builder meta generator
391
+ */
392
+ function createFormBuilderMeta(
393
+ id: string | undefined,
394
+ config: FormBuilderClientConfig,
395
+ ) {
396
+ return () => {
397
+ const { queryClient, apiBasePath, apiBaseURL } = config;
398
+
399
+ let formName = "";
400
+ if (id) {
401
+ const client = createApiClient<FormBuilderApiRouter>({
402
+ baseURL: apiBaseURL,
403
+ basePath: apiBasePath,
404
+ });
405
+ const queries = createFormBuilderQueryKeys(client);
406
+ const form = queryClient.getQueryData(queries.forms.byId(id).queryKey) as
407
+ | { name: string }
408
+ | undefined;
409
+ formName = form?.name || "";
410
+ }
411
+
412
+ const title = id ? `Edit ${formName || "Form"}` : "New Form";
413
+
414
+ return [
415
+ { title },
416
+ { name: "title", content: title },
417
+ { name: "robots", content: "noindex" },
418
+ ];
419
+ };
420
+ }
421
+
422
+ /**
423
+ * Create submissions meta generator
424
+ */
425
+ function createSubmissionsMeta(
426
+ formId: string,
427
+ config: FormBuilderClientConfig,
428
+ ) {
429
+ return () => {
430
+ const { queryClient, apiBasePath, apiBaseURL } = config;
431
+ const client = createApiClient<FormBuilderApiRouter>({
432
+ baseURL: apiBaseURL,
433
+ basePath: apiBasePath,
434
+ });
435
+ const queries = createFormBuilderQueryKeys(client);
436
+ const form = queryClient.getQueryData(
437
+ queries.forms.byId(formId).queryKey,
438
+ ) as { name: string } | undefined;
439
+
440
+ const title = form?.name ? `${form.name} Submissions` : "Submissions";
441
+
442
+ return [
443
+ { title },
444
+ { name: "title", content: title },
445
+ { name: "robots", content: "noindex" },
446
+ ];
447
+ };
448
+ }
449
+
450
+ /**
451
+ * Form Builder client plugin
452
+ * Provides routes and components for the Form Builder admin interface
453
+ */
454
+ export const formBuilderClientPlugin = (config: FormBuilderClientConfig) =>
455
+ defineClientPlugin({
456
+ name: "form-builder",
457
+
458
+ routes: () => ({
459
+ formList: createRoute("/forms", () => ({
460
+ PageComponent: () => <FormListPageComponent />,
461
+ loader: createFormListLoader(config),
462
+ meta: createFormListMeta(),
463
+ })),
464
+
465
+ newForm: createRoute("/forms/new", () => ({
466
+ PageComponent: () => <FormBuilderPageComponent />,
467
+ loader: createFormBuilderLoader(undefined, config),
468
+ meta: createFormBuilderMeta(undefined, config),
469
+ })),
470
+
471
+ editForm: createRoute("/forms/:id/edit", ({ params }) => ({
472
+ PageComponent: () => <FormBuilderPageComponent id={params.id} />,
473
+ loader: createFormBuilderLoader(params.id, config),
474
+ meta: createFormBuilderMeta(params.id, config),
475
+ })),
476
+
477
+ submissions: createRoute("/forms/:id/submissions", ({ params }) => ({
478
+ PageComponent: () => <SubmissionsPageComponent formId={params.id} />,
479
+ loader: createSubmissionsLoader(params.id, config),
480
+ meta: createSubmissionsMeta(params.id, config),
481
+ })),
482
+ }),
483
+
484
+ sitemap: async () => {
485
+ // Form Builder admin pages should NOT be in sitemap
486
+ return [];
487
+ },
488
+ });
@@ -0,0 +1,3 @@
1
+ /* Form Builder Plugin Client CSS */
2
+ /* No custom styles needed - uses shadcn/ui components */
3
+
@@ -0,0 +1,99 @@
1
+ import { createDbPlugin } from "@btst/db";
2
+
3
+ /**
4
+ * Form Builder plugin schema
5
+ * Defines the database tables for forms and form submissions
6
+ */
7
+ export const formBuilderSchema = createDbPlugin("form-builder", {
8
+ form: {
9
+ modelName: "form",
10
+ fields: {
11
+ name: {
12
+ type: "string",
13
+ required: true,
14
+ },
15
+ slug: {
16
+ type: "string",
17
+ required: true,
18
+ unique: true,
19
+ },
20
+ description: {
21
+ type: "string",
22
+ required: false,
23
+ },
24
+ // JSON Schema stored as string (includes steps, fieldType, stepGroup, etc.)
25
+ schema: {
26
+ type: "string",
27
+ required: true,
28
+ },
29
+ // Optional custom success message after submission
30
+ successMessage: {
31
+ type: "string",
32
+ required: false,
33
+ },
34
+ // Optional redirect URL after submission
35
+ redirectUrl: {
36
+ type: "string",
37
+ required: false,
38
+ },
39
+ // Form status: active, inactive, archived
40
+ status: {
41
+ type: "string",
42
+ defaultValue: "active",
43
+ },
44
+ // User who created the form
45
+ createdBy: {
46
+ type: "string",
47
+ required: false,
48
+ },
49
+ createdAt: {
50
+ type: "date",
51
+ defaultValue: () => new Date(),
52
+ },
53
+ updatedAt: {
54
+ type: "date",
55
+ defaultValue: () => new Date(),
56
+ },
57
+ },
58
+ },
59
+ formSubmission: {
60
+ modelName: "formSubmission",
61
+ fields: {
62
+ formId: {
63
+ type: "string",
64
+ required: true,
65
+ // Database reference for efficient joins
66
+ references: {
67
+ model: "form",
68
+ field: "id",
69
+ onDelete: "cascade",
70
+ },
71
+ },
72
+ // Submitted data as JSON string
73
+ data: {
74
+ type: "string",
75
+ required: true,
76
+ },
77
+ // Submission timestamp
78
+ submittedAt: {
79
+ type: "date",
80
+ defaultValue: () => new Date(),
81
+ },
82
+ // Optional user ID if authenticated
83
+ submittedBy: {
84
+ type: "string",
85
+ required: false,
86
+ },
87
+ // IP address for rate limiting and spam protection
88
+ ipAddress: {
89
+ type: "string",
90
+ required: false,
91
+ },
92
+ // User agent for analytics
93
+ userAgent: {
94
+ type: "string",
95
+ required: false,
96
+ },
97
+ },
98
+ },
99
+ });