@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,51 @@
1
+ "use client";
2
+
3
+ import { useDraggable, useDroppable } from "@dnd-kit/core";
4
+ import { type Task } from "@locusai/shared";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ interface DroppableSectionProps {
8
+ id: string;
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ export function DroppableSection({ id, children }: DroppableSectionProps) {
13
+ const { isOver, setNodeRef } = useDroppable({ id });
14
+
15
+ return (
16
+ <div
17
+ ref={setNodeRef}
18
+ className={cn(
19
+ "rounded-lg transition-all duration-200",
20
+ isOver && "bg-primary/10 ring-2 ring-primary/30 ring-dashed"
21
+ )}
22
+ >
23
+ {children}
24
+ </div>
25
+ );
26
+ }
27
+
28
+ interface DraggableTaskProps {
29
+ task: Task;
30
+ children: React.ReactNode;
31
+ }
32
+
33
+ export function DraggableTask({ task, children }: DraggableTaskProps) {
34
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
35
+ id: task.id,
36
+ });
37
+
38
+ return (
39
+ <div
40
+ ref={setNodeRef}
41
+ {...listeners}
42
+ {...attributes}
43
+ className={cn(
44
+ "cursor-grab active:cursor-grabbing transition-all",
45
+ isDragging && "opacity-50 scale-[0.98]"
46
+ )}
47
+ >
48
+ {children}
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Docs Editor Area Component
3
+ *
4
+ * Displays the main editor for documentation files.
5
+ * Supports edit and preview modes with rich text editing.
6
+ *
7
+ * @example
8
+ * <DocsEditorArea
9
+ * selectedDoc={doc}
10
+ * content={content}
11
+ * onContentChange={handleChange}
12
+ * contentMode="edit"
13
+ * onNewDoc={handleCreate}
14
+ * />
15
+ */
16
+
17
+ "use client";
18
+
19
+ import { type Doc } from "@locusai/shared";
20
+ import { BookOpen, Plus } from "lucide-react";
21
+ import { Editor } from "@/components/Editor";
22
+ import { SecondaryText } from "@/components/typography";
23
+ import { Button, EmptyState } from "@/components/ui";
24
+
25
+ interface DocsEditorAreaProps {
26
+ /** Selected documentation file */
27
+ selectedDoc: Doc | null;
28
+ /** Current content being edited */
29
+ content: string;
30
+ /** Called when content changes */
31
+ onContentChange: (content: string) => void;
32
+ /** Edit or preview mode */
33
+ contentMode: "edit" | "preview";
34
+ /** Called to create new doc */
35
+ onNewDoc: () => void;
36
+ }
37
+
38
+ export function DocsEditorArea({
39
+ selectedDoc,
40
+ content,
41
+ onContentChange,
42
+ contentMode,
43
+ onNewDoc,
44
+ }: DocsEditorAreaProps) {
45
+ if (!selectedDoc) {
46
+ return (
47
+ <div className="h-full flex items-center justify-center p-12 bg-secondary/5 border border-dashed border-border/40 rounded-3xl group transition-all hover:bg-secondary/10">
48
+ <EmptyState
49
+ icon={BookOpen}
50
+ title="Documentation Nexus"
51
+ description="Access the collective engineering intelligence. Forge new product requirements, architectural designs, or team processes."
52
+ action={
53
+ <Button
54
+ variant="secondary"
55
+ size="sm"
56
+ className="h-11 px-8 font-black uppercase tracking-widest text-[10px] rounded-xl shadow-lg border-border/40"
57
+ onClick={onNewDoc}
58
+ >
59
+ <Plus size={16} className="mr-2" />
60
+ Initialize Node
61
+ </Button>
62
+ }
63
+ className="max-w-xl scale-110 group-hover:scale-[1.12] transition-transform duration-500"
64
+ />
65
+ </div>
66
+ );
67
+ }
68
+
69
+ return (
70
+ <div className="flex flex-col h-full gap-5">
71
+ <div className="flex-1 bg-card/20 backdrop-blur-sm border border-border/40 rounded-2xl overflow-hidden shadow-xl shadow-black/5 relative group">
72
+ <Editor
73
+ value={content}
74
+ onChange={onContentChange}
75
+ readOnly={contentMode === "preview"}
76
+ />
77
+ {contentMode === "preview" && (
78
+ <div className="absolute top-4 right-4 pointer-events-none group-hover:opacity-100 opacity-0 transition-opacity">
79
+ <SecondaryText size="xs" className="text-muted-foreground/20">
80
+ Vision Mode Only
81
+ </SecondaryText>
82
+ </div>
83
+ )}
84
+ </div>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Docs Header Actions Component
3
+ *
4
+ * Displays header actions for documentation editor.
5
+ * Includes mode toggle, new doc, and save buttons.
6
+ *
7
+ * @example
8
+ * <DocsHeaderActions
9
+ * selectedDoc={doc}
10
+ * contentMode="edit"
11
+ * setContentMode={setMode}
12
+ * onNewDoc={handleNew}
13
+ * onSave={handleSave}
14
+ * hasUnsavedChanges={true}
15
+ * />
16
+ */
17
+
18
+ "use client";
19
+
20
+ import { type Doc, type DocGroup } from "@locusai/shared";
21
+ import { FolderOpen, Plus, Save } from "lucide-react";
22
+ import { Button } from "@/components/ui";
23
+ import { cn } from "@/lib/utils";
24
+
25
+ interface DocsHeaderActionsProps {
26
+ /** Selected doc or null */
27
+ selectedDoc: Doc | null;
28
+ /** Current editor mode */
29
+ contentMode: "edit" | "preview";
30
+ /** Called when changing mode */
31
+ setContentMode: (mode: "edit" | "preview") => void;
32
+ /** Called to create new doc */
33
+ onNewDoc: () => void;
34
+ /** Called to save doc */
35
+ onSave: () => void;
36
+ /** Whether there are unsaved changes */
37
+ hasUnsavedChanges: boolean;
38
+ /** Available doc groups */
39
+ groups: DocGroup[];
40
+ /** Called when changing doc group */
41
+ onGroupChange: (docId: string, groupId: string | null) => void;
42
+ }
43
+
44
+ export function DocsHeaderActions({
45
+ selectedDoc,
46
+ contentMode,
47
+ setContentMode,
48
+ onNewDoc,
49
+ onSave,
50
+ hasUnsavedChanges,
51
+ groups,
52
+ onGroupChange,
53
+ }: DocsHeaderActionsProps) {
54
+ return (
55
+ <div className="flex items-center gap-3">
56
+ {selectedDoc && (
57
+ <>
58
+ <div className="flex bg-secondary/30 p-1 rounded-xl border border-border/20 shadow-inner mr-2">
59
+ <button
60
+ className={cn(
61
+ "px-4 py-1.5 text-[10px] font-black uppercase tracking-widest rounded-lg transition-all",
62
+ contentMode === "edit"
63
+ ? "bg-background text-primary shadow-sm scale-105"
64
+ : "text-muted-foreground hover:text-foreground"
65
+ )}
66
+ onClick={() => setContentMode("edit")}
67
+ >
68
+ Forge
69
+ </button>
70
+ <button
71
+ className={cn(
72
+ "px-4 py-1.5 text-[10px] font-black uppercase tracking-widest rounded-lg transition-all",
73
+ contentMode === "preview"
74
+ ? "bg-background text-primary shadow-sm scale-105"
75
+ : "text-muted-foreground hover:text-foreground"
76
+ )}
77
+ onClick={() => setContentMode("preview")}
78
+ >
79
+ Vision
80
+ </button>
81
+ </div>
82
+ <div className="flex items-center gap-2 bg-secondary/20 px-3 py-1.5 rounded-xl border border-border/20">
83
+ <FolderOpen size={14} className="text-muted-foreground" />
84
+ <select
85
+ value={selectedDoc.groupId || ""}
86
+ onChange={(e) =>
87
+ onGroupChange(selectedDoc.id, e.target.value || null)
88
+ }
89
+ className="bg-transparent text-xs font-medium text-foreground focus:outline-none cursor-pointer appearance-none pr-2"
90
+ >
91
+ <option value="">Unsorted</option>
92
+ {groups.map((g) => (
93
+ <option key={g.id} value={g.id}>
94
+ {g.name}
95
+ </option>
96
+ ))}
97
+ </select>
98
+ </div>
99
+ </>
100
+ )}
101
+ <Button
102
+ onClick={onNewDoc}
103
+ variant="outline"
104
+ className="h-9 border-border/50"
105
+ >
106
+ <Plus size={16} className="mr-2" />
107
+ New Doc
108
+ </Button>
109
+ {selectedDoc && (
110
+ <Button
111
+ onClick={onSave}
112
+ className="h-9 px-6 shadow-lg shadow-primary/20"
113
+ disabled={!hasUnsavedChanges}
114
+ >
115
+ <Save size={16} className="mr-2" />
116
+ Commit
117
+ </Button>
118
+ )}
119
+ </div>
120
+ );
121
+ }
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Docs Sidebar Component
3
+ *
4
+ * Displays documentation file/folder hierarchy with search and creation.
5
+ * Supports doc groups, file search, templates, and deletion.
6
+ *
7
+ * @example
8
+ * <DocsSidebar
9
+ * groups={groups}
10
+ * docsByGroup={docsMap}
11
+ * selectedId={activeId}
12
+ * onSelect={handleSelect}
13
+ * // ... other props
14
+ * />
15
+ */
16
+
17
+ "use client";
18
+
19
+ import { type Doc, type DocGroup } from "@locusai/shared";
20
+ import {
21
+ ChevronDown,
22
+ ChevronRight,
23
+ File,
24
+ FileText,
25
+ FolderPlus,
26
+ Plus,
27
+ Search,
28
+ Trash2,
29
+ X,
30
+ } from "lucide-react";
31
+ import { useEffect, useState } from "react";
32
+ import { SecondaryText, SectionLabel } from "@/components/typography";
33
+ import { Button, Input } from "@/components/ui";
34
+ import { DOC_TEMPLATES } from "@/hooks";
35
+ import { cn } from "@/lib/utils";
36
+
37
+ interface DocsSidebarProps {
38
+ /** Doc groups/folders */
39
+ groups: DocGroup[];
40
+ /** Docs organized by group ID */
41
+ docsByGroup: Record<string, Doc[]>;
42
+ /** Currently selected doc ID */
43
+ selectedId: string | null;
44
+ /** Called when selecting a doc */
45
+ onSelect: (id: string | null) => void;
46
+ /** Search query filter */
47
+ searchQuery: string;
48
+ /** Called when search changes */
49
+ onSearchChange: (query: string) => void;
50
+ /** Whether in creation mode */
51
+ isCreating: boolean;
52
+ /** Set creation mode */
53
+ setIsCreating: (value: boolean) => void;
54
+ /** New file name being created */
55
+ newFileName: string;
56
+ /** Set new file name */
57
+ setNewFileName: (name: string) => void;
58
+ /** Selected template ID */
59
+ selectedTemplate: string;
60
+ /** Called when selecting template */
61
+ onTemplateSelect: (id: string) => void;
62
+ /** Called to create new file */
63
+ onCreateFile: () => void;
64
+ /** Called to delete doc/group */
65
+ onDelete: (id: string) => void;
66
+ /** Called to create new group */
67
+ onCreateGroup: (name: string) => void;
68
+ /** Currently selected group */
69
+ selectedGroupId: string | null;
70
+ /** Called when selecting group */
71
+ onGroupSelect: (id: string | null) => void;
72
+ }
73
+
74
+ export function DocsSidebar({
75
+ groups,
76
+ docsByGroup,
77
+ selectedId,
78
+ onSelect,
79
+ searchQuery,
80
+ onSearchChange,
81
+ isCreating,
82
+ setIsCreating,
83
+ newFileName,
84
+ setNewFileName,
85
+ selectedTemplate,
86
+ onTemplateSelect,
87
+ onCreateFile,
88
+ onDelete,
89
+ onCreateGroup,
90
+ selectedGroupId,
91
+ onGroupSelect,
92
+ }: DocsSidebarProps) {
93
+ const [expandedGroups, setExpandedGroups] = useState<Set<string>>(
94
+ new Set(["ungrouped"])
95
+ );
96
+ const [isCreatingGroup, setIsCreatingGroup] = useState(false);
97
+ const [newGroupName, setNewGroupName] = useState("");
98
+
99
+ // Auto-expand all groups on mount
100
+ useEffect(() => {
101
+ setExpandedGroups(new Set(["ungrouped", ...groups.map((g) => g.id)]));
102
+ }, [groups]);
103
+
104
+ const toggleGroup = (groupId: string) => {
105
+ const next = new Set(expandedGroups);
106
+ if (next.has(groupId)) next.delete(groupId);
107
+ else next.add(groupId);
108
+ setExpandedGroups(next);
109
+ };
110
+
111
+ const handleCreateGroup = (e: React.FormEvent) => {
112
+ e.preventDefault();
113
+ if (newGroupName.trim()) {
114
+ onCreateGroup(newGroupName.trim());
115
+ setNewGroupName("");
116
+ setIsCreatingGroup(false);
117
+ }
118
+ };
119
+
120
+ const renderDocItem = (doc: Doc) => (
121
+ <div
122
+ key={doc.id}
123
+ className={cn(
124
+ "flex items-center gap-2 w-full px-3 py-1.5 text-xs font-medium rounded-lg transition-all group/item cursor-pointer",
125
+ selectedId === doc.id
126
+ ? "bg-primary text-primary-foreground shadow-sm"
127
+ : "text-muted-foreground/80 hover:bg-secondary/50 hover:text-foreground"
128
+ )}
129
+ onClick={() => onSelect(doc.id)}
130
+ >
131
+ <FileText size={14} className="shrink-0 opacity-70" />
132
+ <span className="truncate capitalize">
133
+ {doc.title.replace(/[-_]/g, " ")}
134
+ </span>
135
+ <button
136
+ type="button"
137
+ className="ml-auto p-1 opacity-0 group-hover/item:opacity-100 hover:text-destructive transition-all"
138
+ onClick={(e) => {
139
+ e.stopPropagation();
140
+ onDelete(doc.id);
141
+ }}
142
+ >
143
+ <Trash2 size={12} />
144
+ </button>
145
+ </div>
146
+ );
147
+
148
+ return (
149
+ <aside className="w-80 flex flex-col bg-card/30 backdrop-blur-xl border border-border/40 rounded-2xl overflow-hidden shadow-xl shadow-black/20">
150
+ <div className="p-4 space-y-3 border-b border-border/40 bg-card/10">
151
+ <div className="relative">
152
+ <Search
153
+ size={14}
154
+ className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground/60"
155
+ />
156
+ <Input
157
+ placeholder="Search library..."
158
+ value={searchQuery}
159
+ onChange={(e) => onSearchChange(e.target.value)}
160
+ className="h-9 pl-9 text-xs bg-secondary/20 border-border/30 focus:bg-secondary/40 rounded-xl"
161
+ />
162
+ </div>
163
+ <div className="flex gap-2">
164
+ <Button
165
+ variant="ghost"
166
+ size="sm"
167
+ className="flex-1 h-8 text-[10px] uppercase font-black tracking-widest gap-2 bg-primary/5 hover:bg-primary/10 text-primary border border-primary/20"
168
+ onClick={() => setIsCreating(true)}
169
+ >
170
+ <Plus size={14} /> New Doc
171
+ </Button>
172
+ <Button
173
+ variant="ghost"
174
+ size="sm"
175
+ className="flex-1 h-8 text-[10px] uppercase font-black tracking-widest gap-2 hover:bg-secondary/50"
176
+ onClick={() => setIsCreatingGroup(true)}
177
+ >
178
+ <FolderPlus size={14} /> New Group
179
+ </Button>
180
+ </div>
181
+ </div>
182
+
183
+ {/* Item Creation Form */}
184
+ {(isCreating || isCreatingGroup) && (
185
+ <div className="p-5 bg-primary/5 border-b border-border/40 animate-in fade-in slide-in-from-top-4 duration-300">
186
+ <div className="flex items-center justify-between mb-4">
187
+ <SectionLabel className="text-primary">
188
+ {isCreatingGroup ? "Initialize Group" : "Initialize Node"}
189
+ </SectionLabel>
190
+ <button
191
+ className="text-muted-foreground hover:text-foreground transition-colors p-1"
192
+ onClick={() => {
193
+ setIsCreating(false);
194
+ setIsCreatingGroup(false);
195
+ }}
196
+ >
197
+ <X size={16} />
198
+ </button>
199
+ </div>
200
+
201
+ {isCreatingGroup ? (
202
+ <form onSubmit={handleCreateGroup}>
203
+ <Input
204
+ autoFocus
205
+ placeholder="group-name..."
206
+ value={newGroupName}
207
+ onChange={(e) => setNewGroupName(e.target.value)}
208
+ className="h-9 mb-4 bg-background/50 border-border/40 rounded-xl font-mono text-xs"
209
+ />
210
+ <Button
211
+ type="submit"
212
+ className="w-full h-9 font-black uppercase tracking-widest text-[10px] shadow-lg shadow-primary/20 rounded-xl"
213
+ disabled={!newGroupName.trim()}
214
+ >
215
+ Create Group
216
+ </Button>
217
+ </form>
218
+ ) : (
219
+ <div className="space-y-4">
220
+ <Input
221
+ autoFocus
222
+ placeholder="document-handle..."
223
+ value={newFileName}
224
+ onChange={(e) => setNewFileName(e.target.value)}
225
+ className="h-9 bg-background/50 border-border/40 rounded-xl font-mono text-xs"
226
+ />
227
+
228
+ <div className="space-y-2">
229
+ <SecondaryText size="xs" className="ml-1">
230
+ Assign to Group
231
+ </SecondaryText>
232
+ <select
233
+ value={selectedGroupId || ""}
234
+ onChange={(e) => onGroupSelect(e.target.value || null)}
235
+ className="w-full h-9 bg-background/50 border border-border/40 rounded-xl text-xs px-3 focus:outline-none focus:ring-1 focus:ring-primary/50 appearance-none cursor-pointer"
236
+ >
237
+ <option value="">No Group (Root)</option>
238
+ {groups.map((g) => (
239
+ <option key={g.id} value={g.id}>
240
+ {g.name}
241
+ </option>
242
+ ))}
243
+ </select>
244
+ </div>
245
+
246
+ <div className="space-y-2">
247
+ <SecondaryText size="xs" className="ml-1">
248
+ Blueprint Template
249
+ </SecondaryText>
250
+ <div className="grid grid-cols-2 gap-2">
251
+ {DOC_TEMPLATES.map((template) => (
252
+ <button
253
+ key={template.id}
254
+ className={cn(
255
+ "px-3 py-2 text-[10px] font-bold rounded-xl border transition-all text-left uppercase tracking-wider",
256
+ selectedTemplate === template.id
257
+ ? "border-primary bg-primary/10 text-primary shadow-inner"
258
+ : "border-border/20 text-muted-foreground/60 hover:border-border/40 hover:bg-secondary/30"
259
+ )}
260
+ onClick={() => onTemplateSelect(template.id)}
261
+ >
262
+ {template.label}
263
+ </button>
264
+ ))}
265
+ </div>
266
+ </div>
267
+
268
+ <Button
269
+ className="w-full h-9 font-black uppercase tracking-widest text-[10px] shadow-lg shadow-primary/20 rounded-xl"
270
+ onClick={onCreateFile}
271
+ disabled={!newFileName.trim()}
272
+ >
273
+ Deploy Document
274
+ </Button>
275
+ </div>
276
+ )}
277
+ </div>
278
+ )}
279
+
280
+ {/* Grouped Document List */}
281
+ <div className="flex-1 overflow-y-auto p-3 space-y-4 scrollbar-thin">
282
+ {/* Render Groups */}
283
+ {groups.map((group) => (
284
+ <div key={group.id} className="space-y-1">
285
+ <button
286
+ onClick={() => toggleGroup(group.id)}
287
+ className="flex items-center gap-2 w-full px-2 py-1 text-muted-foreground/70 hover:text-foreground transition-colors group"
288
+ >
289
+ {expandedGroups.has(group.id) ? (
290
+ <ChevronDown size={14} />
291
+ ) : (
292
+ <ChevronRight size={14} />
293
+ )}
294
+ <SectionLabel className="m-0 flex-1">{group.name}</SectionLabel>
295
+ <span className="ml-auto opacity-0 group-hover:opacity-100 bg-secondary/50 px-1.5 py-0.5 rounded text-[8px]">
296
+ {docsByGroup[group.id]?.length || 0}
297
+ </span>
298
+ </button>
299
+ {expandedGroups.has(group.id) && (
300
+ <div className="pl-2 space-y-0.5 border-l border-border/20 ml-3.5 mt-1">
301
+ {docsByGroup[group.id]?.length > 0 ? (
302
+ docsByGroup[group.id].map(renderDocItem)
303
+ ) : (
304
+ <div className="py-2 px-3 text-[10px] text-muted-foreground/40 italic">
305
+ Empty group
306
+ </div>
307
+ )}
308
+ </div>
309
+ )}
310
+ </div>
311
+ ))}
312
+
313
+ {/* Render Ungrouped Documents */}
314
+ <div className="space-y-1">
315
+ <button
316
+ onClick={() => toggleGroup("ungrouped")}
317
+ className="flex items-center gap-2 w-full px-2 py-1 text-muted-foreground/70 hover:text-foreground transition-colors group"
318
+ >
319
+ {expandedGroups.has("ungrouped") ? (
320
+ <ChevronDown size={14} />
321
+ ) : (
322
+ <ChevronRight size={14} />
323
+ )}
324
+ <SectionLabel className="m-0 flex-1">Unsorted</SectionLabel>
325
+ <span className="ml-auto opacity-0 group-hover:opacity-100 bg-secondary/50 px-1.5 py-0.5 rounded text-[8px]">
326
+ {docsByGroup.ungrouped?.length || 0}
327
+ </span>
328
+ </button>
329
+ {expandedGroups.has("ungrouped") && (
330
+ <div className="pl-2 space-y-0.5 border-l border-border/20 ml-3.5 mt-1">
331
+ {docsByGroup.ungrouped?.length > 0 ? (
332
+ docsByGroup.ungrouped.map(renderDocItem)
333
+ ) : (
334
+ <div className="py-2 px-3 text-[10px] text-muted-foreground/40 italic">
335
+ No files
336
+ </div>
337
+ )}
338
+ </div>
339
+ )}
340
+ </div>
341
+
342
+ {groups.length === 0 && docsByGroup.ungrouped?.length === 0 && (
343
+ <div className="flex flex-col items-center justify-center py-20 opacity-40">
344
+ <File size={32} className="mb-4 text-muted-foreground" />
345
+ <SecondaryText size="xs">Library Empty</SecondaryText>
346
+ </div>
347
+ )}
348
+ </div>
349
+ </aside>
350
+ );
351
+ }
@@ -1,7 +1,14 @@
1
+ export * from "./AuthLayoutUI";
2
+ export * from "./BoardFilter";
1
3
  export * from "./BoardFilter";
2
4
  export * from "./Header";
5
+ export * from "./PageLayout";
3
6
  export * from "./PropertyItem";
4
7
  export * from "./Sidebar";
8
+ export * from "./SprintCreateModal";
5
9
  export * from "./TaskCard";
6
10
  export * from "./TaskCreateModal";
7
11
  export * from "./TaskPanel";
12
+ export * from "./typography";
13
+ export * from "./typography-scales";
14
+ export * from "./WorkspaceProtected";
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ interface StepProgressProps {
4
+ currentStep: number;
5
+ totalSteps: number;
6
+ }
7
+
8
+ export function StepProgress({ currentStep, totalSteps }: StepProgressProps) {
9
+ return (
10
+ <div className="flex w-full gap-2 mb-8">
11
+ {Array.from({ length: totalSteps }).map((_, i) => (
12
+ <div
13
+ key={i}
14
+ className={`h-1 flex-1 rounded-full transition-colors duration-300 ${
15
+ i + 1 <= currentStep ? "bg-primary" : "bg-muted/50"
16
+ }`}
17
+ />
18
+ ))}
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1 @@
1
+ export * from "./StepProgress";