@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,108 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Button } from "@workspace/ui/components/button";
5
+ import { Input } from "@workspace/ui/components/input";
6
+ import { Textarea } from "@workspace/ui/components/textarea";
7
+ import { Label } from "@workspace/ui/components/label";
8
+ import { useBoardMutations } from "../../hooks/kanban-hooks";
9
+ import type { SerializedBoard } from "../../../types";
10
+
11
+ interface BoardFormProps {
12
+ board?: SerializedBoard;
13
+ onClose: () => void;
14
+ onSuccess: (boardId: string) => void;
15
+ }
16
+
17
+ export function BoardForm({ board, onClose, onSuccess }: BoardFormProps) {
18
+ const isEditing = !!board;
19
+ const { createBoard, updateBoard, isCreating, isUpdating } =
20
+ useBoardMutations();
21
+
22
+ const [name, setName] = useState(board?.name || "");
23
+ const [description, setDescription] = useState(board?.description || "");
24
+ const [error, setError] = useState<string | null>(null);
25
+
26
+ const isPending = isCreating || isUpdating;
27
+
28
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
29
+ e.preventDefault();
30
+ setError(null);
31
+
32
+ if (!name.trim()) {
33
+ setError("Name is required");
34
+ return;
35
+ }
36
+
37
+ try {
38
+ if (isEditing && board) {
39
+ await updateBoard(board.id, { name, description });
40
+ onSuccess(board.id);
41
+ } else {
42
+ const newBoard = await createBoard({ name, description });
43
+ if (newBoard?.id) {
44
+ onSuccess(newBoard.id);
45
+ }
46
+ }
47
+ } catch (err) {
48
+ setError(err instanceof Error ? err.message : "An error occurred");
49
+ }
50
+ };
51
+
52
+ return (
53
+ <form onSubmit={handleSubmit} className="space-y-4">
54
+ <div className="space-y-2">
55
+ <Label htmlFor="name">Name *</Label>
56
+ <Input
57
+ id="name"
58
+ value={name}
59
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
60
+ setName(e.target.value)
61
+ }
62
+ placeholder="e.g., Project Alpha"
63
+ disabled={isPending}
64
+ />
65
+ </div>
66
+
67
+ <div className="space-y-2">
68
+ <Label htmlFor="description">Description</Label>
69
+ <Textarea
70
+ id="description"
71
+ value={description}
72
+ onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) =>
73
+ setDescription(e.target.value)
74
+ }
75
+ placeholder="Describe your board..."
76
+ disabled={isPending}
77
+ rows={3}
78
+ />
79
+ </div>
80
+
81
+ {error && (
82
+ <div className="p-3 text-sm text-red-600 bg-red-50 border border-red-200 rounded-md">
83
+ {error}
84
+ </div>
85
+ )}
86
+
87
+ <div className="flex gap-2 pt-2">
88
+ <Button type="submit" disabled={isPending}>
89
+ {isPending
90
+ ? isEditing
91
+ ? "Updating..."
92
+ : "Creating..."
93
+ : isEditing
94
+ ? "Update Board"
95
+ : "Create Board"}
96
+ </Button>
97
+ <Button
98
+ type="button"
99
+ variant="outline"
100
+ onClick={onClose}
101
+ disabled={isPending}
102
+ >
103
+ Cancel
104
+ </Button>
105
+ </div>
106
+ </form>
107
+ );
108
+ }
@@ -0,0 +1,97 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Button } from "@workspace/ui/components/button";
5
+ import { Input } from "@workspace/ui/components/input";
6
+ import { Label } from "@workspace/ui/components/label";
7
+ import { useColumnMutations } from "../../hooks/kanban-hooks";
8
+ import type { SerializedColumn } from "../../../types";
9
+
10
+ interface ColumnFormProps {
11
+ boardId: string;
12
+ columnId?: string;
13
+ column?: SerializedColumn;
14
+ onClose: () => void;
15
+ onSuccess: () => void;
16
+ }
17
+
18
+ export function ColumnForm({
19
+ boardId,
20
+ columnId,
21
+ column,
22
+ onClose,
23
+ onSuccess,
24
+ }: ColumnFormProps) {
25
+ const isEditing = !!columnId;
26
+ const { createColumn, updateColumn, isCreating, isUpdating } =
27
+ useColumnMutations();
28
+
29
+ const [title, setTitle] = useState(column?.title || "");
30
+ const [error, setError] = useState<string | null>(null);
31
+
32
+ const isPending = isCreating || isUpdating;
33
+
34
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
35
+ e.preventDefault();
36
+ setError(null);
37
+
38
+ if (!title.trim()) {
39
+ setError("Title is required");
40
+ return;
41
+ }
42
+
43
+ try {
44
+ if (isEditing && columnId) {
45
+ await updateColumn(columnId, { title });
46
+ } else {
47
+ await createColumn({ title, boardId });
48
+ }
49
+ onSuccess();
50
+ } catch (err) {
51
+ setError(err instanceof Error ? err.message : "An error occurred");
52
+ }
53
+ };
54
+
55
+ return (
56
+ <form onSubmit={handleSubmit} className="space-y-4">
57
+ <div className="space-y-2">
58
+ <Label htmlFor="title">Title *</Label>
59
+ <Input
60
+ id="title"
61
+ value={title}
62
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
63
+ setTitle(e.target.value)
64
+ }
65
+ placeholder="e.g., To Do"
66
+ disabled={isPending}
67
+ />
68
+ </div>
69
+
70
+ {error && (
71
+ <div className="p-3 text-sm text-red-600 bg-red-50 border border-red-200 rounded-md">
72
+ {error}
73
+ </div>
74
+ )}
75
+
76
+ <div className="flex gap-2 pt-2">
77
+ <Button type="submit" disabled={isPending}>
78
+ {isPending
79
+ ? isEditing
80
+ ? "Updating..."
81
+ : "Creating..."
82
+ : isEditing
83
+ ? "Update Column"
84
+ : "Create Column"}
85
+ </Button>
86
+ <Button
87
+ type="button"
88
+ variant="outline"
89
+ onClick={onClose}
90
+ disabled={isPending}
91
+ >
92
+ Cancel
93
+ </Button>
94
+ </div>
95
+ </form>
96
+ );
97
+ }
@@ -0,0 +1,274 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Trash2 } from "lucide-react";
5
+ import { Button } from "@workspace/ui/components/button";
6
+ import { Input } from "@workspace/ui/components/input";
7
+ import { Label } from "@workspace/ui/components/label";
8
+ import {
9
+ Select,
10
+ SelectContent,
11
+ SelectItem,
12
+ SelectTrigger,
13
+ SelectValue,
14
+ } from "@workspace/ui/components/select";
15
+ import { MinimalTiptapEditor } from "@workspace/ui/components/minimal-tiptap";
16
+ import SearchSelect from "@workspace/ui/components/search-select";
17
+ import { useTaskMutations, useSearchUsers } from "../../hooks/kanban-hooks";
18
+ import { PRIORITY_OPTIONS } from "../../../utils";
19
+ import type {
20
+ SerializedColumn,
21
+ SerializedTask,
22
+ Priority,
23
+ } from "../../../types";
24
+
25
+ interface TaskFormProps {
26
+ columnId: string;
27
+ boardId: string;
28
+ taskId?: string;
29
+ task?: SerializedTask;
30
+ columns: SerializedColumn[];
31
+ onClose: () => void;
32
+ onSuccess: () => void;
33
+ onDelete?: () => void;
34
+ }
35
+
36
+ export function TaskForm({
37
+ columnId,
38
+ boardId,
39
+ taskId,
40
+ task,
41
+ columns,
42
+ onClose,
43
+ onSuccess,
44
+ onDelete,
45
+ }: TaskFormProps) {
46
+ const isEditing = !!taskId;
47
+ const {
48
+ createTask,
49
+ updateTask,
50
+ moveTask,
51
+ isCreating,
52
+ isUpdating,
53
+ isDeleting,
54
+ isMoving,
55
+ } = useTaskMutations();
56
+
57
+ const [title, setTitle] = useState(task?.title || "");
58
+ const [description, setDescription] = useState(task?.description || "");
59
+ const [priority, setPriority] = useState<Priority>(
60
+ task?.priority || "MEDIUM",
61
+ );
62
+ const [selectedColumnId, setSelectedColumnId] = useState(
63
+ task?.columnId || columnId,
64
+ );
65
+ const [assigneeId, setAssigneeId] = useState<string>(task?.assigneeId || "");
66
+ const [error, setError] = useState<string | null>(null);
67
+
68
+ // Fetch available users for assignment
69
+ const { data: users = [] } = useSearchUsers("", boardId);
70
+ const userOptions = [
71
+ { value: "", label: "Unassigned" },
72
+ ...users.map((user) => ({ value: user.id, label: user.name })),
73
+ ];
74
+
75
+ const isPending = isCreating || isUpdating || isDeleting || isMoving;
76
+
77
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
78
+ e.preventDefault();
79
+ setError(null);
80
+
81
+ if (!title.trim()) {
82
+ setError("Title is required");
83
+ return;
84
+ }
85
+
86
+ try {
87
+ if (isEditing && taskId) {
88
+ const isColumnChanging =
89
+ task?.columnId && selectedColumnId !== task.columnId;
90
+
91
+ if (isColumnChanging) {
92
+ // When changing columns, we need two operations:
93
+ // 1. Update task properties (title, description, priority, assigneeId)
94
+ // 2. Move task to new column with proper order calculation
95
+ //
96
+ // To avoid partial failure confusion, we attempt both operations
97
+ // but provide clear messaging if one succeeds and the other fails.
98
+
99
+ // First update the task properties (title, description, priority, assigneeId)
100
+ // If this fails, nothing is saved and the outer catch handles it
101
+ await updateTask(taskId, {
102
+ title,
103
+ description,
104
+ priority,
105
+ assigneeId: assigneeId || null,
106
+ });
107
+
108
+ // Then move the task to the new column with calculated order
109
+ // Place at the end of the destination column
110
+ try {
111
+ const targetColumn = columns.find((c) => c.id === selectedColumnId);
112
+ const targetTasks = targetColumn?.tasks || [];
113
+ const targetOrder =
114
+ targetTasks.length > 0
115
+ ? Math.max(...targetTasks.map((t) => t.order)) + 1
116
+ : 0;
117
+
118
+ await moveTask(taskId, selectedColumnId, targetOrder);
119
+ } catch (moveErr) {
120
+ // Properties were saved but column move failed
121
+ // Provide specific error message about partial success
122
+ const moveErrorMsg =
123
+ moveErr instanceof Error ? moveErr.message : "Unknown error";
124
+ setError(
125
+ `Task properties were saved, but moving to the new column failed: ${moveErrorMsg}. ` +
126
+ `You can try dragging the task to the desired column.`,
127
+ );
128
+ // Don't call onSuccess since the operation wasn't fully completed
129
+ // but also don't throw - we want to show the specific error
130
+ return;
131
+ }
132
+ } else {
133
+ // Same column - just update the task properties
134
+ await updateTask(taskId, {
135
+ title,
136
+ description,
137
+ priority,
138
+ columnId: selectedColumnId,
139
+ assigneeId: assigneeId || null,
140
+ });
141
+ }
142
+ } else {
143
+ await createTask({
144
+ title,
145
+ description,
146
+ priority,
147
+ columnId: selectedColumnId,
148
+ assigneeId: assigneeId || undefined,
149
+ });
150
+ }
151
+ onSuccess();
152
+ } catch (err) {
153
+ setError(err instanceof Error ? err.message : "An error occurred");
154
+ }
155
+ };
156
+
157
+ return (
158
+ <form onSubmit={handleSubmit} className="space-y-4">
159
+ <div className="space-y-2">
160
+ <Label htmlFor="title">Title *</Label>
161
+ <Input
162
+ id="title"
163
+ value={title}
164
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
165
+ setTitle(e.target.value)
166
+ }
167
+ placeholder="e.g., Fix login bug"
168
+ disabled={isPending}
169
+ />
170
+ </div>
171
+
172
+ <div className="grid grid-cols-2 gap-4">
173
+ <div className="space-y-2">
174
+ <Label htmlFor="priority">Priority</Label>
175
+ <Select
176
+ value={priority}
177
+ onValueChange={(v: string) => setPriority(v as Priority)}
178
+ >
179
+ <SelectTrigger>
180
+ <SelectValue placeholder="Select priority" />
181
+ </SelectTrigger>
182
+ <SelectContent>
183
+ {PRIORITY_OPTIONS.map((option) => (
184
+ <SelectItem key={option.value} value={option.value}>
185
+ {option.label}
186
+ </SelectItem>
187
+ ))}
188
+ </SelectContent>
189
+ </Select>
190
+ </div>
191
+
192
+ <div className="space-y-2">
193
+ <Label htmlFor="column">Column</Label>
194
+ <Select value={selectedColumnId} onValueChange={setSelectedColumnId}>
195
+ <SelectTrigger>
196
+ <SelectValue placeholder="Select column" />
197
+ </SelectTrigger>
198
+ <SelectContent>
199
+ {columns.map((col) => (
200
+ <SelectItem key={col.id} value={col.id}>
201
+ {col.title}
202
+ </SelectItem>
203
+ ))}
204
+ </SelectContent>
205
+ </Select>
206
+ </div>
207
+ </div>
208
+
209
+ <div className="space-y-2">
210
+ <Label htmlFor="assignee">Assignee</Label>
211
+ <SearchSelect
212
+ options={userOptions}
213
+ value={assigneeId}
214
+ onChange={setAssigneeId}
215
+ placeholder="Select assignee"
216
+ emptyMessage="No users found"
217
+ />
218
+ </div>
219
+
220
+ <div className="space-y-2">
221
+ <Label>Description</Label>
222
+ <MinimalTiptapEditor
223
+ value={description}
224
+ onChange={(value) =>
225
+ setDescription(typeof value === "string" ? value : "")
226
+ }
227
+ output="markdown"
228
+ placeholder="Describe the task..."
229
+ editable={!isPending}
230
+ className="min-h-[150px]"
231
+ />
232
+ </div>
233
+
234
+ {error && (
235
+ <div className="p-3 text-sm text-red-600 bg-red-50 border border-red-200 rounded-md">
236
+ {error}
237
+ </div>
238
+ )}
239
+
240
+ <div className="flex justify-between pt-2">
241
+ <div className="flex gap-2">
242
+ <Button type="submit" disabled={isPending}>
243
+ {isPending
244
+ ? isEditing
245
+ ? "Updating..."
246
+ : "Creating..."
247
+ : isEditing
248
+ ? "Update Task"
249
+ : "Create Task"}
250
+ </Button>
251
+ <Button
252
+ type="button"
253
+ variant="outline"
254
+ onClick={onClose}
255
+ disabled={isPending}
256
+ >
257
+ Cancel
258
+ </Button>
259
+ </div>
260
+ {isEditing && onDelete && (
261
+ <Button
262
+ type="button"
263
+ variant="destructive"
264
+ onClick={onDelete}
265
+ disabled={isPending}
266
+ >
267
+ <Trash2 className="mr-2 h-4 w-4" />
268
+ Delete
269
+ </Button>
270
+ )}
271
+ </div>
272
+ </form>
273
+ );
274
+ }
@@ -0,0 +1,21 @@
1
+ // Pages
2
+ export { BoardsListPageComponent } from "./pages/boards-list-page";
3
+ export { BoardPageComponent } from "./pages/board-page";
4
+ export { NewBoardPageComponent } from "./pages/new-board-page";
5
+ export { NotFoundPage } from "./pages/404-page";
6
+
7
+ // Shared
8
+ export { PageWrapper } from "./shared/page-wrapper";
9
+ export { EmptyState } from "./shared/empty-state";
10
+ export { DefaultError } from "./shared/default-error";
11
+ export { KanbanBoard } from "./shared/kanban-board";
12
+ export { ColumnContent } from "./shared/column-content";
13
+ export { TaskCard } from "./shared/task-card";
14
+
15
+ // Forms
16
+ export { BoardForm } from "./forms/board-form";
17
+ export { ColumnForm } from "./forms/column-form";
18
+ export { TaskForm } from "./forms/task-form";
19
+
20
+ // Loading
21
+ export { BoardsListSkeleton, BoardSkeleton } from "./loading";
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { Skeleton } from "@workspace/ui/components/skeleton";
4
+
5
+ export function BoardSkeleton() {
6
+ return (
7
+ <div className="container mx-auto py-8 px-4">
8
+ <div className="w-full flex items-center justify-between mb-8">
9
+ <div className="flex items-center gap-4">
10
+ <Skeleton className="h-5 w-5" />
11
+ <div>
12
+ <Skeleton className="h-9 w-48" />
13
+ <Skeleton className="h-5 w-64 mt-2" />
14
+ </div>
15
+ </div>
16
+ <Skeleton className="h-10 w-28" />
17
+ </div>
18
+ <div className="grid gap-4 md:grid-cols-3">
19
+ {Array.from({ length: 3 }).map((_, colIdx) => (
20
+ <div
21
+ key={colIdx}
22
+ className="rounded-lg border bg-zinc-100 dark:bg-zinc-900 p-2.5"
23
+ >
24
+ <div className="flex items-center gap-2 mb-4">
25
+ <Skeleton className="h-8 w-8" />
26
+ <Skeleton className="h-6 w-24" />
27
+ <Skeleton className="h-5 w-8" />
28
+ </div>
29
+ <div className="space-y-2">
30
+ {Array.from({ length: 3 }).map((_, taskIdx) => (
31
+ <div key={taskIdx} className="rounded-md border bg-card p-3">
32
+ <div className="flex items-center gap-2 mb-2">
33
+ <Skeleton className="h-6 w-6" />
34
+ <Skeleton className="h-5 flex-1" />
35
+ <Skeleton className="h-5 w-16" />
36
+ </div>
37
+ <div className="flex items-center justify-between">
38
+ <Skeleton className="h-4 w-20" />
39
+ <Skeleton className="h-4 w-16" />
40
+ </div>
41
+ </div>
42
+ ))}
43
+ </div>
44
+ </div>
45
+ ))}
46
+ </div>
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,34 @@
1
+ "use client";
2
+
3
+ import { Skeleton } from "@workspace/ui/components/skeleton";
4
+ import { Card, CardContent, CardHeader } from "@workspace/ui/components/card";
5
+
6
+ export function BoardsListSkeleton() {
7
+ return (
8
+ <div className="container mx-auto py-8 px-4">
9
+ <div className="w-full flex items-center justify-between mb-8">
10
+ <div>
11
+ <Skeleton className="h-9 w-48" />
12
+ <Skeleton className="h-5 w-64 mt-2" />
13
+ </div>
14
+ <Skeleton className="h-10 w-32" />
15
+ </div>
16
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
17
+ {Array.from({ length: 6 }).map((_, i) => (
18
+ <Card key={i}>
19
+ <CardHeader>
20
+ <Skeleton className="h-6 w-3/4" />
21
+ <Skeleton className="h-4 w-full mt-2" />
22
+ </CardHeader>
23
+ <CardContent>
24
+ <div className="flex items-center justify-between">
25
+ <Skeleton className="h-4 w-20" />
26
+ <Skeleton className="h-4 w-24" />
27
+ </div>
28
+ </CardContent>
29
+ </Card>
30
+ ))}
31
+ </div>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,2 @@
1
+ export { BoardsListSkeleton } from "./boards-list-skeleton";
2
+ export { BoardSkeleton } from "./board-skeleton";
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import { Button } from "@workspace/ui/components/button";
4
+ import { usePluginOverrides } from "@btst/stack/context";
5
+ import type { KanbanPluginOverrides } from "../../overrides";
6
+
7
+ export function NotFoundPage() {
8
+ const { navigate: overrideNavigate } =
9
+ usePluginOverrides<KanbanPluginOverrides>("kanban");
10
+ const navigate =
11
+ overrideNavigate ||
12
+ ((path: string) => {
13
+ window.location.href = path;
14
+ });
15
+
16
+ return (
17
+ <div
18
+ className="flex min-h-[400px] flex-col items-center justify-center text-center"
19
+ data-testid="empty-state"
20
+ >
21
+ <h2 className="text-2xl font-bold mb-2">Page Not Found</h2>
22
+ <p className="text-muted-foreground mb-6">
23
+ The page you're looking for doesn't exist.
24
+ </p>
25
+ <Button onClick={() => navigate("/pages/kanban")}>Back to Boards</Button>
26
+ </div>
27
+ );
28
+ }