@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,371 @@
1
+ /**
2
+ * Registration page step components
3
+ * Separate UI components for each step of the registration flow
4
+ */
5
+
6
+ import { Button, Input, OtpInput } from "@/components/ui";
7
+ import { cn } from "@/lib/utils";
8
+
9
+ interface RegisterEmailStepProps {
10
+ email: string;
11
+ loading: boolean;
12
+ onEmailChange: (email: string) => void;
13
+ onSubmit: (e: React.FormEvent) => void;
14
+ }
15
+
16
+ /**
17
+ * Email entry step for registration
18
+ */
19
+ export function RegisterEmailStep({
20
+ email,
21
+ loading,
22
+ onEmailChange,
23
+ onSubmit,
24
+ }: RegisterEmailStepProps) {
25
+ return (
26
+ <form onSubmit={onSubmit} className="space-y-4">
27
+ <div className="space-y-2">
28
+ <label className="text-sm font-medium">Work Email</label>
29
+ <Input
30
+ type="email"
31
+ placeholder="name@company.com"
32
+ value={email}
33
+ onChange={(e) => onEmailChange(e.target.value)}
34
+ required
35
+ disabled={loading}
36
+ autoFocus
37
+ />
38
+ </div>
39
+ <Button type="submit" className="w-full" disabled={loading}>
40
+ {loading ? "Sending code..." : "Continue with Email"}
41
+ </Button>
42
+ </form>
43
+ );
44
+ }
45
+
46
+ interface RegisterOtpStepProps {
47
+ email: string;
48
+ otp: string;
49
+ loading: boolean;
50
+ onOtpChange: (otp: string) => void;
51
+ onSubmit: (e: React.FormEvent) => void;
52
+ onBack: () => void;
53
+ }
54
+
55
+ /**
56
+ * OTP verification step for registration
57
+ */
58
+ export function RegisterOtpStep({
59
+ email,
60
+ otp,
61
+ loading,
62
+ onOtpChange,
63
+ onSubmit,
64
+ onBack,
65
+ }: RegisterOtpStepProps) {
66
+ return (
67
+ <form onSubmit={onSubmit} className="space-y-6">
68
+ <div className="space-y-4">
69
+ <OtpInput value={otp} onChange={onOtpChange} disabled={loading} />
70
+ <p className="text-xs text-center text-muted-foreground">
71
+ Sent to <span className="font-semibold text-foreground">{email}</span>
72
+ <button
73
+ type="button"
74
+ onClick={onBack}
75
+ className="ml-2 text-primary hover:underline transition-colors"
76
+ >
77
+ Change
78
+ </button>
79
+ </p>
80
+ </div>
81
+ <Button type="submit" className="w-full h-11" disabled={otp.length < 6}>
82
+ Verify Code
83
+ </Button>
84
+ </form>
85
+ );
86
+ }
87
+
88
+ interface RegisterProfileStepProps {
89
+ name: string;
90
+ userRole: string;
91
+ onNameChange: (name: string) => void;
92
+ onRoleChange: (role: string) => void;
93
+ onSubmit: (e: React.FormEvent) => void;
94
+ }
95
+
96
+ const ROLE_OPTIONS = [
97
+ { id: "developer", label: "Developer" },
98
+ { id: "designer", label: "Designer" },
99
+ { id: "product_manager", label: "Product" },
100
+ { id: "other", label: "Other" },
101
+ ] as const;
102
+
103
+ /**
104
+ * Profile step for registration
105
+ */
106
+ export function RegisterProfileStep({
107
+ name,
108
+ userRole,
109
+ onNameChange,
110
+ onRoleChange,
111
+ onSubmit,
112
+ }: RegisterProfileStepProps) {
113
+ return (
114
+ <form onSubmit={onSubmit} className="space-y-6">
115
+ <div className="space-y-2">
116
+ <label className="text-sm font-semibold text-foreground/90">
117
+ What's your name?
118
+ </label>
119
+ <Input
120
+ type="text"
121
+ placeholder="Full Name"
122
+ value={name}
123
+ onChange={(e) => onNameChange(e.target.value)}
124
+ required
125
+ autoFocus
126
+ className="h-11 bg-secondary/20 border-border/40 focus:bg-background"
127
+ />
128
+ </div>
129
+ <div className="space-y-3">
130
+ <label className="text-sm font-semibold text-foreground/90">
131
+ What's your role?
132
+ </label>
133
+ <div className="grid grid-cols-2 gap-3">
134
+ {ROLE_OPTIONS.map((role) => (
135
+ <button
136
+ key={role.id}
137
+ type="button"
138
+ onClick={() => onRoleChange(role.id)}
139
+ className={cn(
140
+ "flex items-center justify-center p-3 rounded-xl border text-sm font-medium transition-all duration-200",
141
+ userRole === role.id
142
+ ? "bg-primary/10 border-primary text-primary shadow-[0_0_15px_-5px_rgba(var(--primary),0.3)]"
143
+ : "bg-secondary/10 border-border/40 text-muted-foreground hover:border-border hover:bg-secondary/20"
144
+ )}
145
+ >
146
+ {role.label}
147
+ </button>
148
+ ))}
149
+ </div>
150
+ </div>
151
+ <Button
152
+ type="submit"
153
+ className="w-full h-11 text-base font-bold shadow-lg shadow-primary/20"
154
+ disabled={!userRole || !name}
155
+ >
156
+ Continue
157
+ </Button>
158
+ </form>
159
+ );
160
+ }
161
+
162
+ interface RegisterOrganizationStepProps {
163
+ companyName: string;
164
+ teamSize: string;
165
+ onCompanyNameChange: (name: string) => void;
166
+ onTeamSizeChange: (size: string) => void;
167
+ onSubmit: (e: React.FormEvent) => void;
168
+ }
169
+
170
+ const TEAM_SIZE_OPTIONS = [
171
+ { id: "solo", label: "Solo" },
172
+ { id: "2-10", label: "2-10" },
173
+ { id: "11-50", label: "11-50" },
174
+ { id: "51-200", label: "51-200" },
175
+ { id: "200+", label: "200+" },
176
+ ] as const;
177
+
178
+ /**
179
+ * Organization step for registration
180
+ */
181
+ export function RegisterOrganizationStep({
182
+ companyName,
183
+ teamSize,
184
+ onCompanyNameChange,
185
+ onTeamSizeChange,
186
+ onSubmit,
187
+ }: RegisterOrganizationStepProps) {
188
+ return (
189
+ <form onSubmit={onSubmit} className="space-y-6">
190
+ <div className="space-y-2">
191
+ <label className="text-sm font-semibold text-foreground/90">
192
+ Company Name
193
+ </label>
194
+ <Input
195
+ type="text"
196
+ placeholder="Acme Inc."
197
+ value={companyName}
198
+ onChange={(e) => onCompanyNameChange(e.target.value)}
199
+ required
200
+ autoFocus
201
+ className="h-11 bg-secondary/20 border-border/40 focus:bg-background"
202
+ />
203
+ </div>
204
+ <div className="space-y-3">
205
+ <label className="text-sm font-semibold text-foreground/90">
206
+ Team Size
207
+ </label>
208
+ <div className="grid grid-cols-2 gap-3">
209
+ {TEAM_SIZE_OPTIONS.map((size) => (
210
+ <button
211
+ key={size.id}
212
+ type="button"
213
+ onClick={() => onTeamSizeChange(size.id)}
214
+ className={cn(
215
+ "flex items-center justify-center p-3 rounded-xl border text-sm font-medium transition-all duration-200",
216
+ teamSize === size.id
217
+ ? "bg-primary/10 border-primary text-primary shadow-[0_0_15px_-5px_rgba(var(--primary),0.3)]"
218
+ : "bg-secondary/10 border-border/40 text-muted-foreground hover:border-border hover:bg-secondary/20"
219
+ )}
220
+ >
221
+ {size.label}
222
+ </button>
223
+ ))}
224
+ </div>
225
+ </div>
226
+ <Button
227
+ type="submit"
228
+ className="w-full h-11 text-base font-bold shadow-lg shadow-primary/20"
229
+ disabled={!teamSize || !companyName}
230
+ >
231
+ Continue
232
+ </Button>
233
+ </form>
234
+ );
235
+ }
236
+
237
+ interface RegisterWorkspaceStepProps {
238
+ workspaceName: string;
239
+ onWorkspaceNameChange: (name: string) => void;
240
+ onSubmit: (e: React.FormEvent) => void;
241
+ }
242
+
243
+ /**
244
+ * Workspace step for registration
245
+ */
246
+ export function RegisterWorkspaceStep({
247
+ workspaceName,
248
+ onWorkspaceNameChange,
249
+ onSubmit,
250
+ }: RegisterWorkspaceStepProps) {
251
+ return (
252
+ <form onSubmit={onSubmit} className="space-y-6">
253
+ <div className="space-y-2">
254
+ <label className="text-sm font-semibold text-foreground/90">
255
+ Name your first workspace
256
+ </label>
257
+ <p className="text-xs text-muted-foreground/70">
258
+ Workspaces are where your team manages specific projects or
259
+ departments.
260
+ </p>
261
+ <Input
262
+ type="text"
263
+ placeholder="e.g. Engineering, Marketing"
264
+ value={workspaceName}
265
+ onChange={(e) => onWorkspaceNameChange(e.target.value)}
266
+ required
267
+ autoFocus
268
+ className="h-11 bg-secondary/20 border-border/40 focus:bg-background"
269
+ />
270
+ </div>
271
+ <Button
272
+ type="submit"
273
+ className="w-full h-11 text-base font-bold shadow-lg shadow-primary/20"
274
+ disabled={!workspaceName}
275
+ >
276
+ Continue
277
+ </Button>
278
+ </form>
279
+ );
280
+ }
281
+
282
+ interface RegisterInviteStepProps {
283
+ invitedEmails: string[];
284
+ currentInviteEmail: string;
285
+ loading: boolean;
286
+ onCurrentEmailChange: (email: string) => void;
287
+ onAddInvite: () => void;
288
+ onRemoveInvite: (email: string) => void;
289
+ onSkip: () => void;
290
+ onSubmit: () => void;
291
+ onKeyDown?: (e: React.KeyboardEvent) => void;
292
+ }
293
+
294
+ /**
295
+ * Invite step for registration
296
+ */
297
+ export function RegisterInviteStep({
298
+ invitedEmails,
299
+ currentInviteEmail,
300
+ loading,
301
+ onCurrentEmailChange,
302
+ onAddInvite,
303
+ onRemoveInvite,
304
+ onSkip,
305
+ onSubmit,
306
+ onKeyDown,
307
+ }: RegisterInviteStepProps) {
308
+ return (
309
+ <div className="space-y-6">
310
+ <div className="space-y-3">
311
+ <label className="text-sm font-semibold text-foreground/90">
312
+ Invite your team (Optional)
313
+ </label>
314
+ <div className="flex gap-2">
315
+ <Input
316
+ type="email"
317
+ placeholder="colleague@company.com"
318
+ value={currentInviteEmail}
319
+ onChange={(e) => onCurrentEmailChange(e.target.value)}
320
+ onKeyDown={onKeyDown}
321
+ className="h-11 bg-secondary/20 border-border/40 focus:bg-background"
322
+ />
323
+ <Button
324
+ type="button"
325
+ onClick={onAddInvite}
326
+ variant="secondary"
327
+ className="h-11 px-6"
328
+ >
329
+ Add
330
+ </Button>
331
+ </div>
332
+ {invitedEmails.length > 0 && (
333
+ <div className="flex flex-wrap gap-2 mt-4">
334
+ {invitedEmails.map((email) => (
335
+ <div
336
+ key={email}
337
+ className="bg-primary/5 text-primary border border-primary/20 px-3 py-1.5 rounded-full text-xs font-medium flex items-center gap-2 animate-in zoom-in-95 duration-200"
338
+ >
339
+ {email}
340
+ <button
341
+ type="button"
342
+ onClick={() => onRemoveInvite(email)}
343
+ className="hover:text-primary/70 transition-colors"
344
+ >
345
+ ×
346
+ </button>
347
+ </div>
348
+ ))}
349
+ </div>
350
+ )}
351
+ </div>
352
+ <div className="pt-4 space-y-3">
353
+ <Button
354
+ onClick={onSubmit}
355
+ className="w-full h-11 text-base font-bold shadow-lg shadow-primary/20"
356
+ disabled={loading}
357
+ >
358
+ {loading ? "Creating your space..." : "Finish Setup"}
359
+ </Button>
360
+ <Button
361
+ variant="ghost"
362
+ onClick={onSkip}
363
+ className="w-full h-11 text-muted-foreground hover:text-foreground"
364
+ disabled={loading}
365
+ >
366
+ Skip for now
367
+ </Button>
368
+ </div>
369
+ </div>
370
+ );
371
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./InviteSteps";
2
+ export * from "./LoginSteps";
3
+ export * from "./RegisterSteps";
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Backlog List Component
3
+ *
4
+ * Displays unscheduled tasks in the backlog.
5
+ * Supports expand/collapse and drag-drop to sprints.
6
+ *
7
+ * @example
8
+ * <BacklogList
9
+ * tasks={unscheduledTasks}
10
+ * isExpanded={true}
11
+ * onToggle={handleToggle}
12
+ * onTaskClick={handleSelectTask}
13
+ * onTaskDelete={handleDeleteTask}
14
+ * />
15
+ */
16
+
17
+ "use client";
18
+
19
+ import { type Task } from "@locusai/shared";
20
+ import { motion } from "framer-motion";
21
+ import { Inbox } from "lucide-react";
22
+ import { DraggableTask, DroppableSection } from "@/components/dnd";
23
+ import { TaskCard } from "@/components/TaskCard";
24
+ import { BacklogSection } from "./BacklogSection";
25
+
26
+ interface BacklogListProps {
27
+ /** Unscheduled tasks to display */
28
+ tasks: Task[];
29
+ /** Whether the backlog section is expanded */
30
+ isExpanded: boolean;
31
+ /** Called when expanding/collapsing */
32
+ onToggle: () => void;
33
+ /** Called when a task is selected */
34
+ onTaskClick: (taskId: string) => void;
35
+ /** Called when delete action is triggered */
36
+ onTaskDelete: (taskId: string) => void;
37
+ }
38
+
39
+ export function BacklogList({
40
+ tasks,
41
+ isExpanded,
42
+ onToggle,
43
+ onTaskClick,
44
+ onTaskDelete,
45
+ }: BacklogListProps) {
46
+ return (
47
+ <motion.div
48
+ layout="position"
49
+ initial={{ opacity: 0 }}
50
+ animate={{ opacity: 1 }}
51
+ transition={{ duration: 0.15, delay: 0.05 }}
52
+ >
53
+ <BacklogSection
54
+ id="backlog"
55
+ title="Backlog"
56
+ icon={<Inbox size={18} className="text-slate-400" />}
57
+ count={tasks.length}
58
+ isExpanded={isExpanded}
59
+ onToggle={onToggle}
60
+ accentColor="slate"
61
+ >
62
+ <DroppableSection id="backlog">
63
+ {tasks.length === 0 ? (
64
+ <div className="py-6 border border-dashed border-border/20 rounded-xl text-center text-muted-foreground/40 text-[11px] font-medium bg-secondary/5">
65
+ No tasks in backlog
66
+ </div>
67
+ ) : (
68
+ <div className="space-y-1.5 pt-1">
69
+ {tasks.map((task) => (
70
+ <motion.div
71
+ key={task.id}
72
+ layout="position"
73
+ initial={{ opacity: 0 }}
74
+ animate={{ opacity: 1 }}
75
+ >
76
+ <DraggableTask task={task}>
77
+ <TaskCard
78
+ task={task}
79
+ variant="list"
80
+ onClick={() => onTaskClick(task.id)}
81
+ onDelete={onTaskDelete}
82
+ />
83
+ </DraggableTask>
84
+ </motion.div>
85
+ ))}
86
+ </div>
87
+ )}
88
+ </DroppableSection>
89
+ </BacklogSection>
90
+ </motion.div>
91
+ );
92
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Backlog Section Component
3
+ *
4
+ * Reusable section component for backlog and sprint organization.
5
+ * Supports collapsible header with count badge and actions.
6
+ *
7
+ * @example
8
+ * <BacklogSection
9
+ * id="sprint-1"
10
+ * title="Sprint 1"
11
+ * icon={<Flag size={18} />}
12
+ * count={12}
13
+ * isExpanded={true}
14
+ * onToggle={handleToggle}
15
+ * accentColor="primary"
16
+ * >
17
+ * { Section content }
18
+ * </BacklogSection>
19
+ */
20
+
21
+ "use client";
22
+
23
+ import { ChevronDown, ChevronRight } from "lucide-react";
24
+ import { cn } from "@/lib/utils";
25
+
26
+ interface BacklogSectionProps {
27
+ /** Unique section identifier */
28
+ id: string;
29
+ /** Display title */
30
+ title: string;
31
+ /** Icon to display */
32
+ icon: React.ReactNode;
33
+ /** Item count to display */
34
+ count: number;
35
+ /** Whether section is expanded */
36
+ isExpanded: boolean;
37
+ /** Called when toggling expand state */
38
+ onToggle: () => void;
39
+ /** Color theme for section */
40
+ accentColor: "slate" | "primary" | "amber" | "green" | "emerald";
41
+ /** Optional badge text */
42
+ badge?: string;
43
+ /** Optional action elements */
44
+ actions?: React.ReactNode;
45
+ /** Section content */
46
+ children: React.ReactNode;
47
+ }
48
+
49
+ export function BacklogSection({
50
+ id: _id,
51
+ title,
52
+ icon,
53
+ count,
54
+ isExpanded,
55
+ onToggle,
56
+ accentColor,
57
+ badge,
58
+ actions,
59
+ children,
60
+ }: BacklogSectionProps) {
61
+ const colors = {
62
+ slate: "border-l-slate-400/60 bg-slate-50/50 dark:bg-slate-900/10",
63
+ primary: "border-l-primary bg-primary/[0.08] dark:bg-primary/[0.06]",
64
+ emerald:
65
+ "border-l-emerald-500 bg-emerald-500/[0.08] dark:bg-emerald-500/[0.06]",
66
+ amber: "border-l-amber-500 bg-amber-500/[0.08] dark:bg-amber-500/[0.06]",
67
+ green: "border-l-green-500 bg-green-500/[0.03] dark:bg-green-500/[0.02]",
68
+ };
69
+
70
+ const badgeColors = {
71
+ slate: "bg-muted text-muted-foreground",
72
+ primary: "bg-primary/20 text-primary dark:text-primary-foreground/90",
73
+ emerald: "bg-emerald-500/20 text-emerald-700 dark:text-emerald-400",
74
+ amber: "bg-amber-500/15 text-amber-700 dark:text-amber-400",
75
+ green: "bg-green-500/15 text-green-700 dark:text-green-400",
76
+ };
77
+
78
+ return (
79
+ <div
80
+ className={cn(
81
+ "rounded-xl border border-border/40 overflow-hidden transition-all duration-200",
82
+ colors[accentColor],
83
+ "border-l-4",
84
+ isExpanded
85
+ ? "shadow-sm ring-1 ring-border/10"
86
+ : "hover:bg-secondary/5 dark:hover:bg-white/2"
87
+ )}
88
+ >
89
+ {/* Header */}
90
+ <div
91
+ onClick={onToggle}
92
+ onKeyDown={(e) => e.key === "Enter" && onToggle()}
93
+ className="w-full flex items-center justify-between p-3.5 cursor-pointer select-none group"
94
+ >
95
+ <div className="flex items-center gap-3">
96
+ <div className="p-1 rounded-md hover:bg-black/5 dark:hover:bg-white/10 transition-colors">
97
+ {isExpanded ? (
98
+ <ChevronDown size={18} className="text-muted-foreground/70" />
99
+ ) : (
100
+ <ChevronRight size={18} className="text-muted-foreground/70" />
101
+ )}
102
+ </div>
103
+ <div className="opacity-70 group-hover:opacity-100 transition-opacity">
104
+ {icon}
105
+ </div>
106
+ <span className="font-semibold text-sm tracking-tight text-foreground/90">
107
+ {title}
108
+ </span>
109
+ <span className="text-[11px] text-muted-foreground/70 bg-background/50 border border-border/10 px-2 py-0.5 rounded-full font-mono">
110
+ {count}
111
+ </span>
112
+ {badge && (
113
+ <span
114
+ className={cn(
115
+ "text-[10px] uppercase tracking-wider px-2 py-0.5 rounded-full font-bold shadow-sm",
116
+ badgeColors[accentColor]
117
+ )}
118
+ >
119
+ {badge}
120
+ </span>
121
+ )}
122
+ </div>
123
+ {actions && (
124
+ <div
125
+ className="flex items-center"
126
+ onClick={(e) => e.stopPropagation()}
127
+ >
128
+ {actions}
129
+ </div>
130
+ )}
131
+ </div>
132
+
133
+ {/* Content */}
134
+ {isExpanded && <div className="px-3.5 pb-3.5 space-y-1">{children}</div>}
135
+ </div>
136
+ );
137
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Completed Sprint Item Component
3
+ *
4
+ * Displays a single completed sprint as a collapsible item.
5
+ * Shows completed tasks and allows viewing historical sprint details.
6
+ */
7
+
8
+ "use client";
9
+
10
+ import { type Task } from "@locusai/shared";
11
+ import { AnimatePresence, motion } from "framer-motion";
12
+ import { CheckCircle, ChevronDown, ChevronRight } from "lucide-react";
13
+ import { TaskCard } from "@/components/TaskCard";
14
+
15
+ interface CompletedSprintItemProps {
16
+ /** Sprint name */
17
+ name: string;
18
+ /** Total tasks in sprint */
19
+ taskCount: number;
20
+ /** Completed tasks */
21
+ tasks: Task[];
22
+ /** Whether expanded */
23
+ isExpanded: boolean;
24
+ /** Called when toggling expand */
25
+ onToggle: () => void;
26
+ /** Called when task is clicked */
27
+ onTaskClick: (taskId: string) => void;
28
+ }
29
+
30
+ export function CompletedSprintItem({
31
+ name,
32
+ taskCount,
33
+ tasks,
34
+ isExpanded,
35
+ onToggle,
36
+ onTaskClick,
37
+ }: CompletedSprintItemProps) {
38
+ return (
39
+ <div className="rounded-lg border border-border/40 overflow-hidden bg-card/20 group transition-colors hover:border-border/60">
40
+ <div
41
+ onClick={onToggle}
42
+ className="p-3 flex items-center justify-between cursor-pointer hover:bg-card/40 transition-colors"
43
+ >
44
+ <div className="flex items-center gap-3">
45
+ <div className="text-muted-foreground/60 group-hover:text-muted-foreground transition-colors">
46
+ {isExpanded ? (
47
+ <ChevronDown size={14} />
48
+ ) : (
49
+ <ChevronRight size={14} />
50
+ )}
51
+ </div>
52
+ <div className="flex items-center gap-2">
53
+ <CheckCircle size={15} className="text-green-500/60" />
54
+ <span className="font-medium text-sm text-foreground/80">
55
+ {name}
56
+ </span>
57
+ </div>
58
+ </div>
59
+ <span className="text-[10px] font-medium text-muted-foreground/60 bg-secondary/30 px-3 py-0.5 rounded-full border border-border/20">
60
+ {taskCount} tasks
61
+ </span>
62
+ </div>
63
+
64
+ <AnimatePresence>
65
+ {isExpanded && (
66
+ <motion.div
67
+ initial={{ height: 0, opacity: 0 }}
68
+ animate={{ height: "auto", opacity: 1 }}
69
+ exit={{ height: 0, opacity: 0 }}
70
+ transition={{ duration: 0.2 }}
71
+ >
72
+ <div className="px-3 pb-3 space-y-1">
73
+ {tasks.length === 0 ? (
74
+ <div className="py-4 text-center text-[10px] text-muted-foreground/40 italic">
75
+ No tasks in this sprint
76
+ </div>
77
+ ) : (
78
+ <div className="space-y-1">
79
+ {tasks.map((task) => (
80
+ <TaskCard
81
+ key={task.id}
82
+ task={task}
83
+ variant="list"
84
+ onClick={() => onTaskClick(task.id)}
85
+ />
86
+ ))}
87
+ </div>
88
+ )}
89
+ </div>
90
+ </motion.div>
91
+ )}
92
+ </AnimatePresence>
93
+ </div>
94
+ );
95
+ }