@btst/stack 1.10.0 → 1.12.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 (237) hide show
  1. 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 +1 -1
  2. 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 +1 -1
  3. 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 +77 -0
  4. 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 +79 -3
  5. package/dist/node_modules/.pnpm/@radix-ui_react-avatar@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react_850cfbef1935a6e49a6ad6c93c7ca70d/node_modules/@radix-ui/react-avatar/dist/index.cjs +140 -0
  6. package/dist/node_modules/.pnpm/@radix-ui_react-avatar@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react_850cfbef1935a6e49a6ad6c93c7ca70d/node_modules/@radix-ui/react-avatar/dist/index.mjs +119 -0
  7. package/dist/node_modules/.pnpm/@radix-ui_react-context@1.1.3_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-context/dist/index.cjs +80 -0
  8. package/dist/node_modules/.pnpm/@radix-ui_react-context@1.1.3_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-context/dist/index.mjs +64 -0
  9. package/dist/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.cjs +18 -0
  10. package/dist/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.mjs +16 -0
  11. package/dist/packages/better-stack/src/plugins/kanban/api/plugin.cjs +846 -0
  12. package/dist/packages/better-stack/src/plugins/kanban/api/plugin.mjs +844 -0
  13. package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/board-form.cjs +85 -0
  14. package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/board-form.mjs +83 -0
  15. package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/column-form.cjs +72 -0
  16. package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/column-form.mjs +70 -0
  17. package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/task-form.cjs +200 -0
  18. package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/task-form.mjs +198 -0
  19. package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/board-skeleton.cjs +47 -0
  20. package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/board-skeleton.mjs +45 -0
  21. package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/boards-list-skeleton.cjs +30 -0
  22. package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/boards-list-skeleton.mjs +28 -0
  23. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/404-page.cjs +27 -0
  24. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/404-page.mjs +25 -0
  25. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.cjs +31 -0
  26. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +458 -0
  27. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +456 -0
  28. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.mjs +29 -0
  29. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.cjs +30 -0
  30. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.cjs +72 -0
  31. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.mjs +70 -0
  32. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.mjs +28 -0
  33. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.cjs +30 -0
  34. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.internal.cjs +51 -0
  35. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.internal.mjs +49 -0
  36. package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.mjs +28 -0
  37. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/column-content.cjs +76 -0
  38. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/column-content.mjs +74 -0
  39. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/default-error.cjs +27 -0
  40. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/default-error.mjs +25 -0
  41. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/empty-state.cjs +32 -0
  42. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/empty-state.mjs +30 -0
  43. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/kanban-board.cjs +78 -0
  44. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/kanban-board.mjs +76 -0
  45. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/page-wrapper.cjs +15 -0
  46. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/page-wrapper.mjs +13 -0
  47. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/task-card.cjs +68 -0
  48. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/task-card.mjs +66 -0
  49. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/user-avatar.cjs +32 -0
  50. package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/user-avatar.mjs +30 -0
  51. package/dist/packages/better-stack/src/plugins/kanban/client/hooks/kanban-hooks.cjs +391 -0
  52. package/dist/packages/better-stack/src/plugins/kanban/client/hooks/kanban-hooks.mjs +381 -0
  53. package/dist/packages/better-stack/src/plugins/kanban/client/plugin.cjs +290 -0
  54. package/dist/packages/better-stack/src/plugins/kanban/client/plugin.mjs +288 -0
  55. package/dist/packages/better-stack/src/plugins/kanban/db.cjs +125 -0
  56. package/dist/packages/better-stack/src/plugins/kanban/db.mjs +123 -0
  57. package/dist/packages/better-stack/src/plugins/kanban/schemas.cjs +117 -0
  58. package/dist/packages/better-stack/src/plugins/kanban/schemas.mjs +102 -0
  59. package/dist/packages/better-stack/src/plugins/kanban/utils.cjs +49 -0
  60. package/dist/packages/better-stack/src/plugins/kanban/utils.mjs +45 -0
  61. package/dist/packages/ui/src/components/avatar.cjs +58 -0
  62. package/dist/packages/ui/src/components/avatar.mjs +54 -0
  63. package/dist/packages/ui/src/components/command.cjs +3 -3
  64. package/dist/packages/ui/src/components/command.mjs +3 -3
  65. package/dist/packages/ui/src/components/form-builder/index.mjs +2 -2
  66. package/dist/packages/ui/src/components/kanban.cjs +835 -0
  67. package/dist/packages/ui/src/components/kanban.mjs +805 -0
  68. package/dist/packages/ui/src/components/minimal-tiptap/utils.cjs +15 -11
  69. package/dist/packages/ui/src/components/minimal-tiptap/utils.mjs +15 -11
  70. package/dist/packages/ui/src/components/popover.cjs +8 -3
  71. package/dist/packages/ui/src/components/popover.mjs +9 -4
  72. package/dist/packages/ui/src/components/search-select.cjs +75 -0
  73. package/dist/packages/ui/src/components/search-select.mjs +73 -0
  74. package/dist/packages/ui/src/components/ui-builder/index.cjs +9 -7
  75. package/dist/packages/ui/src/components/ui-builder/index.mjs +9 -7
  76. package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.cjs +6 -3
  77. package/dist/packages/ui/src/components/ui-builder/internal/canvas/auto-frame.mjs +6 -3
  78. package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.cjs +228 -48
  79. package/dist/packages/ui/src/components/ui-builder/internal/components/add-component-popover.mjs +228 -48
  80. package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.cjs +1 -1
  81. package/dist/packages/ui/src/components/ui-builder/internal/components/element-selector.mjs +1 -1
  82. package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.cjs +4 -2
  83. package/dist/packages/ui/src/components/ui-builder/internal/components/error-fallback.mjs +4 -2
  84. package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.cjs +6 -3
  85. package/dist/packages/ui/src/components/ui-builder/internal/components/multi-select.mjs +6 -3
  86. package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.cjs +67 -0
  87. package/dist/packages/ui/src/components/ui-builder/internal/dnd/draggable-new-component.mjs +62 -0
  88. package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.cjs +181 -37
  89. package/dist/packages/ui/src/components/ui-builder/internal/dnd/drop-zone.mjs +181 -38
  90. package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.cjs +1 -1
  91. package/dist/packages/ui/src/components/ui-builder/internal/editor-panel.mjs +1 -1
  92. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.cjs +1 -1
  93. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-group-control.mjs +1 -1
  94. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.cjs +9 -2
  95. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/classname-control/classname-item-control.mjs +9 -2
  96. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.cjs +3 -2
  97. package/dist/packages/ui/src/components/ui-builder/internal/form-fields/iconname-field.mjs +3 -2
  98. package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.cjs +1 -1
  99. package/dist/packages/ui/src/components/ui-builder/internal/layers-panel.mjs +1 -1
  100. package/dist/packages/ui/src/components/ui-builder/internal/props-panel.cjs +17 -5
  101. package/dist/packages/ui/src/components/ui-builder/internal/props-panel.mjs +17 -5
  102. package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.cjs +70 -16
  103. package/dist/packages/ui/src/components/ui-builder/internal/utils/render-utils.mjs +73 -20
  104. package/dist/packages/ui/src/lib/compose-refs.cjs +56 -0
  105. package/dist/packages/ui/src/lib/compose-refs.mjs +39 -0
  106. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.cjs +14 -9
  107. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context-colission-utils.mjs +14 -9
  108. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.cjs +38 -10
  109. package/dist/packages/ui/src/lib/ui-builder/context/dnd-context.mjs +35 -11
  110. package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.cjs +1 -0
  111. package/dist/packages/ui/src/lib/ui-builder/context/dnd-contexts.mjs +1 -0
  112. package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.cjs +7 -4
  113. package/dist/packages/ui/src/lib/ui-builder/context/drag-overlay.mjs +7 -4
  114. package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.cjs +4 -4
  115. package/dist/packages/ui/src/lib/ui-builder/hooks/use-auto-scroll.mjs +4 -4
  116. package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.cjs +53 -16
  117. package/dist/packages/ui/src/lib/ui-builder/hooks/use-dnd-event-handlers.mjs +53 -16
  118. package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.cjs +23 -7
  119. package/dist/packages/ui/src/lib/ui-builder/hooks/use-drop-validation.mjs +23 -7
  120. package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.cjs +110 -11
  121. package/dist/packages/ui/src/lib/ui-builder/registry/form-field-overrides.mjs +111 -13
  122. package/dist/packages/ui/src/lib/ui-builder/store/editor-store.cjs +3 -2
  123. package/dist/packages/ui/src/lib/ui-builder/store/editor-store.mjs +3 -2
  124. package/dist/packages/ui/src/lib/ui-builder/store/layer-store.cjs +53 -7
  125. package/dist/packages/ui/src/lib/ui-builder/store/layer-store.mjs +54 -8
  126. package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.cjs +4 -3
  127. package/dist/packages/ui/src/lib/ui-builder/store/layer-utils.mjs +4 -3
  128. package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.cjs +12 -0
  129. package/dist/packages/ui/src/lib/ui-builder/utils/variable-resolver.mjs +12 -1
  130. package/dist/plugins/blog/api/index.d.cts +1 -1
  131. package/dist/plugins/blog/api/index.d.mts +1 -1
  132. package/dist/plugins/blog/api/index.d.ts +1 -1
  133. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  134. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  135. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  136. package/dist/plugins/blog/client/index.d.cts +1 -1
  137. package/dist/plugins/blog/client/index.d.mts +1 -1
  138. package/dist/plugins/blog/client/index.d.ts +1 -1
  139. package/dist/plugins/blog/query-keys.d.cts +2 -2
  140. package/dist/plugins/blog/query-keys.d.mts +2 -2
  141. package/dist/plugins/blog/query-keys.d.ts +2 -2
  142. package/dist/plugins/kanban/api/index.cjs +7 -0
  143. package/dist/plugins/kanban/api/index.d.cts +403 -0
  144. package/dist/plugins/kanban/api/index.d.mts +403 -0
  145. package/dist/plugins/kanban/api/index.d.ts +403 -0
  146. package/dist/plugins/kanban/api/index.mjs +1 -0
  147. package/dist/plugins/kanban/client/components/index.cjs +35 -0
  148. package/dist/plugins/kanban/client/components/index.d.cts +102 -0
  149. package/dist/plugins/kanban/client/components/index.d.mts +102 -0
  150. package/dist/plugins/kanban/client/components/index.d.ts +102 -0
  151. package/dist/plugins/kanban/client/components/index.mjs +15 -0
  152. package/dist/plugins/kanban/client/hooks/index.cjs +15 -0
  153. package/dist/plugins/kanban/client/hooks/index.d.cts +143 -0
  154. package/dist/plugins/kanban/client/hooks/index.d.mts +143 -0
  155. package/dist/plugins/kanban/client/hooks/index.d.ts +143 -0
  156. package/dist/plugins/kanban/client/hooks/index.mjs +1 -0
  157. package/dist/plugins/kanban/client/index.cjs +7 -0
  158. package/dist/plugins/kanban/client/index.d.cts +196 -0
  159. package/dist/plugins/kanban/client/index.d.mts +196 -0
  160. package/dist/plugins/kanban/client/index.d.ts +196 -0
  161. package/dist/plugins/kanban/client/index.mjs +1 -0
  162. package/dist/plugins/kanban/client.css +68 -0
  163. package/dist/plugins/kanban/query-keys.cjs +105 -0
  164. package/dist/plugins/kanban/query-keys.d.cts +59 -0
  165. package/dist/plugins/kanban/query-keys.d.mts +59 -0
  166. package/dist/plugins/kanban/query-keys.d.ts +59 -0
  167. package/dist/plugins/kanban/query-keys.mjs +103 -0
  168. package/dist/plugins/kanban/style.css +7 -0
  169. package/dist/plugins/ui-builder/client/components/index.d.cts +1 -1
  170. package/dist/plugins/ui-builder/client/components/index.d.mts +1 -1
  171. package/dist/plugins/ui-builder/client/components/index.d.ts +1 -1
  172. package/dist/plugins/ui-builder/client/hooks/index.d.cts +2 -2
  173. package/dist/plugins/ui-builder/client/hooks/index.d.mts +2 -2
  174. package/dist/plugins/ui-builder/client/hooks/index.d.ts +2 -2
  175. package/dist/plugins/ui-builder/client/index.d.cts +17 -7
  176. package/dist/plugins/ui-builder/client/index.d.mts +17 -7
  177. package/dist/plugins/ui-builder/client/index.d.ts +17 -7
  178. package/dist/plugins/ui-builder/index.d.cts +2 -2
  179. package/dist/plugins/ui-builder/index.d.mts +2 -2
  180. package/dist/plugins/ui-builder/index.d.ts +2 -2
  181. package/dist/plugins/ui-builder/style.css +6 -0
  182. package/dist/shared/{stack.BSM2cgoq.d.cts → stack.BYysGdHl.d.cts} +1 -1
  183. package/dist/shared/{stack.CqfZWfjJ.d.cts → stack.BdJFrdyt.d.cts} +8 -2
  184. package/dist/shared/{stack.e1FN86dE.d.mts → stack.ChVuHi5e.d.mts} +8 -2
  185. package/dist/shared/stack.DKDMI-QO.d.cts +70 -0
  186. package/dist/shared/stack.DKDMI-QO.d.mts +70 -0
  187. package/dist/shared/stack.DKDMI-QO.d.ts +70 -0
  188. package/dist/shared/{stack.CLtOoAqF.d.mts → stack.DYCFcnkL.d.mts} +1 -1
  189. package/dist/shared/{stack.MMntCVZZ.d.ts → stack.EhM4pmtN.d.ts} +8 -2
  190. package/dist/shared/stack.FeaWkglm.d.cts +190 -0
  191. package/dist/shared/stack.FeaWkglm.d.mts +190 -0
  192. package/dist/shared/stack.FeaWkglm.d.ts +190 -0
  193. package/dist/shared/{stack.BD1m-4yB.d.ts → stack.kFbDspnF.d.ts} +1 -1
  194. package/package.json +56 -2
  195. package/src/plugins/kanban/api/index.ts +6 -0
  196. package/src/plugins/kanban/api/plugin.ts +1245 -0
  197. package/src/plugins/kanban/client/components/forms/board-form.tsx +108 -0
  198. package/src/plugins/kanban/client/components/forms/column-form.tsx +97 -0
  199. package/src/plugins/kanban/client/components/forms/task-form.tsx +274 -0
  200. package/src/plugins/kanban/client/components/index.tsx +21 -0
  201. package/src/plugins/kanban/client/components/loading/board-skeleton.tsx +49 -0
  202. package/src/plugins/kanban/client/components/loading/boards-list-skeleton.tsx +34 -0
  203. package/src/plugins/kanban/client/components/loading/index.tsx +2 -0
  204. package/src/plugins/kanban/client/components/pages/404-page.tsx +28 -0
  205. package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +575 -0
  206. package/src/plugins/kanban/client/components/pages/board-page.tsx +31 -0
  207. package/src/plugins/kanban/client/components/pages/boards-list-page.internal.tsx +101 -0
  208. package/src/plugins/kanban/client/components/pages/boards-list-page.tsx +26 -0
  209. package/src/plugins/kanban/client/components/pages/new-board-page.internal.tsx +65 -0
  210. package/src/plugins/kanban/client/components/pages/new-board-page.tsx +26 -0
  211. package/src/plugins/kanban/client/components/shared/column-content.tsx +108 -0
  212. package/src/plugins/kanban/client/components/shared/default-error.tsx +32 -0
  213. package/src/plugins/kanban/client/components/shared/empty-state.tsx +37 -0
  214. package/src/plugins/kanban/client/components/shared/kanban-board.tsx +87 -0
  215. package/src/plugins/kanban/client/components/shared/page-wrapper.tsx +20 -0
  216. package/src/plugins/kanban/client/components/shared/task-card.tsx +79 -0
  217. package/src/plugins/kanban/client/components/shared/user-avatar.tsx +63 -0
  218. package/src/plugins/kanban/client/hooks/index.tsx +11 -0
  219. package/src/plugins/kanban/client/hooks/kanban-hooks.tsx +560 -0
  220. package/src/plugins/kanban/client/index.ts +8 -0
  221. package/src/plugins/kanban/client/localization/index.ts +28 -0
  222. package/src/plugins/kanban/client/localization/kanban-common.ts +69 -0
  223. package/src/plugins/kanban/client/localization/kanban-forms.ts +70 -0
  224. package/src/plugins/kanban/client/localization/kanban-list.ts +36 -0
  225. package/src/plugins/kanban/client/overrides.ts +145 -0
  226. package/src/plugins/kanban/client/plugin.tsx +463 -0
  227. package/src/plugins/kanban/client.css +68 -0
  228. package/src/plugins/kanban/db.ts +125 -0
  229. package/src/plugins/kanban/query-keys.ts +154 -0
  230. package/src/plugins/kanban/schemas.ts +143 -0
  231. package/src/plugins/kanban/style.css +7 -0
  232. package/src/plugins/kanban/types.ts +106 -0
  233. package/src/plugins/kanban/utils.ts +107 -0
  234. package/src/plugins/ui-builder/style.css +6 -0
  235. package/dist/shared/{stack.DLhzx1-D.d.mts → stack.CcI4sYJP.d.cts} +1 -1
  236. package/dist/shared/{stack.DLhzx1-D.d.ts → stack.CcI4sYJP.d.mts} +1 -1
  237. package/dist/shared/{stack.DLhzx1-D.d.cts → stack.CcI4sYJP.d.ts} +1 -1
@@ -0,0 +1,844 @@
1
+ import { defineBackendPlugin, createEndpoint } from '@btst/stack/plugins/api';
2
+ import { kanbanSchema } from '../db.mjs';
3
+ import { slugify } from '../utils.mjs';
4
+ import { BoardListQuerySchema, createBoardSchema, updateBoardSchema, createColumnSchema, updateColumnSchema, reorderColumnsSchema, createTaskSchema, updateTaskSchema, moveTaskSchema, reorderTasksSchema } from '../schemas.mjs';
5
+
6
+ const kanbanBackendPlugin = (hooks) => defineBackendPlugin({
7
+ name: "kanban",
8
+ dbPlugin: kanbanSchema,
9
+ routes: (adapter) => {
10
+ const listBoards = createEndpoint(
11
+ "/boards",
12
+ {
13
+ method: "GET",
14
+ query: BoardListQuerySchema
15
+ },
16
+ async (ctx) => {
17
+ const { query, headers } = ctx;
18
+ const context = { query, headers };
19
+ try {
20
+ if (hooks?.onBeforeListBoards) {
21
+ const canList = await hooks.onBeforeListBoards(query, context);
22
+ if (!canList) {
23
+ throw ctx.error(403, {
24
+ message: "Unauthorized: Cannot list boards"
25
+ });
26
+ }
27
+ }
28
+ const whereConditions = [];
29
+ if (query.slug) {
30
+ whereConditions.push({
31
+ field: "slug",
32
+ value: query.slug,
33
+ operator: "eq"
34
+ });
35
+ }
36
+ if (query.ownerId) {
37
+ whereConditions.push({
38
+ field: "ownerId",
39
+ value: query.ownerId,
40
+ operator: "eq"
41
+ });
42
+ }
43
+ if (query.organizationId) {
44
+ whereConditions.push({
45
+ field: "organizationId",
46
+ value: query.organizationId,
47
+ operator: "eq"
48
+ });
49
+ }
50
+ const boards = await adapter.findMany({
51
+ model: "kanbanBoard",
52
+ limit: query.limit ?? 50,
53
+ offset: query.offset ?? 0,
54
+ where: whereConditions,
55
+ sortBy: {
56
+ field: "createdAt",
57
+ direction: "desc"
58
+ },
59
+ join: {
60
+ kanbanColumn: true
61
+ }
62
+ });
63
+ const columnIds = [];
64
+ for (const board of boards) {
65
+ if (board.column) {
66
+ for (const col of board.column) {
67
+ columnIds.push(col.id);
68
+ }
69
+ }
70
+ }
71
+ const tasksByColumn = /* @__PURE__ */ new Map();
72
+ if (columnIds.length > 0) {
73
+ const taskQueries = columnIds.map(
74
+ (columnId) => adapter.findMany({
75
+ model: "kanbanTask",
76
+ where: [
77
+ {
78
+ field: "columnId",
79
+ value: columnId,
80
+ operator: "eq"
81
+ }
82
+ ],
83
+ sortBy: { field: "order", direction: "asc" }
84
+ })
85
+ );
86
+ const taskResults = await Promise.all(taskQueries);
87
+ for (let i = 0; i < columnIds.length; i++) {
88
+ const columnId = columnIds[i];
89
+ const tasks = taskResults[i];
90
+ if (columnId && tasks) {
91
+ tasksByColumn.set(columnId, tasks);
92
+ }
93
+ }
94
+ }
95
+ const result = boards.map((board) => {
96
+ const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
97
+ ...col,
98
+ tasks: tasksByColumn.get(col.id) || []
99
+ }));
100
+ const { column: _, ...boardWithoutJoin } = board;
101
+ return {
102
+ ...boardWithoutJoin,
103
+ columns
104
+ };
105
+ });
106
+ if (hooks?.onBoardsRead) {
107
+ await hooks.onBoardsRead(result, query, context);
108
+ }
109
+ return result;
110
+ } catch (error) {
111
+ if (hooks?.onListBoardsError) {
112
+ await hooks.onListBoardsError(error, context);
113
+ }
114
+ throw error;
115
+ }
116
+ }
117
+ );
118
+ const getBoard = createEndpoint(
119
+ "/boards/:id",
120
+ {
121
+ method: "GET"
122
+ },
123
+ async (ctx) => {
124
+ const { params, headers } = ctx;
125
+ const context = { params, headers };
126
+ try {
127
+ if (hooks?.onBeforeReadBoard) {
128
+ const canRead = await hooks.onBeforeReadBoard(params.id, context);
129
+ if (!canRead) {
130
+ throw ctx.error(403, {
131
+ message: "Unauthorized: Cannot read board"
132
+ });
133
+ }
134
+ }
135
+ const board = await adapter.findOne({
136
+ model: "kanbanBoard",
137
+ where: [
138
+ { field: "id", value: params.id, operator: "eq" }
139
+ ],
140
+ join: {
141
+ kanbanColumn: true
142
+ }
143
+ });
144
+ if (!board) {
145
+ throw ctx.error(404, { message: "Board not found" });
146
+ }
147
+ const columnIds = (board.column || []).map((c) => c.id);
148
+ const tasksByColumn = /* @__PURE__ */ new Map();
149
+ if (columnIds.length > 0) {
150
+ const taskQueries = columnIds.map(
151
+ (columnId) => adapter.findMany({
152
+ model: "kanbanTask",
153
+ where: [
154
+ {
155
+ field: "columnId",
156
+ value: columnId,
157
+ operator: "eq"
158
+ }
159
+ ],
160
+ sortBy: { field: "order", direction: "asc" }
161
+ })
162
+ );
163
+ const taskResults = await Promise.all(taskQueries);
164
+ for (let i = 0; i < columnIds.length; i++) {
165
+ const columnId = columnIds[i];
166
+ const tasks = taskResults[i];
167
+ if (columnId && tasks) {
168
+ tasksByColumn.set(columnId, tasks);
169
+ }
170
+ }
171
+ }
172
+ const columns = (board.column || []).sort((a, b) => a.order - b.order).map((col) => ({
173
+ ...col,
174
+ tasks: tasksByColumn.get(col.id) || []
175
+ }));
176
+ const { column: _, ...boardWithoutJoin } = board;
177
+ const result = {
178
+ ...boardWithoutJoin,
179
+ columns
180
+ };
181
+ if (hooks?.onBoardRead) {
182
+ await hooks.onBoardRead(result, context);
183
+ }
184
+ return result;
185
+ } catch (error) {
186
+ if (hooks?.onReadBoardError) {
187
+ await hooks.onReadBoardError(error, context);
188
+ }
189
+ throw error;
190
+ }
191
+ }
192
+ );
193
+ const createBoard = createEndpoint(
194
+ "/boards",
195
+ {
196
+ method: "POST",
197
+ body: createBoardSchema
198
+ },
199
+ async (ctx) => {
200
+ const context = {
201
+ body: ctx.body,
202
+ headers: ctx.headers
203
+ };
204
+ try {
205
+ if (hooks?.onBeforeCreateBoard) {
206
+ const canCreate = await hooks.onBeforeCreateBoard(
207
+ ctx.body,
208
+ context
209
+ );
210
+ if (!canCreate) {
211
+ throw ctx.error(403, {
212
+ message: "Unauthorized: Cannot create board"
213
+ });
214
+ }
215
+ }
216
+ const { ...boardData } = ctx.body;
217
+ const slug = slugify(boardData.slug || boardData.name);
218
+ if (!slug) {
219
+ throw ctx.error(400, {
220
+ message: "Invalid slug: must contain at least one alphanumeric character"
221
+ });
222
+ }
223
+ let newBoard;
224
+ const createdColumns = [];
225
+ await adapter.transaction(async (tx) => {
226
+ newBoard = await tx.create({
227
+ model: "kanbanBoard",
228
+ data: {
229
+ ...boardData,
230
+ slug,
231
+ createdAt: /* @__PURE__ */ new Date(),
232
+ updatedAt: /* @__PURE__ */ new Date()
233
+ }
234
+ });
235
+ const defaultColumns = [
236
+ { title: "To Do", order: 0, boardId: newBoard.id },
237
+ { title: "In Progress", order: 1, boardId: newBoard.id },
238
+ { title: "Done", order: 2, boardId: newBoard.id }
239
+ ];
240
+ for (const colData of defaultColumns) {
241
+ const col = await tx.create({
242
+ model: "kanbanColumn",
243
+ data: {
244
+ ...colData,
245
+ createdAt: /* @__PURE__ */ new Date(),
246
+ updatedAt: /* @__PURE__ */ new Date()
247
+ }
248
+ });
249
+ createdColumns.push({ ...col, tasks: [] });
250
+ }
251
+ });
252
+ if (!newBoard) {
253
+ throw ctx.error(500, {
254
+ message: "Failed to create board"
255
+ });
256
+ }
257
+ const result = { ...newBoard, columns: createdColumns };
258
+ if (hooks?.onBoardCreated) {
259
+ await hooks.onBoardCreated(result, context);
260
+ }
261
+ return result;
262
+ } catch (error) {
263
+ if (hooks?.onCreateBoardError) {
264
+ await hooks.onCreateBoardError(error, context);
265
+ }
266
+ throw error;
267
+ }
268
+ }
269
+ );
270
+ const updateBoard = createEndpoint(
271
+ "/boards/:id",
272
+ {
273
+ method: "PUT",
274
+ body: updateBoardSchema.omit({ id: true })
275
+ },
276
+ async (ctx) => {
277
+ const context = {
278
+ body: ctx.body,
279
+ params: ctx.params,
280
+ headers: ctx.headers
281
+ };
282
+ try {
283
+ if (hooks?.onBeforeUpdateBoard) {
284
+ const canUpdate = await hooks.onBeforeUpdateBoard(
285
+ ctx.params.id,
286
+ { ...ctx.body, id: ctx.params.id },
287
+ context
288
+ );
289
+ if (!canUpdate) {
290
+ throw ctx.error(403, {
291
+ message: "Unauthorized: Cannot update board"
292
+ });
293
+ }
294
+ }
295
+ const { slug: rawSlug, ...restBoardData } = ctx.body;
296
+ const slugified = rawSlug ? slugify(rawSlug) : void 0;
297
+ if (rawSlug && !slugified) {
298
+ throw ctx.error(400, {
299
+ message: "Invalid slug: must contain at least one alphanumeric character"
300
+ });
301
+ }
302
+ const boardData = {
303
+ ...restBoardData,
304
+ ...slugified ? { slug: slugified } : {}
305
+ };
306
+ const updated = await adapter.update({
307
+ model: "kanbanBoard",
308
+ where: [{ field: "id", value: ctx.params.id }],
309
+ update: {
310
+ ...boardData,
311
+ updatedAt: /* @__PURE__ */ new Date()
312
+ }
313
+ });
314
+ if (!updated) {
315
+ throw ctx.error(404, { message: "Board not found" });
316
+ }
317
+ if (hooks?.onBoardUpdated) {
318
+ await hooks.onBoardUpdated(updated, context);
319
+ }
320
+ return updated;
321
+ } catch (error) {
322
+ if (hooks?.onUpdateBoardError) {
323
+ await hooks.onUpdateBoardError(error, context);
324
+ }
325
+ throw error;
326
+ }
327
+ }
328
+ );
329
+ const deleteBoard = createEndpoint(
330
+ "/boards/:id",
331
+ {
332
+ method: "DELETE"
333
+ },
334
+ async (ctx) => {
335
+ const context = {
336
+ params: ctx.params,
337
+ headers: ctx.headers
338
+ };
339
+ try {
340
+ const existingBoard = await adapter.findOne({
341
+ model: "kanbanBoard",
342
+ where: [
343
+ { field: "id", value: ctx.params.id, operator: "eq" }
344
+ ]
345
+ });
346
+ if (!existingBoard) {
347
+ throw ctx.error(404, { message: "Board not found" });
348
+ }
349
+ if (hooks?.onBeforeDeleteBoard) {
350
+ const canDelete = await hooks.onBeforeDeleteBoard(
351
+ ctx.params.id,
352
+ context
353
+ );
354
+ if (!canDelete) {
355
+ throw ctx.error(403, {
356
+ message: "Unauthorized: Cannot delete board"
357
+ });
358
+ }
359
+ }
360
+ await adapter.delete({
361
+ model: "kanbanBoard",
362
+ where: [{ field: "id", value: ctx.params.id }]
363
+ });
364
+ if (hooks?.onBoardDeleted) {
365
+ await hooks.onBoardDeleted(ctx.params.id, context);
366
+ }
367
+ return { success: true };
368
+ } catch (error) {
369
+ if (hooks?.onDeleteBoardError) {
370
+ await hooks.onDeleteBoardError(error, context);
371
+ }
372
+ throw error;
373
+ }
374
+ }
375
+ );
376
+ const createColumn = createEndpoint(
377
+ "/columns",
378
+ {
379
+ method: "POST",
380
+ body: createColumnSchema
381
+ },
382
+ async (ctx) => {
383
+ const context = {
384
+ body: ctx.body,
385
+ headers: ctx.headers
386
+ };
387
+ try {
388
+ if (hooks?.onBeforeCreateColumn) {
389
+ const canCreate = await hooks.onBeforeCreateColumn(
390
+ ctx.body,
391
+ context
392
+ );
393
+ if (!canCreate) {
394
+ throw ctx.error(403, {
395
+ message: "Unauthorized: Cannot create column"
396
+ });
397
+ }
398
+ }
399
+ const existingColumns = await adapter.findMany({
400
+ model: "kanbanColumn",
401
+ where: [
402
+ {
403
+ field: "boardId",
404
+ value: ctx.body.boardId,
405
+ operator: "eq"
406
+ }
407
+ ]
408
+ });
409
+ const nextOrder = existingColumns.length > 0 ? Math.max(...existingColumns.map((c) => c.order)) + 1 : 0;
410
+ const newColumn = await adapter.create({
411
+ model: "kanbanColumn",
412
+ data: {
413
+ ...ctx.body,
414
+ order: ctx.body.order ?? nextOrder,
415
+ createdAt: /* @__PURE__ */ new Date(),
416
+ updatedAt: /* @__PURE__ */ new Date()
417
+ }
418
+ });
419
+ if (hooks?.onColumnCreated) {
420
+ await hooks.onColumnCreated(newColumn, context);
421
+ }
422
+ return newColumn;
423
+ } catch (error) {
424
+ throw error;
425
+ }
426
+ }
427
+ );
428
+ const updateColumn = createEndpoint(
429
+ "/columns/:id",
430
+ {
431
+ method: "PUT",
432
+ body: updateColumnSchema.omit({ id: true })
433
+ },
434
+ async (ctx) => {
435
+ const context = {
436
+ body: ctx.body,
437
+ params: ctx.params,
438
+ headers: ctx.headers
439
+ };
440
+ try {
441
+ if (hooks?.onBeforeUpdateColumn) {
442
+ const canUpdate = await hooks.onBeforeUpdateColumn(
443
+ ctx.params.id,
444
+ { ...ctx.body, id: ctx.params.id },
445
+ context
446
+ );
447
+ if (!canUpdate) {
448
+ throw ctx.error(403, {
449
+ message: "Unauthorized: Cannot update column"
450
+ });
451
+ }
452
+ }
453
+ const updated = await adapter.update({
454
+ model: "kanbanColumn",
455
+ where: [{ field: "id", value: ctx.params.id }],
456
+ update: {
457
+ ...ctx.body,
458
+ updatedAt: /* @__PURE__ */ new Date()
459
+ }
460
+ });
461
+ if (!updated) {
462
+ throw ctx.error(404, { message: "Column not found" });
463
+ }
464
+ if (hooks?.onColumnUpdated) {
465
+ await hooks.onColumnUpdated(updated, context);
466
+ }
467
+ return updated;
468
+ } catch (error) {
469
+ throw error;
470
+ }
471
+ }
472
+ );
473
+ const deleteColumn = createEndpoint(
474
+ "/columns/:id",
475
+ {
476
+ method: "DELETE"
477
+ },
478
+ async (ctx) => {
479
+ const context = {
480
+ params: ctx.params,
481
+ headers: ctx.headers
482
+ };
483
+ try {
484
+ const existingColumn = await adapter.findOne({
485
+ model: "kanbanColumn",
486
+ where: [
487
+ { field: "id", value: ctx.params.id, operator: "eq" }
488
+ ]
489
+ });
490
+ if (!existingColumn) {
491
+ throw ctx.error(404, { message: "Column not found" });
492
+ }
493
+ if (hooks?.onBeforeDeleteColumn) {
494
+ const canDelete = await hooks.onBeforeDeleteColumn(
495
+ ctx.params.id,
496
+ context
497
+ );
498
+ if (!canDelete) {
499
+ throw ctx.error(403, {
500
+ message: "Unauthorized: Cannot delete column"
501
+ });
502
+ }
503
+ }
504
+ await adapter.delete({
505
+ model: "kanbanColumn",
506
+ where: [{ field: "id", value: ctx.params.id }]
507
+ });
508
+ if (hooks?.onColumnDeleted) {
509
+ await hooks.onColumnDeleted(ctx.params.id, context);
510
+ }
511
+ return { success: true };
512
+ } catch (error) {
513
+ throw error;
514
+ }
515
+ }
516
+ );
517
+ const reorderColumns = createEndpoint(
518
+ "/columns/reorder",
519
+ {
520
+ method: "POST",
521
+ body: reorderColumnsSchema
522
+ },
523
+ async (ctx) => {
524
+ const { boardId, columnIds } = ctx.body;
525
+ const context = {
526
+ body: ctx.body,
527
+ headers: ctx.headers
528
+ };
529
+ if (hooks?.onBeforeUpdateColumn) {
530
+ for (let i = 0; i < columnIds.length; i++) {
531
+ const columnId = columnIds[i];
532
+ if (!columnId) continue;
533
+ const canUpdate = await hooks.onBeforeUpdateColumn(
534
+ columnId,
535
+ { id: columnId, order: i },
536
+ context
537
+ );
538
+ if (!canUpdate) {
539
+ throw ctx.error(403, {
540
+ message: "Unauthorized: Cannot reorder columns"
541
+ });
542
+ }
543
+ }
544
+ }
545
+ const updatedColumns = [];
546
+ await adapter.transaction(async (tx) => {
547
+ for (let i = 0; i < columnIds.length; i++) {
548
+ const columnId = columnIds[i];
549
+ if (!columnId) continue;
550
+ const updated = await tx.update({
551
+ model: "kanbanColumn",
552
+ where: [
553
+ { field: "id", value: columnId },
554
+ { field: "boardId", value: boardId, operator: "eq" }
555
+ ],
556
+ update: { order: i, updatedAt: /* @__PURE__ */ new Date() }
557
+ });
558
+ if (updated) {
559
+ updatedColumns.push(updated);
560
+ }
561
+ }
562
+ });
563
+ if (hooks?.onColumnUpdated) {
564
+ for (const column of updatedColumns) {
565
+ await hooks.onColumnUpdated(column, context);
566
+ }
567
+ }
568
+ return { success: true };
569
+ }
570
+ );
571
+ const createTask = createEndpoint(
572
+ "/tasks",
573
+ {
574
+ method: "POST",
575
+ body: createTaskSchema
576
+ },
577
+ async (ctx) => {
578
+ const context = {
579
+ body: ctx.body,
580
+ headers: ctx.headers
581
+ };
582
+ try {
583
+ if (hooks?.onBeforeCreateTask) {
584
+ const canCreate = await hooks.onBeforeCreateTask(
585
+ ctx.body,
586
+ context
587
+ );
588
+ if (!canCreate) {
589
+ throw ctx.error(403, {
590
+ message: "Unauthorized: Cannot create task"
591
+ });
592
+ }
593
+ }
594
+ const existingTasks = await adapter.findMany({
595
+ model: "kanbanTask",
596
+ where: [
597
+ {
598
+ field: "columnId",
599
+ value: ctx.body.columnId,
600
+ operator: "eq"
601
+ }
602
+ ]
603
+ });
604
+ const nextOrder = existingTasks.length > 0 ? Math.max(...existingTasks.map((t) => t.order)) + 1 : 0;
605
+ const taskData = {
606
+ title: ctx.body.title,
607
+ columnId: ctx.body.columnId,
608
+ description: ctx.body.description,
609
+ priority: ctx.body.priority || "MEDIUM",
610
+ order: ctx.body.order ?? nextOrder,
611
+ assigneeId: ctx.body.assigneeId ?? void 0,
612
+ isArchived: ctx.body.isArchived ?? false,
613
+ createdAt: /* @__PURE__ */ new Date(),
614
+ updatedAt: /* @__PURE__ */ new Date()
615
+ };
616
+ const newTask = await adapter.create({
617
+ model: "kanbanTask",
618
+ data: taskData
619
+ });
620
+ if (hooks?.onTaskCreated) {
621
+ await hooks.onTaskCreated(newTask, context);
622
+ }
623
+ return newTask;
624
+ } catch (error) {
625
+ throw error;
626
+ }
627
+ }
628
+ );
629
+ const updateTask = createEndpoint(
630
+ "/tasks/:id",
631
+ {
632
+ method: "PUT",
633
+ body: updateTaskSchema.omit({ id: true })
634
+ },
635
+ async (ctx) => {
636
+ const context = {
637
+ body: ctx.body,
638
+ params: ctx.params,
639
+ headers: ctx.headers
640
+ };
641
+ try {
642
+ if (hooks?.onBeforeUpdateTask) {
643
+ const canUpdate = await hooks.onBeforeUpdateTask(
644
+ ctx.params.id,
645
+ { ...ctx.body, id: ctx.params.id },
646
+ context
647
+ );
648
+ if (!canUpdate) {
649
+ throw ctx.error(403, {
650
+ message: "Unauthorized: Cannot update task"
651
+ });
652
+ }
653
+ }
654
+ const updated = await adapter.update({
655
+ model: "kanbanTask",
656
+ where: [{ field: "id", value: ctx.params.id }],
657
+ update: {
658
+ ...ctx.body,
659
+ updatedAt: /* @__PURE__ */ new Date()
660
+ }
661
+ });
662
+ if (!updated) {
663
+ throw ctx.error(404, { message: "Task not found" });
664
+ }
665
+ if (hooks?.onTaskUpdated) {
666
+ await hooks.onTaskUpdated(updated, context);
667
+ }
668
+ return updated;
669
+ } catch (error) {
670
+ throw error;
671
+ }
672
+ }
673
+ );
674
+ const deleteTask = createEndpoint(
675
+ "/tasks/:id",
676
+ {
677
+ method: "DELETE"
678
+ },
679
+ async (ctx) => {
680
+ const context = {
681
+ params: ctx.params,
682
+ headers: ctx.headers
683
+ };
684
+ try {
685
+ const existingTask = await adapter.findOne({
686
+ model: "kanbanTask",
687
+ where: [
688
+ { field: "id", value: ctx.params.id, operator: "eq" }
689
+ ]
690
+ });
691
+ if (!existingTask) {
692
+ throw ctx.error(404, { message: "Task not found" });
693
+ }
694
+ if (hooks?.onBeforeDeleteTask) {
695
+ const canDelete = await hooks.onBeforeDeleteTask(
696
+ ctx.params.id,
697
+ context
698
+ );
699
+ if (!canDelete) {
700
+ throw ctx.error(403, {
701
+ message: "Unauthorized: Cannot delete task"
702
+ });
703
+ }
704
+ }
705
+ await adapter.delete({
706
+ model: "kanbanTask",
707
+ where: [{ field: "id", value: ctx.params.id }]
708
+ });
709
+ if (hooks?.onTaskDeleted) {
710
+ await hooks.onTaskDeleted(ctx.params.id, context);
711
+ }
712
+ return { success: true };
713
+ } catch (error) {
714
+ throw error;
715
+ }
716
+ }
717
+ );
718
+ const moveTask = createEndpoint(
719
+ "/tasks/move",
720
+ {
721
+ method: "POST",
722
+ body: moveTaskSchema
723
+ },
724
+ async (ctx) => {
725
+ const { taskId, targetColumnId, targetOrder } = ctx.body;
726
+ const context = {
727
+ body: ctx.body,
728
+ headers: ctx.headers
729
+ };
730
+ const task = await adapter.findOne({
731
+ model: "kanbanTask",
732
+ where: [{ field: "id", value: taskId, operator: "eq" }]
733
+ });
734
+ if (!task) {
735
+ throw ctx.error(404, { message: "Task not found" });
736
+ }
737
+ if (hooks?.onBeforeUpdateTask) {
738
+ const canUpdate = await hooks.onBeforeUpdateTask(
739
+ taskId,
740
+ { id: taskId, columnId: targetColumnId, order: targetOrder },
741
+ context
742
+ );
743
+ if (!canUpdate) {
744
+ throw ctx.error(403, {
745
+ message: "Unauthorized: Cannot move task"
746
+ });
747
+ }
748
+ }
749
+ const updated = await adapter.update({
750
+ model: "kanbanTask",
751
+ where: [{ field: "id", value: taskId }],
752
+ update: {
753
+ columnId: targetColumnId,
754
+ order: targetOrder,
755
+ updatedAt: /* @__PURE__ */ new Date()
756
+ }
757
+ });
758
+ if (!updated) {
759
+ throw ctx.error(404, { message: "Task not found" });
760
+ }
761
+ if (hooks?.onTaskUpdated) {
762
+ await hooks.onTaskUpdated(updated, context);
763
+ }
764
+ return updated;
765
+ }
766
+ );
767
+ const reorderTasks = createEndpoint(
768
+ "/tasks/reorder",
769
+ {
770
+ method: "POST",
771
+ body: reorderTasksSchema
772
+ },
773
+ async (ctx) => {
774
+ const { columnId, taskIds } = ctx.body;
775
+ const context = {
776
+ body: ctx.body,
777
+ headers: ctx.headers
778
+ };
779
+ if (hooks?.onBeforeUpdateTask) {
780
+ for (let i = 0; i < taskIds.length; i++) {
781
+ const taskId = taskIds[i];
782
+ if (!taskId) continue;
783
+ const canUpdate = await hooks.onBeforeUpdateTask(
784
+ taskId,
785
+ { id: taskId, order: i },
786
+ context
787
+ );
788
+ if (!canUpdate) {
789
+ throw ctx.error(403, {
790
+ message: "Unauthorized: Cannot reorder tasks"
791
+ });
792
+ }
793
+ }
794
+ }
795
+ const updatedTasks = [];
796
+ await adapter.transaction(async (tx) => {
797
+ for (let i = 0; i < taskIds.length; i++) {
798
+ const taskId = taskIds[i];
799
+ if (!taskId) continue;
800
+ const updated = await tx.update({
801
+ model: "kanbanTask",
802
+ where: [
803
+ { field: "id", value: taskId },
804
+ {
805
+ field: "columnId",
806
+ value: columnId,
807
+ operator: "eq"
808
+ }
809
+ ],
810
+ update: { order: i, updatedAt: /* @__PURE__ */ new Date() }
811
+ });
812
+ if (updated) {
813
+ updatedTasks.push(updated);
814
+ }
815
+ }
816
+ });
817
+ if (hooks?.onTaskUpdated) {
818
+ for (const task of updatedTasks) {
819
+ await hooks.onTaskUpdated(task, context);
820
+ }
821
+ }
822
+ return { success: true };
823
+ }
824
+ );
825
+ return {
826
+ listBoards,
827
+ getBoard,
828
+ createBoard,
829
+ updateBoard,
830
+ deleteBoard,
831
+ createColumn,
832
+ updateColumn,
833
+ deleteColumn,
834
+ reorderColumns,
835
+ createTask,
836
+ updateTask,
837
+ deleteTask,
838
+ moveTask,
839
+ reorderTasks
840
+ };
841
+ }
842
+ });
843
+
844
+ export { kanbanBackendPlugin };