@locusai/web 0.1.7 → 0.2.2

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 (346) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/next.config.js +15 -2
  3. package/package.json +26 -3
  4. package/src/app/(auth)/invite/page.tsx +109 -0
  5. package/src/app/(auth)/layout.tsx +19 -0
  6. package/src/app/(auth)/login/page.tsx +65 -0
  7. package/src/app/(auth)/onboarding/workspace/page.tsx +46 -0
  8. package/src/app/(auth)/register/page.tsx +165 -0
  9. package/src/app/(dashboard)/activity/page.tsx +7 -0
  10. package/src/app/(dashboard)/backlog/page.tsx +195 -0
  11. package/src/app/(dashboard)/board/page.tsx +141 -0
  12. package/src/app/(dashboard)/layout.tsx +32 -0
  13. package/src/app/(dashboard)/page.tsx +14 -0
  14. package/src/app/(dashboard)/settings/page.tsx +161 -0
  15. package/src/app/(dashboard)/settings/team/page.tsx +75 -0
  16. package/src/app/globals.css +259 -0
  17. package/src/app/layout.tsx +10 -20
  18. package/src/app/providers.tsx +26 -3
  19. package/src/components/AuthLayoutUI.tsx +53 -0
  20. package/src/components/BoardFilter.tsx +75 -74
  21. package/src/components/CreateModal/CreateModal.tsx +142 -0
  22. package/src/components/CreateModal/index.ts +1 -0
  23. package/src/components/Editor.tsx +279 -0
  24. package/src/components/Header.tsx +99 -12
  25. package/src/components/PageLayout.tsx +69 -0
  26. package/src/components/PropertyItem.tsx +55 -9
  27. package/src/components/Sidebar.tsx +280 -36
  28. package/src/components/SprintCreateModal.tsx +84 -0
  29. package/src/components/TaskCard.tsx +196 -78
  30. package/src/components/TaskCreateModal.tsx +181 -178
  31. package/src/components/TaskPanel.tsx +140 -692
  32. package/src/components/WorkspaceCreateModal.tsx +97 -0
  33. package/src/components/WorkspaceProtected.tsx +91 -0
  34. package/src/components/auth/InviteSteps.tsx +220 -0
  35. package/src/components/auth/LoginSteps.tsx +86 -0
  36. package/src/components/auth/RegisterSteps.tsx +371 -0
  37. package/src/components/auth/index.ts +3 -0
  38. package/src/components/backlog/BacklogList.tsx +92 -0
  39. package/src/components/backlog/BacklogSection.tsx +137 -0
  40. package/src/components/backlog/CompletedSprintItem.tsx +95 -0
  41. package/src/components/backlog/CompletedSprintsSection.tsx +77 -0
  42. package/src/components/backlog/SprintSection.tsx +155 -0
  43. package/src/components/backlog/constants.ts +26 -0
  44. package/src/components/board/BoardColumn.tsx +97 -0
  45. package/src/components/board/BoardContent.tsx +84 -0
  46. package/src/components/board/BoardEmptyState.tsx +82 -0
  47. package/src/components/board/BoardHeader.tsx +47 -0
  48. package/src/components/board/SprintMindmap.tsx +65 -0
  49. package/src/components/board/constants.ts +40 -0
  50. package/src/components/board/index.ts +5 -0
  51. package/src/components/common/ErrorState.tsx +124 -0
  52. package/src/components/common/LoadingState.tsx +83 -0
  53. package/src/components/common/index.ts +40 -0
  54. package/src/components/dashboard/ActivityFeed.tsx +77 -0
  55. package/src/components/dashboard/ActivityItem.tsx +207 -0
  56. package/src/components/dashboard/QuickActions.tsx +50 -0
  57. package/src/components/dashboard/StatCard.tsx +79 -0
  58. package/src/components/dnd/index.tsx +51 -0
  59. package/src/components/docs/DocsEditorArea.tsx +87 -0
  60. package/src/components/docs/DocsHeaderActions.tsx +121 -0
  61. package/src/components/docs/DocsSidebar.tsx +351 -0
  62. package/src/components/index.ts +7 -0
  63. package/src/components/onboarding/StepProgress.tsx +21 -0
  64. package/src/components/onboarding/index.ts +1 -0
  65. package/src/components/settings/ApiKeyConfirmationModal.tsx +81 -0
  66. package/src/components/settings/ApiKeyCreatedModal.tsx +96 -0
  67. package/src/components/settings/ApiKeysList.tsx +143 -0
  68. package/src/components/settings/ApiKeysSettings.tsx +144 -0
  69. package/src/components/settings/InviteMemberModal.tsx +106 -0
  70. package/src/components/settings/ProjectSetupGuide.tsx +147 -0
  71. package/src/components/settings/SettingItem.tsx +32 -0
  72. package/src/components/settings/SettingSection.tsx +50 -0
  73. package/src/components/settings/TeamInvitationsList.tsx +90 -0
  74. package/src/components/settings/TeamMembersList.tsx +95 -0
  75. package/src/components/settings/index.ts +8 -0
  76. package/src/components/task-panel/TaskActivity.tsx +127 -0
  77. package/src/components/task-panel/TaskChecklist.tsx +142 -0
  78. package/src/components/task-panel/TaskDescription.tsx +201 -0
  79. package/src/components/task-panel/TaskDocs.tsx +137 -0
  80. package/src/components/task-panel/TaskHeader.tsx +125 -0
  81. package/src/components/task-panel/TaskProperties.tsx +111 -0
  82. package/src/components/task-panel/index.ts +12 -0
  83. package/src/components/typography/EmptyStateText.tsx +59 -0
  84. package/src/components/typography/MetadataText.tsx +65 -0
  85. package/src/components/typography/SecondaryText.tsx +60 -0
  86. package/src/components/typography/SectionLabel.tsx +60 -0
  87. package/src/components/typography/index.ts +14 -0
  88. package/src/components/typography-scales.tsx +218 -0
  89. package/src/components/ui/Avatar.tsx +123 -0
  90. package/src/components/ui/Badge.tsx +69 -2
  91. package/src/components/ui/Button.tsx +71 -30
  92. package/src/components/ui/Checkbox.tsx +34 -0
  93. package/src/components/ui/Dropdown.tsx +67 -1
  94. package/src/components/ui/EmptyState.tsx +129 -0
  95. package/src/components/ui/Input.tsx +53 -6
  96. package/src/components/ui/Modal.tsx +45 -12
  97. package/src/components/ui/OtpInput.tsx +148 -0
  98. package/src/components/ui/Skeleton.tsx +36 -0
  99. package/src/components/ui/Spinner.tsx +112 -0
  100. package/src/components/ui/Textarea.tsx +28 -3
  101. package/src/components/ui/Toast.tsx +99 -0
  102. package/src/components/ui/Toggle.tsx +63 -0
  103. package/src/components/ui/constants.ts +108 -0
  104. package/src/components/ui/index.ts +7 -0
  105. package/src/context/AuthContext.tsx +140 -0
  106. package/src/context/index.ts +1 -0
  107. package/src/hooks/backlog/index.ts +13 -0
  108. package/src/hooks/backlog/useBacklogActions.ts +144 -0
  109. package/src/hooks/backlog/useBacklogComposite.ts +73 -0
  110. package/src/hooks/backlog/useBacklogData.ts +74 -0
  111. package/src/hooks/backlog/useBacklogDragDrop.ts +118 -0
  112. package/src/hooks/backlog/useBacklogUI.ts +74 -0
  113. package/src/hooks/index.ts +22 -0
  114. package/src/hooks/task-panel/index.ts +13 -0
  115. package/src/hooks/task-panel/useTaskActions.ts +200 -0
  116. package/src/hooks/task-panel/useTaskComputedValues.ts +30 -0
  117. package/src/hooks/task-panel/useTaskData.ts +78 -0
  118. package/src/hooks/task-panel/useTaskPanelComposite.ts +161 -0
  119. package/src/hooks/task-panel/useTaskUIState.ts +80 -0
  120. package/src/hooks/useAuthLayoutLogic.ts +43 -0
  121. package/src/hooks/useAuthenticatedUser.ts +36 -0
  122. package/src/hooks/useAuthenticatedUserWithOrg.ts +41 -0
  123. package/src/hooks/useBacklog.ts +303 -0
  124. package/src/hooks/useBoard.ts +230 -0
  125. package/src/hooks/useDashboardLayout.ts +49 -0
  126. package/src/hooks/useDocs.ts +279 -0
  127. package/src/hooks/useDocsQuery.ts +99 -0
  128. package/src/hooks/useDocsSidebarState.ts +104 -0
  129. package/src/hooks/useFormState.ts +40 -0
  130. package/src/hooks/useGlobalKeydowns.ts +52 -0
  131. package/src/hooks/useInviteForm.ts +122 -0
  132. package/src/hooks/useLoginForm.ts +84 -0
  133. package/src/hooks/useMutationWithToast.ts +56 -0
  134. package/src/hooks/useOrganizationQuery.ts +55 -0
  135. package/src/hooks/useRegisterForm.ts +216 -0
  136. package/src/hooks/useSprintsQuery.ts +38 -0
  137. package/src/hooks/useTaskDescription.ts +102 -0
  138. package/src/hooks/useTaskPanel.ts +341 -0
  139. package/src/hooks/useTasksQuery.ts +39 -0
  140. package/src/hooks/useTeamManagement.ts +92 -0
  141. package/src/hooks/useWorkspaceCreateForm.ts +70 -0
  142. package/src/hooks/useWorkspaceId.ts +29 -0
  143. package/src/lib/api-client.ts +40 -23
  144. package/src/lib/config.ts +25 -0
  145. package/src/lib/constants.ts +83 -0
  146. package/src/lib/options.ts +96 -0
  147. package/src/lib/query-keys.ts +91 -0
  148. package/src/lib/typography.ts +103 -0
  149. package/src/lib/utils.ts +4 -0
  150. package/src/lib/validation.ts +192 -0
  151. package/src/services/index.ts +7 -3
  152. package/src/services/notifications.ts +80 -0
  153. package/src/utils/env.utils.ts +15 -0
  154. package/src/views/ActivityView.tsx +123 -0
  155. package/src/views/Dashboard.tsx +126 -0
  156. package/src/views/Docs.tsx +98 -612
  157. package/tsconfig.tsbuildinfo +1 -1
  158. package/.next/BUILD_ID +0 -1
  159. package/.next/app-build-manifest.json +0 -55
  160. package/.next/app-path-routes-manifest.json +0 -8
  161. package/.next/build-manifest.json +0 -33
  162. package/.next/cache/.previewinfo +0 -1
  163. package/.next/cache/.rscinfo +0 -1
  164. package/.next/cache/.tsbuildinfo +0 -1
  165. package/.next/cache/config.json +0 -7
  166. package/.next/cache/webpack/client-production/0.pack +0 -0
  167. package/.next/cache/webpack/client-production/index.pack +0 -0
  168. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  169. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  170. package/.next/cache/webpack/server-production/0.pack +0 -0
  171. package/.next/cache/webpack/server-production/index.pack +0 -0
  172. package/.next/diagnostics/build-diagnostics.json +0 -6
  173. package/.next/diagnostics/framework.json +0 -1
  174. package/.next/export-detail.json +0 -5
  175. package/.next/export-marker.json +0 -6
  176. package/.next/images-manifest.json +0 -57
  177. package/.next/next-minimal-server.js.nft.json +0 -1
  178. package/.next/next-server.js.nft.json +0 -1
  179. package/.next/package.json +0 -1
  180. package/.next/prerender-manifest.json +0 -137
  181. package/.next/react-loadable-manifest.json +0 -32
  182. package/.next/required-server-files.json +0 -324
  183. package/.next/routes-manifest.json +0 -77
  184. package/.next/server/app/_not-found/page.js +0 -2
  185. package/.next/server/app/_not-found/page.js.nft.json +0 -1
  186. package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  187. package/.next/server/app/_not-found.html +0 -1
  188. package/.next/server/app/_not-found.meta +0 -8
  189. package/.next/server/app/_not-found.rsc +0 -21
  190. package/.next/server/app/backlog/page.js +0 -2
  191. package/.next/server/app/backlog/page.js.nft.json +0 -1
  192. package/.next/server/app/backlog/page_client-reference-manifest.js +0 -1
  193. package/.next/server/app/backlog.html +0 -1
  194. package/.next/server/app/backlog.meta +0 -7
  195. package/.next/server/app/backlog.rsc +0 -25
  196. package/.next/server/app/docs/page.js +0 -97
  197. package/.next/server/app/docs/page.js.nft.json +0 -1
  198. package/.next/server/app/docs/page_client-reference-manifest.js +0 -1
  199. package/.next/server/app/docs.html +0 -1
  200. package/.next/server/app/docs.meta +0 -7
  201. package/.next/server/app/docs.rsc +0 -26
  202. package/.next/server/app/favicon.ico/route.js +0 -1
  203. package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  204. package/.next/server/app/favicon.ico.body +0 -0
  205. package/.next/server/app/favicon.ico.meta +0 -1
  206. package/.next/server/app/index.html +0 -1
  207. package/.next/server/app/index.meta +0 -7
  208. package/.next/server/app/index.rsc +0 -25
  209. package/.next/server/app/page.js +0 -2
  210. package/.next/server/app/page.js.nft.json +0 -1
  211. package/.next/server/app/page_client-reference-manifest.js +0 -1
  212. package/.next/server/app/settings/page.js +0 -2
  213. package/.next/server/app/settings/page.js.nft.json +0 -1
  214. package/.next/server/app/settings/page_client-reference-manifest.js +0 -1
  215. package/.next/server/app/settings.html +0 -1
  216. package/.next/server/app/settings.meta +0 -7
  217. package/.next/server/app/settings.rsc +0 -25
  218. package/.next/server/app-paths-manifest.json +0 -8
  219. package/.next/server/chunks/496.js +0 -6
  220. package/.next/server/chunks/585.js +0 -1
  221. package/.next/server/chunks/665.js +0 -1
  222. package/.next/server/chunks/699.js +0 -22
  223. package/.next/server/chunks/852.js +0 -7
  224. package/.next/server/functions-config-manifest.json +0 -4
  225. package/.next/server/interception-route-rewrite-manifest.js +0 -1
  226. package/.next/server/middleware-build-manifest.js +0 -1
  227. package/.next/server/middleware-manifest.json +0 -6
  228. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  229. package/.next/server/next-font-manifest.js +0 -1
  230. package/.next/server/next-font-manifest.json +0 -1
  231. package/.next/server/pages/404.html +0 -1
  232. package/.next/server/pages/500.html +0 -1
  233. package/.next/server/pages/_app.js +0 -1
  234. package/.next/server/pages/_app.js.nft.json +0 -1
  235. package/.next/server/pages/_document.js +0 -1
  236. package/.next/server/pages/_document.js.nft.json +0 -1
  237. package/.next/server/pages/_error.js +0 -19
  238. package/.next/server/pages/_error.js.nft.json +0 -1
  239. package/.next/server/pages-manifest.json +0 -6
  240. package/.next/server/server-reference-manifest.js +0 -1
  241. package/.next/server/server-reference-manifest.json +0 -1
  242. package/.next/server/webpack-runtime.js +0 -1
  243. package/.next/static/D0NXe04ZCLNDckV_quc8g/_buildManifest.js +0 -1
  244. package/.next/static/D0NXe04ZCLNDckV_quc8g/_ssgManifest.js +0 -1
  245. package/.next/static/chunks/138.b98511c56423f8bb.js +0 -1
  246. package/.next/static/chunks/146-34259952c594a3b0.js +0 -1
  247. package/.next/static/chunks/337-d3bb75304d130513.js +0 -1
  248. package/.next/static/chunks/477.1a6ecfe53375bd9c.js +0 -1
  249. package/.next/static/chunks/487-1808785ba665f784.js +0 -1
  250. package/.next/static/chunks/544.a9569941cc886e9d.js +0 -1
  251. package/.next/static/chunks/87c73c54-1f4741035a95c140.js +0 -1
  252. package/.next/static/chunks/902-d6926825a9fe8784.js +0 -1
  253. package/.next/static/chunks/955-c8f8f6235ae8f8c6.js +0 -1
  254. package/.next/static/chunks/996.e0a334e6ae90900e.js +0 -1
  255. package/.next/static/chunks/app/_not-found/page-44b1804abb44a34d.js +0 -1
  256. package/.next/static/chunks/app/backlog/page-dce1450769bfae8f.js +0 -1
  257. package/.next/static/chunks/app/docs/page-1efee819f25492cb.js +0 -1
  258. package/.next/static/chunks/app/layout-05f504c042b9f7ee.js +0 -1
  259. package/.next/static/chunks/app/page-3fd91aaaa4776ced.js +0 -1
  260. package/.next/static/chunks/app/settings/page-84e16c9638d657e4.js +0 -1
  261. package/.next/static/chunks/framework-152a1bc8c81c7458.js +0 -1
  262. package/.next/static/chunks/main-843ab130fc1be309.js +0 -1
  263. package/.next/static/chunks/main-app-123e879c5a937a00.js +0 -1
  264. package/.next/static/chunks/pages/_app-a050a8e6e4fb04cf.js +0 -1
  265. package/.next/static/chunks/pages/_error-3e422ffd891594de.js +0 -1
  266. package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  267. package/.next/static/chunks/webpack-99a10a055b5bb9c4.js +0 -1
  268. package/.next/static/css/13e8617b72f9d3aa.css +0 -1
  269. package/.next/static/css/8aea088cdc4338f0.css +0 -1
  270. package/.next/static/css/b301ab0424111664.css +0 -1
  271. package/.next/static/media/24c15609eaa28576-s.woff2 +0 -0
  272. package/.next/static/media/2c07349e02a7b712-s.woff2 +0 -0
  273. package/.next/static/media/456105d6ea6d39e0-s.woff2 +0 -0
  274. package/.next/static/media/47cbc4e2adbc5db9-s.p.woff2 +0 -0
  275. package/.next/static/media/4f77bef990aad698-s.woff2 +0 -0
  276. package/.next/static/media/627d916fd739a539-s.woff2 +0 -0
  277. package/.next/static/media/63b255f18bea0ca9-s.woff2 +0 -0
  278. package/.next/static/media/70bd82ac89b4fa42-s.woff2 +0 -0
  279. package/.next/static/media/84602850c8fd81c3-s.woff2 +0 -0
  280. package/.next/trace +0 -46
  281. package/.next/types/app/backlog/page.ts +0 -84
  282. package/.next/types/app/docs/page.ts +0 -84
  283. package/.next/types/app/layout.ts +0 -84
  284. package/.next/types/app/page.ts +0 -84
  285. package/.next/types/app/settings/page.ts +0 -84
  286. package/.next/types/cache-life.d.ts +0 -141
  287. package/.next/types/package.json +0 -1
  288. package/next-env.d.ts +0 -5
  289. package/out/404.html +0 -1
  290. package/out/_next/static/D0NXe04ZCLNDckV_quc8g/_buildManifest.js +0 -1
  291. package/out/_next/static/D0NXe04ZCLNDckV_quc8g/_ssgManifest.js +0 -1
  292. package/out/_next/static/chunks/138.b98511c56423f8bb.js +0 -1
  293. package/out/_next/static/chunks/146-34259952c594a3b0.js +0 -1
  294. package/out/_next/static/chunks/337-d3bb75304d130513.js +0 -1
  295. package/out/_next/static/chunks/477.1a6ecfe53375bd9c.js +0 -1
  296. package/out/_next/static/chunks/487-1808785ba665f784.js +0 -1
  297. package/out/_next/static/chunks/544.a9569941cc886e9d.js +0 -1
  298. package/out/_next/static/chunks/87c73c54-1f4741035a95c140.js +0 -1
  299. package/out/_next/static/chunks/902-d6926825a9fe8784.js +0 -1
  300. package/out/_next/static/chunks/955-c8f8f6235ae8f8c6.js +0 -1
  301. package/out/_next/static/chunks/996.e0a334e6ae90900e.js +0 -1
  302. package/out/_next/static/chunks/app/_not-found/page-44b1804abb44a34d.js +0 -1
  303. package/out/_next/static/chunks/app/backlog/page-dce1450769bfae8f.js +0 -1
  304. package/out/_next/static/chunks/app/docs/page-1efee819f25492cb.js +0 -1
  305. package/out/_next/static/chunks/app/layout-05f504c042b9f7ee.js +0 -1
  306. package/out/_next/static/chunks/app/page-3fd91aaaa4776ced.js +0 -1
  307. package/out/_next/static/chunks/app/settings/page-84e16c9638d657e4.js +0 -1
  308. package/out/_next/static/chunks/framework-152a1bc8c81c7458.js +0 -1
  309. package/out/_next/static/chunks/main-843ab130fc1be309.js +0 -1
  310. package/out/_next/static/chunks/main-app-123e879c5a937a00.js +0 -1
  311. package/out/_next/static/chunks/pages/_app-a050a8e6e4fb04cf.js +0 -1
  312. package/out/_next/static/chunks/pages/_error-3e422ffd891594de.js +0 -1
  313. package/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  314. package/out/_next/static/chunks/webpack-99a10a055b5bb9c4.js +0 -1
  315. package/out/_next/static/css/13e8617b72f9d3aa.css +0 -1
  316. package/out/_next/static/css/8aea088cdc4338f0.css +0 -1
  317. package/out/_next/static/css/b301ab0424111664.css +0 -1
  318. package/out/_next/static/media/24c15609eaa28576-s.woff2 +0 -0
  319. package/out/_next/static/media/2c07349e02a7b712-s.woff2 +0 -0
  320. package/out/_next/static/media/456105d6ea6d39e0-s.woff2 +0 -0
  321. package/out/_next/static/media/47cbc4e2adbc5db9-s.p.woff2 +0 -0
  322. package/out/_next/static/media/4f77bef990aad698-s.woff2 +0 -0
  323. package/out/_next/static/media/627d916fd739a539-s.woff2 +0 -0
  324. package/out/_next/static/media/63b255f18bea0ca9-s.woff2 +0 -0
  325. package/out/_next/static/media/70bd82ac89b4fa42-s.woff2 +0 -0
  326. package/out/_next/static/media/84602850c8fd81c3-s.woff2 +0 -0
  327. package/out/backlog.html +0 -1
  328. package/out/backlog.txt +0 -25
  329. package/out/docs.html +0 -1
  330. package/out/docs.txt +0 -26
  331. package/out/favicon.ico +0 -0
  332. package/out/index.html +0 -1
  333. package/out/index.txt +0 -25
  334. package/out/logo.png +0 -0
  335. package/out/settings.html +0 -1
  336. package/out/settings.txt +0 -25
  337. package/src/app/backlog/page.tsx +0 -19
  338. package/src/app/page.tsx +0 -16
  339. package/src/app/settings/page.tsx +0 -194
  340. package/src/hooks/useTasks.ts +0 -119
  341. package/src/services/doc.service.ts +0 -27
  342. package/src/services/sprint.service.ts +0 -24
  343. package/src/services/task.service.ts +0 -75
  344. package/src/views/Backlog.tsx +0 -691
  345. package/src/views/Board.tsx +0 -306
  346. /package/src/app/{docs → (dashboard)/docs}/page.tsx +0 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Completed Sprints Section Component
3
+ *
4
+ * Displays a collapsible section containing all completed sprints.
5
+ * Allows viewing historical sprint data and completed tasks.
6
+ */
7
+
8
+ "use client";
9
+
10
+ import { type Sprint, type Task } from "@locusai/shared";
11
+ import { motion } from "framer-motion";
12
+ import { Archive } from "lucide-react";
13
+ import { BacklogSection } from "./BacklogSection";
14
+ import { CompletedSprintItem } from "./CompletedSprintItem";
15
+
16
+ interface CompletedSprintsSectionProps {
17
+ /** Completed sprints */
18
+ sprints: Sprint[];
19
+ /** Whether section is expanded */
20
+ isExpanded: boolean;
21
+ /** Called when toggling section */
22
+ onToggle: () => void;
23
+ /** Function to get tasks for a sprint */
24
+ getSprintTasks: (sprintId: string) => Task[];
25
+ /** Set of expanded sprint IDs */
26
+ expandedSprints: Set<string>;
27
+ /** Called when toggling sprint item */
28
+ onToggleSprint: (section: string) => void;
29
+ /** Called when task is clicked */
30
+ onTaskClick: (taskId: string) => void;
31
+ }
32
+
33
+ export function CompletedSprintsSection({
34
+ sprints,
35
+ isExpanded,
36
+ onToggle,
37
+ getSprintTasks,
38
+ expandedSprints,
39
+ onToggleSprint,
40
+ onTaskClick,
41
+ }: CompletedSprintsSectionProps) {
42
+ if (sprints.length === 0) return null;
43
+
44
+ return (
45
+ <motion.div
46
+ layout="position"
47
+ initial={{ opacity: 0 }}
48
+ animate={{ opacity: 1 }}
49
+ transition={{ duration: 0.15, delay: 0.1 }}
50
+ className="pt-4 border-t border-border/50"
51
+ >
52
+ <BacklogSection
53
+ id="completed"
54
+ title="Completed Sprints"
55
+ icon={<Archive size={18} className="text-emerald-500/80" />}
56
+ count={sprints.length}
57
+ isExpanded={isExpanded}
58
+ onToggle={onToggle}
59
+ accentColor="green"
60
+ >
61
+ <div className="space-y-2 mt-2">
62
+ {sprints.map((sprint) => (
63
+ <CompletedSprintItem
64
+ key={sprint.id}
65
+ name={sprint.name}
66
+ taskCount={getSprintTasks(sprint.id).length}
67
+ tasks={getSprintTasks(sprint.id)}
68
+ isExpanded={expandedSprints.has(`completed-sprint-${sprint.id}`)}
69
+ onToggle={() => onToggleSprint(`completed-sprint-${sprint.id}`)}
70
+ onTaskClick={onTaskClick}
71
+ />
72
+ ))}
73
+ </div>
74
+ </BacklogSection>
75
+ </motion.div>
76
+ );
77
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Sprint Section Component
3
+ *
4
+ * Displays a sprint with its tasks in the backlog view.
5
+ * Supports start/complete actions and drag-drop task management.
6
+ *
7
+ * @example
8
+ * <SprintSection
9
+ * sprint={sprint}
10
+ * tasks={sprintTasks}
11
+ * isExpanded={true}
12
+ * onToggle={handleToggle}
13
+ * isActive={true}
14
+ * onStart={handleStart}
15
+ * onComplete={handleComplete}
16
+ * onTaskClick={handleSelectTask}
17
+ * onTaskDelete={handleDeleteTask}
18
+ * />
19
+ */
20
+
21
+ "use client";
22
+
23
+ import { type Sprint, type Task } from "@locusai/shared";
24
+ import { motion } from "framer-motion";
25
+ import { CheckCircle, Flag, Layers, Play } from "lucide-react";
26
+ import { DraggableTask, DroppableSection } from "@/components/dnd";
27
+ import { TaskCard } from "@/components/TaskCard";
28
+ import { Button } from "@/components/ui";
29
+ import { BacklogSection } from "./BacklogSection";
30
+
31
+ interface SprintSectionProps {
32
+ /** Sprint data */
33
+ sprint: Sprint;
34
+ /** Tasks in this sprint */
35
+ tasks: Task[];
36
+ /** Whether section is expanded */
37
+ isExpanded: boolean;
38
+ /** Called when toggling expand state */
39
+ onToggle: () => void;
40
+ /** Whether this is the active sprint */
41
+ isActive?: boolean;
42
+ /** Called when completing sprint */
43
+ onComplete?: (sprintId: string) => void;
44
+ /** Called when starting sprint */
45
+ onStart?: (sprintId: string) => void;
46
+ /** Called when task is selected */
47
+ onTaskClick: (taskId: string) => void;
48
+ /** Called when delete action is triggered */
49
+ onTaskDelete: (taskId: string) => void;
50
+ /** Whether an action is in progress */
51
+ isSubmitting?: boolean;
52
+ /** Whether sprint can be started */
53
+ canStart?: boolean;
54
+ }
55
+
56
+ export function SprintSection({
57
+ sprint,
58
+ tasks,
59
+ isExpanded,
60
+ onToggle,
61
+ isActive,
62
+ onComplete,
63
+ onStart,
64
+ onTaskClick,
65
+ onTaskDelete,
66
+ isSubmitting,
67
+ canStart,
68
+ }: SprintSectionProps) {
69
+ const accentColor = isActive ? "emerald" : "amber";
70
+ const icon = isActive ? (
71
+ <Flag size={18} className="text-emerald-500" />
72
+ ) : (
73
+ <Layers size={18} className="text-amber-500/80" />
74
+ );
75
+
76
+ return (
77
+ <motion.div
78
+ layout="position"
79
+ initial={{ opacity: 0 }}
80
+ animate={{ opacity: 1 }}
81
+ exit={{ opacity: 0 }}
82
+ transition={{ duration: 0.15 }}
83
+ >
84
+ <BacklogSection
85
+ id={`sprint-${sprint.id}`}
86
+ title={sprint.name}
87
+ icon={icon}
88
+ count={tasks.length}
89
+ isExpanded={isExpanded}
90
+ onToggle={onToggle}
91
+ accentColor={accentColor}
92
+ badge={isActive ? "Active" : "Planned"}
93
+ actions={
94
+ isActive ? (
95
+ <Button
96
+ size="sm"
97
+ variant="emerald-subtle"
98
+ onClick={() => onComplete?.(sprint.id)}
99
+ isLoading={isSubmitting}
100
+ >
101
+ <CheckCircle size={14} className="mr-1" />
102
+ Complete
103
+ </Button>
104
+ ) : (
105
+ canStart && (
106
+ <Button
107
+ size="sm"
108
+ variant="amber"
109
+ onClick={() => onStart?.(sprint.id)}
110
+ isLoading={isSubmitting}
111
+ >
112
+ <Play size={12} className="mr-1.5 fill-current" />
113
+ Start
114
+ </Button>
115
+ )
116
+ )
117
+ }
118
+ >
119
+ <DroppableSection id={`sprint-${sprint.id}`}>
120
+ {tasks.length === 0 ? (
121
+ <div
122
+ className={`py-8 border-2 border-dashed rounded-xl text-center text-[11px] font-medium ${
123
+ isActive
124
+ ? "border-emerald-500/10 text-emerald-500/50 bg-emerald-500/5"
125
+ : "border-amber-500/10 text-amber-500/50 bg-amber-500/5"
126
+ }`}
127
+ >
128
+ Drag tasks here to add to this sprint
129
+ </div>
130
+ ) : (
131
+ <div className="space-y-1.5 pt-1">
132
+ {tasks.map((task) => (
133
+ <motion.div
134
+ key={task.id}
135
+ layout="position"
136
+ initial={{ opacity: 0 }}
137
+ animate={{ opacity: 1 }}
138
+ >
139
+ <DraggableTask task={task}>
140
+ <TaskCard
141
+ task={task}
142
+ variant="list"
143
+ onClick={() => onTaskClick(task.id)}
144
+ onDelete={onTaskDelete}
145
+ />
146
+ </DraggableTask>
147
+ </motion.div>
148
+ ))}
149
+ </div>
150
+ )}
151
+ </DroppableSection>
152
+ </BacklogSection>
153
+ </motion.div>
154
+ );
155
+ }
@@ -0,0 +1,26 @@
1
+ export const SPRINT_STATUS_COLORS = {
2
+ active: {
3
+ accent: "primary",
4
+ border: "border-l-primary",
5
+ bg: "bg-primary/5",
6
+ badge: "bg-primary/10 text-primary",
7
+ },
8
+ planned: {
9
+ accent: "amber",
10
+ border: "border-l-amber-400",
11
+ bg: "bg-amber-50/50",
12
+ badge: "bg-amber-100 text-amber-700",
13
+ },
14
+ completed: {
15
+ accent: "green",
16
+ border: "border-l-green-400",
17
+ bg: "bg-green-50/50",
18
+ badge: "bg-green-100 text-green-700",
19
+ },
20
+ backlog: {
21
+ accent: "slate",
22
+ border: "border-l-slate-400",
23
+ bg: "bg-slate-50/50",
24
+ badge: "bg-slate-100 text-slate-700",
25
+ },
26
+ };
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Board Column Component
3
+ *
4
+ * Displays a single kanban column for a task status.
5
+ * Supports drag-and-drop task movement between columns.
6
+ *
7
+ * @example
8
+ * <BoardColumn
9
+ * statusKey="IN_PROGRESS"
10
+ * title="In Progress"
11
+ * tasks={tasksInProgress}
12
+ * onTaskClick={handleSelectTask}
13
+ * onTaskDelete={handleDeleteTask}
14
+ * />
15
+ */
16
+
17
+ "use client";
18
+
19
+ import { type Task } from "@locusai/shared";
20
+ import { DraggableTask, DroppableSection } from "@/components/dnd";
21
+ import { TaskCard } from "@/components/TaskCard";
22
+ import { cn } from "@/lib/utils";
23
+ import { BOARD_STATUSES } from "./constants";
24
+
25
+ interface BoardColumnProps {
26
+ /** Unique key identifying the status */
27
+ statusKey: string;
28
+ /** Display title for the column */
29
+ title: string;
30
+ /** Tasks to display in this column */
31
+ tasks: Task[];
32
+ /** Called when a task card is clicked */
33
+ onTaskClick: (taskId: string) => void;
34
+ /** Called when delete action is triggered */
35
+ onTaskDelete: (taskId: string) => void;
36
+ }
37
+
38
+ /**
39
+ * Board Column Component
40
+ *
41
+ * Features:
42
+ * - Displays tasks in a kanban column
43
+ * - Drag-drop enabled via DroppableSection
44
+ * - Shows task count
45
+ * - Empty state message
46
+ * - Color-coded by status
47
+ *
48
+ * @component
49
+ */
50
+ export function BoardColumn({
51
+ statusKey,
52
+ title,
53
+ tasks,
54
+ onTaskClick,
55
+ onTaskDelete,
56
+ }: BoardColumnProps) {
57
+ const statusConfig = BOARD_STATUSES.find((s) => s.key === statusKey);
58
+ const colorClass = statusConfig?.className || "bg-slate-500";
59
+
60
+ return (
61
+ <div className="flex flex-col w-80 shrink-0 h-full">
62
+ {/* Column Header */}
63
+ <div className="flex items-center gap-2 mb-3 px-1">
64
+ <div className={cn("w-2 h-2 rounded-full", colorClass)} />
65
+ <span className="text-sm font-semibold text-foreground tracking-tight">
66
+ {title}
67
+ </span>
68
+ <span className="text-xs text-muted-foreground bg-muted/50 px-2 py-0.5 rounded-full font-medium">
69
+ {tasks.length}
70
+ </span>
71
+ </div>
72
+
73
+ {/* Column Content */}
74
+ <DroppableSection id={statusKey}>
75
+ <div className="flex-1 rounded-xl bg-secondary/10 border border-border/40 p-2 min-h-[calc(100vh-220px)] backdrop-blur-sm transition-colors hover:bg-secondary/20">
76
+ {tasks.length === 0 ? (
77
+ <div className="h-full flex flex-col items-center justify-center text-xs text-muted-foreground/60 gap-2 opacity-0 hover:opacity-100 transition-opacity">
78
+ <span>Drop tasks here</span>
79
+ </div>
80
+ ) : (
81
+ <div className="space-y-3">
82
+ {tasks.map((task) => (
83
+ <DraggableTask key={task.id} task={task}>
84
+ <TaskCard
85
+ task={task}
86
+ onClick={() => onTaskClick(task.id)}
87
+ onDelete={onTaskDelete}
88
+ />
89
+ </DraggableTask>
90
+ ))}
91
+ </div>
92
+ )}
93
+ </div>
94
+ </DroppableSection>
95
+ </div>
96
+ );
97
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Board main content component
3
+ * Renders columns, empty state, or drag overlay
4
+ */
5
+
6
+ import {
7
+ closestCenter,
8
+ DndContext,
9
+ type DndContextProps,
10
+ DragOverlay,
11
+ } from "@dnd-kit/core";
12
+ import { type Task } from "@locusai/shared";
13
+ import { BoardColumn, BoardEmptyState } from "@/components/board";
14
+ import { BOARD_STATUSES } from "@/components/board/constants";
15
+ import { TaskCard } from "@/components/TaskCard";
16
+
17
+ interface BoardContentProps {
18
+ filteredTasks: Task[];
19
+ tasksByStatus: Record<string, Task[]>;
20
+ shouldShowEmptyState: boolean;
21
+ activeTask: Task | null;
22
+ sensors: DndContextProps["sensors"];
23
+ onDragStart: DndContextProps["onDragStart"];
24
+ onDragEnd: DndContextProps["onDragEnd"];
25
+ onTaskClick: (taskId: string) => void;
26
+ onTaskDelete: (taskId: string) => void;
27
+ onNewTask: () => void;
28
+ }
29
+
30
+ export function BoardContent({
31
+ filteredTasks,
32
+ tasksByStatus,
33
+ shouldShowEmptyState,
34
+ activeTask,
35
+ sensors,
36
+ onDragStart,
37
+ onDragEnd,
38
+ onTaskClick,
39
+ onTaskDelete,
40
+ onNewTask,
41
+ }: BoardContentProps) {
42
+ return (
43
+ <DndContext
44
+ sensors={sensors}
45
+ collisionDetection={closestCenter}
46
+ onDragStart={onDragStart}
47
+ onDragEnd={onDragEnd}
48
+ >
49
+ {shouldShowEmptyState ? (
50
+ <div className="flex-1">
51
+ <BoardEmptyState hasActiveSprint={false} onNewTask={onNewTask} />
52
+ </div>
53
+ ) : filteredTasks.length === 0 ? (
54
+ <div className="flex-1">
55
+ <BoardEmptyState hasActiveSprint={true} onNewTask={onNewTask} />
56
+ </div>
57
+ ) : (
58
+ <div className="flex-1 overflow-x-auto min-h-0">
59
+ <div className="flex gap-4 h-full min-w-max pb-4">
60
+ {BOARD_STATUSES.map((status) => (
61
+ <BoardColumn
62
+ key={status.key}
63
+ statusKey={status.key}
64
+ title={status.label}
65
+ tasks={tasksByStatus[status.key] || []}
66
+ onTaskClick={onTaskClick}
67
+ onTaskDelete={onTaskDelete}
68
+ />
69
+ ))}
70
+ </div>
71
+ </div>
72
+ )}
73
+
74
+ {/* Drag Overlay */}
75
+ <DragOverlay>
76
+ {activeTask && (
77
+ <div className="opacity-90 rotate-2 shadow-2xl cursor-grabbing w-72">
78
+ <TaskCard task={activeTask} />
79
+ </div>
80
+ )}
81
+ </DragOverlay>
82
+ </DndContext>
83
+ );
84
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Board Empty State Component
3
+ *
4
+ * Displays contextual empty state based on sprint status.
5
+ * Guides users to create sprints or add tasks.
6
+ *
7
+ * @example
8
+ * <BoardEmptyState hasActiveSprint={false} onNewTask={handleCreateTask} />
9
+ */
10
+
11
+ "use client";
12
+
13
+ import { Inbox, Layers, Plus } from "lucide-react";
14
+ import { useRouter } from "next/navigation";
15
+ import { Button, EmptyState } from "@/components/ui";
16
+
17
+ interface BoardEmptyStateProps {
18
+ /** Whether there's an active sprint */
19
+ hasActiveSprint: boolean;
20
+ /** Callback to create a new task */
21
+ onNewTask: () => void;
22
+ }
23
+
24
+ /**
25
+ * Board Empty State Component
26
+ *
27
+ * Features:
28
+ * - Shows "no active sprint" state with backlog navigation
29
+ * - Shows "no tasks" state with create task button
30
+ * - Navigation to backlog and sprint creation
31
+ *
32
+ * @component
33
+ */
34
+ export function BoardEmptyState({
35
+ hasActiveSprint,
36
+ onNewTask,
37
+ }: BoardEmptyStateProps) {
38
+ const router = useRouter();
39
+
40
+ if (!hasActiveSprint) {
41
+ return (
42
+ <EmptyState
43
+ icon={Layers}
44
+ title="No Active Sprint"
45
+ description="There is no active sprint currently. Go to the backlog to plan and start a new sprint."
46
+ action={
47
+ <div className="flex gap-3">
48
+ <Button
49
+ variant="outline"
50
+ className="gap-2"
51
+ onClick={() => router.push("/backlog")}
52
+ >
53
+ <Layers size={16} />
54
+ Go to Backlog
55
+ </Button>
56
+ <Button
57
+ className="gap-2"
58
+ onClick={() => router.push("/backlog?createSprint=true")}
59
+ >
60
+ <Plus size={16} />
61
+ Create Sprint
62
+ </Button>
63
+ </div>
64
+ }
65
+ />
66
+ );
67
+ }
68
+
69
+ return (
70
+ <EmptyState
71
+ icon={Inbox}
72
+ title="No tasks in Sprint"
73
+ description="The active sprint has no tasks yet. Add tasks from the backlog or create new ones."
74
+ action={
75
+ <Button onClick={onNewTask} className="shadow-lg shadow-primary/20">
76
+ <Plus size={18} className="mr-2" />
77
+ New Task
78
+ </Button>
79
+ }
80
+ />
81
+ );
82
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Board page header component
3
+ * Displays sprint info and new task button
4
+ */
5
+
6
+ import { type Sprint } from "@locusai/shared";
7
+ import { Plus } from "lucide-react";
8
+ import { Button } from "@/components/ui";
9
+
10
+ interface BoardHeaderProps {
11
+ activeSprint?: Sprint | null;
12
+ filteredTasksCount: number;
13
+ onNewTask: () => void;
14
+ }
15
+
16
+ export function BoardHeader({
17
+ activeSprint,
18
+ filteredTasksCount,
19
+ onNewTask,
20
+ }: BoardHeaderProps) {
21
+ return {
22
+ title: "Board",
23
+ description: (
24
+ <div className="flex items-center gap-2">
25
+ {activeSprint ? (
26
+ <>
27
+ <span className="text-primary font-bold">{activeSprint.name}</span>
28
+ <span className="w-1 h-1 rounded-full bg-muted-foreground/30" />
29
+ <span>{filteredTasksCount} tasks</span>
30
+ </>
31
+ ) : (
32
+ <span>No active sprint</span>
33
+ )}
34
+ </div>
35
+ ),
36
+ actions: (
37
+ <Button
38
+ onClick={onNewTask}
39
+ size="md"
40
+ className="shadow-lg shadow-primary/20"
41
+ >
42
+ <Plus size={18} className="mr-2" />
43
+ New Task
44
+ </Button>
45
+ ),
46
+ };
47
+ }
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
4
+ import { Editor } from "@/components/Editor";
5
+
6
+ interface SprintMindmapProps {
7
+ mindmap: string | null;
8
+ }
9
+
10
+ export function SprintMindmap({ mindmap }: SprintMindmapProps) {
11
+ if (!mindmap) {
12
+ return (
13
+ <div className="flex flex-col items-center justify-center h-[60vh] text-muted-foreground border-2 border-dashed rounded-xl bg-muted/10">
14
+ <p className="text-lg font-medium text-foreground">
15
+ No mindmap generated yet
16
+ </p>
17
+ <p className="text-sm opacity-70 max-w-xs text-center mt-2">
18
+ The agent will analyze your sprint and generate a strategic mindmap
19
+ once it starts working.
20
+ </p>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ return (
26
+ <motion.div
27
+ initial={{ opacity: 0, y: 20 }}
28
+ animate={{ opacity: 1, y: 0 }}
29
+ className="flex-1 relative min-h-[70vh] bg-[#09090b] rounded-xl border border-white/5 overflow-hidden"
30
+ >
31
+ {/* Canvas Grid Background */}
32
+ <div
33
+ className="absolute inset-0 z-0 pointer-events-none opacity-20"
34
+ style={{
35
+ backgroundImage:
36
+ "radial-gradient(circle, #3f3f46 1px, transparent 1px)",
37
+ backgroundSize: "24px 24px",
38
+ }}
39
+ />
40
+
41
+ <div className="relative z-10 p-8 md:p-12 h-full max-h-[80vh] overflow-hidden">
42
+ <div className="max-w-5xl mx-auto h-full">
43
+ <div className="bg-black/40 backdrop-blur-md border border-white/10 rounded-2xl shadow-2xl shadow-black/50 h-full flex flex-col overflow-hidden">
44
+ <div className="flex-1 overflow-hidden">
45
+ <Editor
46
+ value={mindmap}
47
+ onChange={() => {
48
+ /* Read-only */
49
+ }}
50
+ readOnly={true}
51
+ />
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ {/* Floating Badge */}
58
+ <div className="absolute top-6 right-6 z-20">
59
+ <div className="px-3 py-1 bg-indigo-500/10 border border-indigo-500/20 rounded-full text-[10px] font-bold tracking-widest text-indigo-400 uppercase backdrop-blur-sm">
60
+ Agent Strategy Core
61
+ </div>
62
+ </div>
63
+ </motion.div>
64
+ );
65
+ }
@@ -0,0 +1,40 @@
1
+ import { TaskStatus } from "@locusai/shared";
2
+
3
+ export const BOARD_STATUSES = [
4
+ {
5
+ key: TaskStatus.BACKLOG,
6
+ label: "Backlog",
7
+ colorVar: "var(--color-status-backlog)",
8
+ className: "bg-[var(--color-status-backlog)]",
9
+ },
10
+ {
11
+ key: TaskStatus.IN_PROGRESS,
12
+ label: "In Progress",
13
+ colorVar: "var(--color-status-todo)",
14
+ className: "bg-[var(--color-status-todo)]",
15
+ },
16
+ {
17
+ key: TaskStatus.REVIEW,
18
+ label: "Review",
19
+ colorVar: "var(--color-status-review)",
20
+ className: "bg-[var(--color-status-review)]",
21
+ },
22
+ {
23
+ key: TaskStatus.VERIFICATION,
24
+ label: "Verification",
25
+ colorVar: "var(--color-status-verification)",
26
+ className: "bg-[var(--color-status-verification)]",
27
+ },
28
+ {
29
+ key: TaskStatus.DONE,
30
+ label: "Done",
31
+ colorVar: "var(--color-status-done)",
32
+ className: "bg-[var(--color-status-done)]",
33
+ },
34
+ {
35
+ key: TaskStatus.BLOCKED,
36
+ label: "Blocked",
37
+ colorVar: "var(--color-status-blocked)",
38
+ className: "bg-[var(--color-status-blocked)]",
39
+ },
40
+ ];
@@ -0,0 +1,5 @@
1
+ export * from "./BoardColumn";
2
+ export * from "./BoardContent";
3
+ export * from "./BoardEmptyState";
4
+ export * from "./BoardHeader";
5
+ export * from "./SprintMindmap";