@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,147 @@
1
+ "use client";
2
+
3
+ import { Check, Clipboard, Terminal } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { toast } from "sonner";
6
+ import { Button } from "@/components/ui";
7
+ import { SettingSection } from "./SettingSection";
8
+
9
+ interface ProjectSetupGuideProps {
10
+ hasApiKeys: boolean;
11
+ workspaceId?: string;
12
+ }
13
+
14
+ export function ProjectSetupGuide({
15
+ hasApiKeys,
16
+ workspaceId,
17
+ }: ProjectSetupGuideProps) {
18
+ const [copiedCommand, setCopiedCommand] = useState<string | null>(null);
19
+
20
+ const copyToClipboard = (text: string, id: string) => {
21
+ navigator.clipboard.writeText(text);
22
+ setCopiedCommand(id);
23
+ toast.success("Command copied to clipboard");
24
+ setTimeout(() => setCopiedCommand(null), 2000);
25
+ };
26
+
27
+ const steps = [
28
+ {
29
+ id: "api-key",
30
+ title: "1. Create an API Key",
31
+ description:
32
+ "You'll need an API key to authenticate the CLI with your organization.",
33
+ status: hasApiKeys ? "completed" : "pending",
34
+ content: hasApiKeys ? (
35
+ <p className="text-sm text-green-500 flex items-center gap-2">
36
+ <Check size={16} /> API key created
37
+ </p>
38
+ ) : (
39
+ <p className="text-sm text-muted-foreground">
40
+ Go to the API Keys section above and create your first key.
41
+ </p>
42
+ ),
43
+ },
44
+ {
45
+ id: "install",
46
+ title: "2. Initialize your project",
47
+ description:
48
+ "Run this command in your project root to set up Locus configuration and CLAUDE.md.",
49
+ status: "action",
50
+ command: "npx @locusai/cli init",
51
+ },
52
+ {
53
+ id: "run",
54
+ title: "3. Start your agents",
55
+ description:
56
+ "Run this command to start an agent that will poll for tasks from your dashboard.",
57
+ status: "action",
58
+ command: `npx @locusai/cli run --api-key YOUR_API_KEY --workspace ${workspaceId || "YOUR_WORKSPACE_ID"}`,
59
+ },
60
+ ];
61
+
62
+ return (
63
+ <SettingSection title="Project Setup Guide">
64
+ <div className="p-6 space-y-8">
65
+ {steps.map((step) => (
66
+ <div key={step.id} className="relative pl-8">
67
+ {/* Connector Line */}
68
+ {step.id !== "run" && (
69
+ <div className="absolute left-[11px] top-8 bottom-[-32px] w-[2px] bg-border/50" />
70
+ )}
71
+
72
+ {/* Step Indicator */}
73
+ <div
74
+ className={`absolute left-0 top-0 w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold border-2 ${
75
+ step.status === "completed"
76
+ ? "bg-green-500/10 border-green-500 text-green-500"
77
+ : "bg-secondary border-border text-muted-foreground"
78
+ }`}
79
+ >
80
+ {step.status === "completed" ? (
81
+ <Check size={12} />
82
+ ) : step.id === "api-key" ? (
83
+ "1"
84
+ ) : step.id === "install" ? (
85
+ "2"
86
+ ) : (
87
+ "3"
88
+ )}
89
+ </div>
90
+
91
+ <div className="space-y-3">
92
+ <div>
93
+ <h4 className="font-medium text-sm text-foreground">
94
+ {step.title}
95
+ </h4>
96
+ <p className="text-xs text-muted-foreground mt-1">
97
+ {step.description}
98
+ </p>
99
+ </div>
100
+
101
+ {step.content}
102
+
103
+ {step.command && (
104
+ <div className="group relative">
105
+ <div className="bg-secondary/50 border border-border/50 rounded-xl p-3 font-mono text-xs flex items-center justify-between overflow-x-auto">
106
+ <code className="text-foreground whitespace-nowrap">
107
+ <span className="text-muted-foreground mr-2">$</span>
108
+ {step.command}
109
+ </code>
110
+ <Button
111
+ variant="ghost"
112
+ size="icon"
113
+ className="h-8 w-8 ml-2 opacity-0 group-hover:opacity-100 transition-opacity"
114
+ onClick={() => {
115
+ if (step.command) {
116
+ copyToClipboard(step.command, step.id);
117
+ }
118
+ }}
119
+ >
120
+ {copiedCommand === step.id ? (
121
+ <Check size={14} className="text-green-500" />
122
+ ) : (
123
+ <Clipboard size={14} />
124
+ )}
125
+ </Button>
126
+ </div>
127
+ </div>
128
+ )}
129
+ </div>
130
+ </div>
131
+ ))}
132
+
133
+ <div className="pt-4 flex items-center gap-3 p-4 bg-primary/5 border border-primary/10 rounded-2xl">
134
+ <Terminal size={18} className="text-primary" />
135
+ <p className="text-xs text-muted-foreground">
136
+ <span className="font-medium text-foreground">Pro tip:</span> You
137
+ can also set{" "}
138
+ <code className="bg-secondary px-1 rounded text-foreground">
139
+ LOCUS_API_KEY
140
+ </code>{" "}
141
+ environment variable to avoid passing it via flags.
142
+ </p>
143
+ </div>
144
+ </div>
145
+ </SettingSection>
146
+ );
147
+ }
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+
5
+ export interface SettingItemProps {
6
+ icon: React.ReactNode;
7
+ title: string;
8
+ description: string;
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ export function SettingItem({
13
+ icon,
14
+ title,
15
+ description,
16
+ children,
17
+ }: SettingItemProps) {
18
+ return (
19
+ <div className="flex items-center justify-between p-4 rounded-xl hover:bg-secondary/30 transition-colors">
20
+ <div className="flex gap-4">
21
+ <div className="w-10 h-10 rounded-xl bg-secondary flex items-center justify-center text-muted-foreground shrink-0">
22
+ {icon}
23
+ </div>
24
+ <div>
25
+ <h4 className="font-medium text-foreground">{title}</h4>
26
+ <p className="text-sm text-muted-foreground mt-0.5">{description}</p>
27
+ </div>
28
+ </div>
29
+ <div className="shrink-0 flex items-center h-full">{children}</div>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Setting Section Component
3
+ *
4
+ * Reusable section wrapper for settings pages.
5
+ * Provides consistent styling and title display.
6
+ *
7
+ * @example
8
+ * <SettingSection title="Profile">
9
+ * <SettingItem label="Name" value={name} />
10
+ * </SettingSection>
11
+ */
12
+
13
+ "use client";
14
+
15
+ import React from "react";
16
+ import { cn } from "@/lib/utils";
17
+
18
+ interface SettingSectionProps {
19
+ /** Section title */
20
+ title: string;
21
+ /** Section content */
22
+ children: React.ReactNode;
23
+ /** Optional className override */
24
+ className?: string;
25
+ /** Optional title className override */
26
+ titleClassName?: string;
27
+ }
28
+
29
+ export function SettingSection({
30
+ title,
31
+ children,
32
+ className,
33
+ titleClassName,
34
+ }: SettingSectionProps) {
35
+ return (
36
+ <div className={cn("mb-8", className)}>
37
+ <h3
38
+ className={cn(
39
+ "text-xs font-bold uppercase tracking-widest text-muted-foreground mb-4",
40
+ titleClassName
41
+ )}
42
+ >
43
+ {title}
44
+ </h3>
45
+ <div className="bg-card border border-border/50 rounded-2xl overflow-hidden divide-y divide-border/50">
46
+ {children}
47
+ </div>
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Pending invitations list component
3
+ * Displays pending invitations with actions
4
+ */
5
+
6
+ import { type Invitation } from "@locusai/shared";
7
+ import { Copy, Mail, X } from "lucide-react";
8
+ import { Badge, Button } from "@/components/ui";
9
+
10
+ interface TeamInvitationsListProps {
11
+ invitations: Invitation[];
12
+ canManage: boolean;
13
+ onCopyLink: (token: string) => void;
14
+ onRevokeInvitation: (invitationId: string) => void;
15
+ }
16
+
17
+ export function TeamInvitationsList({
18
+ invitations,
19
+ canManage,
20
+ onCopyLink,
21
+ onRevokeInvitation,
22
+ }: TeamInvitationsListProps) {
23
+ if (invitations.length === 0) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <section className="space-y-4">
29
+ <div className="flex items-center justify-between px-1">
30
+ <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wider">
31
+ Pending Invitations ({invitations.length})
32
+ </h3>
33
+ </div>
34
+
35
+ <div className="bg-card/50 border border-secondary/20 rounded-2xl overflow-hidden">
36
+ <div className="divide-y divide-secondary/20">
37
+ {invitations.map((invitation) => (
38
+ <div
39
+ key={invitation.id}
40
+ className="flex items-center justify-between p-4 hover:bg-secondary/10 transition-colors"
41
+ >
42
+ <div className="flex items-center gap-4">
43
+ <div className="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center text-primary">
44
+ <Mail size={20} />
45
+ </div>
46
+ <div>
47
+ <div className="font-medium">{invitation.email}</div>
48
+ <div className="text-sm text-muted-foreground">
49
+ Invited as{" "}
50
+ <span className="capitalize">
51
+ {invitation.role.toLowerCase()}
52
+ </span>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <div className="flex items-center gap-2">
58
+ <Badge variant="warning" size="sm">
59
+ Pending
60
+ </Badge>
61
+ {canManage && (
62
+ <>
63
+ <Button
64
+ variant="ghost"
65
+ size="icon"
66
+ onClick={() => onCopyLink(invitation.token)}
67
+ className="text-muted-foreground"
68
+ title="Copy Invitation Link"
69
+ >
70
+ <Copy size={18} />
71
+ </Button>
72
+ <Button
73
+ variant="ghost"
74
+ size="icon"
75
+ onClick={() => onRevokeInvitation(invitation.id)}
76
+ className="text-muted-foreground hover:text-red-500 hover:bg-red-500/10"
77
+ title="Revoke Invitation"
78
+ >
79
+ <X size={18} />
80
+ </Button>
81
+ </>
82
+ )}
83
+ </div>
84
+ </div>
85
+ ))}
86
+ </div>
87
+ </div>
88
+ </section>
89
+ );
90
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Team members list component
3
+ * Displays organization members with actions
4
+ */
5
+
6
+ import { MembershipRole, type MembershipWithUser } from "@locusai/shared";
7
+ import { Shield, Trash2 } from "lucide-react";
8
+ import { Avatar, Badge, Button } from "@/components/ui";
9
+
10
+ interface TeamMembersListProps {
11
+ members: MembershipWithUser[];
12
+ currentUserId?: string;
13
+ canManage: boolean;
14
+ isLoading: boolean;
15
+ onRemoveMember: (userId: string) => void;
16
+ }
17
+
18
+ export function TeamMembersList({
19
+ members,
20
+ currentUserId,
21
+ canManage,
22
+ isLoading,
23
+ onRemoveMember,
24
+ }: TeamMembersListProps) {
25
+ if (isLoading) {
26
+ return (
27
+ <div className="p-8 text-center text-muted-foreground">Loading...</div>
28
+ );
29
+ }
30
+
31
+ if (members.length === 0) {
32
+ return (
33
+ <div className="p-8 text-center text-muted-foreground">
34
+ No members found.
35
+ </div>
36
+ );
37
+ }
38
+
39
+ return (
40
+ <div className="divide-y divide-secondary/20">
41
+ {members.map((membership) => (
42
+ <div
43
+ key={membership.id}
44
+ className="flex items-center justify-between p-4 hover:bg-secondary/10 transition-colors"
45
+ >
46
+ <div className="flex items-center gap-4">
47
+ <Avatar
48
+ src={membership.user.avatarUrl}
49
+ name={membership.user.name}
50
+ size="md"
51
+ />
52
+ <div>
53
+ <div className="flex items-center gap-2">
54
+ <span className="font-medium">{membership.user.name}</span>
55
+ {membership.userId === currentUserId && (
56
+ <Badge variant="info" size="sm">
57
+ You
58
+ </Badge>
59
+ )}
60
+ </div>
61
+ <span className="text-sm text-muted-foreground">
62
+ {membership.user.email}
63
+ </span>
64
+ </div>
65
+ </div>
66
+
67
+ <div className="flex items-center gap-6">
68
+ <div className="flex items-center gap-1.5 px-3 py-1 rounded-full bg-secondary/30 border border-secondary/20">
69
+ {membership.role === MembershipRole.OWNER ||
70
+ membership.role === MembershipRole.ADMIN ? (
71
+ <Shield size={14} className="text-primary" />
72
+ ) : null}
73
+ <span className="text-xs font-medium capitalize">
74
+ {membership.role.toLowerCase()}
75
+ </span>
76
+ </div>
77
+
78
+ {canManage &&
79
+ membership.userId !== currentUserId &&
80
+ membership.role !== MembershipRole.OWNER && (
81
+ <Button
82
+ variant="ghost"
83
+ size="icon"
84
+ onClick={() => onRemoveMember(membership.userId)}
85
+ className="text-muted-foreground hover:text-red-500 hover:bg-red-500/10"
86
+ >
87
+ <Trash2 size={18} />
88
+ </Button>
89
+ )}
90
+ </div>
91
+ </div>
92
+ ))}
93
+ </div>
94
+ );
95
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./ApiKeyConfirmationModal";
2
+ export * from "./ApiKeyCreatedModal";
3
+ export * from "./ApiKeysList";
4
+ export * from "./ApiKeysSettings";
5
+ export * from "./InviteMemberModal";
6
+ export * from "./ProjectSetupGuide";
7
+ export * from "./SettingItem";
8
+ export * from "./SettingSection";
@@ -0,0 +1,127 @@
1
+ "use client";
2
+
3
+ import { EventType, type Task, type Event as TaskEvent } from "@locusai/shared";
4
+ import { formatDistanceToNow } from "date-fns";
5
+ import {
6
+ CheckCircle,
7
+ Edit,
8
+ MessageSquare,
9
+ PlusSquare,
10
+ Tag,
11
+ } from "lucide-react";
12
+ import { MetadataText, SectionLabel } from "@/components/typography";
13
+ import { Button, EmptyState, Input } from "@/components/ui";
14
+
15
+ interface TaskActivityProps {
16
+ task: Task;
17
+ isLoading?: boolean;
18
+ newComment: string;
19
+ setNewComment: (val: string) => void;
20
+ handleAddComment: () => void;
21
+ }
22
+
23
+ export function TaskActivity({
24
+ task,
25
+ isLoading = false,
26
+ newComment,
27
+ setNewComment,
28
+ handleAddComment,
29
+ }: TaskActivityProps) {
30
+ return (
31
+ <div>
32
+ <SectionLabel as="h4" className="mb-6 pb-2 border-b border-border/40">
33
+ Neural Stream
34
+ </SectionLabel>
35
+
36
+ <div className="flex gap-3 mb-6 bg-background/30 p-2 rounded-2xl border border-border/40 shadow-inner focus-within:border-primary/30 transition-all">
37
+ <Input
38
+ value={newComment}
39
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
40
+ !isLoading && setNewComment(e.target.value)
41
+ }
42
+ disabled={isLoading}
43
+ placeholder="Transmit logs..."
44
+ className="h-10 text-xs font-bold bg-transparent border-none focus:ring-0 placeholder:font-black placeholder:uppercase placeholder:text-[9px] placeholder:tracking-[0.2em]"
45
+ onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
46
+ if (e.key === "Enter" && !isLoading) handleAddComment();
47
+ }}
48
+ />
49
+ <Button
50
+ onClick={handleAddComment}
51
+ disabled={!newComment.trim() || isLoading}
52
+ variant="ghost"
53
+ className="h-10 w-10 p-0 rounded-xl hover:bg-primary/10 hover:text-primary transition-all group shrink-0"
54
+ >
55
+ <MessageSquare
56
+ size={16}
57
+ className="group-hover:rotate-12 transition-transform"
58
+ />
59
+ </Button>
60
+ </div>
61
+
62
+ <div className="space-y-10 max-h-[600px] overflow-y-auto pr-4 scrollbar-thin">
63
+ {task.activityLog.length > 0 ? (
64
+ task.activityLog.map((event: Task["activityLog"][number]) => (
65
+ <div key={event.id} className="relative flex gap-6 group">
66
+ <div className="absolute left-[19px] top-10 bottom-[-28px] w-px bg-border/40 group-last:hidden" />
67
+ <div className="h-10 w-10 rounded-2xl bg-card border border-border/60 flex items-center justify-center shrink-0 z-10 shadow-sm group-hover:border-primary/40 transition-all group-hover:scale-110">
68
+ {event.type === EventType.COMMENT_ADDED && (
69
+ <MessageSquare size={14} className="text-blue-500" />
70
+ )}
71
+ {event.type === EventType.STATUS_CHANGED && (
72
+ <Tag size={14} className="text-amber-500" />
73
+ )}
74
+ {event.type === EventType.TASK_CREATED && (
75
+ <PlusSquare size={14} className="text-emerald-400" />
76
+ )}
77
+ {event.type === EventType.TASK_UPDATED && (
78
+ <Edit size={14} className="text-primary" />
79
+ )}
80
+ {event.type === EventType.CI_RAN && (
81
+ <CheckCircle size={14} className="text-accent" />
82
+ )}
83
+ </div>
84
+ <div className="pt-2 min-w-0">
85
+ <p className="text-xs font-bold text-foreground/80 leading-snug mb-2">
86
+ {formatActivityEvent(event as TaskEvent)}
87
+ </p>
88
+ <MetadataText size="sm">
89
+ {formatDistanceToNow(new Date(event.createdAt), {
90
+ addSuffix: true,
91
+ })}
92
+ </MetadataText>
93
+ </div>
94
+ </div>
95
+ ))
96
+ ) : (
97
+ <EmptyState
98
+ variant="minimal"
99
+ title="Silent Stream"
100
+ className="py-12 opacity-30"
101
+ />
102
+ )}
103
+ </div>
104
+ </div>
105
+ );
106
+ }
107
+
108
+ function formatActivityEvent(event: TaskEvent): string {
109
+ const { type, payload } = event;
110
+ const p = payload as TaskEvent["payload"];
111
+ switch (type) {
112
+ case EventType.STATUS_CHANGED:
113
+ return `Status moved ${p.oldStatus} ➟ ${p.newStatus}`;
114
+ case EventType.COMMENT_ADDED:
115
+ return `${p.author}: "${p.text}"`;
116
+ case EventType.TASK_CREATED:
117
+ return "Task initialized";
118
+ case EventType.TASK_UPDATED:
119
+ return "Parameters calibrated";
120
+ case EventType.TASK_DELETED:
121
+ return "Task deleted";
122
+ case EventType.CI_RAN:
123
+ return `Valuation complete: ${p.summary}`;
124
+ default:
125
+ return (type as string).replace(/_/g, " ").toLowerCase();
126
+ }
127
+ }