@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,83 @@
1
+ /**
2
+ * Application Constants
3
+ *
4
+ * Centralized configuration and constants.
5
+ */
6
+
7
+ /**
8
+ * Loading timeouts and delays
9
+ */
10
+ export const TIMING = {
11
+ DEBOUNCE_MS: 300,
12
+ THROTTLE_MS: 500,
13
+ ANIMATION_DURATION_MS: 300,
14
+ TOAST_DURATION_MS: 3000,
15
+ LONG_TOAST_DURATION_MS: 5000,
16
+ SKELETON_LOAD_TIME_MS: 2000,
17
+ } as const;
18
+
19
+ /**
20
+ * Z-index layers for proper stacking
21
+ */
22
+ export const Z_INDEX = {
23
+ BASE: 0,
24
+ DROPDOWN: 100,
25
+ STICKY: 500,
26
+ FIXED: 900,
27
+ MODAL_OVERLAY: 940,
28
+ MODAL: 950,
29
+ TOOLTIP: 1000,
30
+ NOTIFICATION: 1100,
31
+ } as const;
32
+
33
+ /**
34
+ * Keyboard shortcuts
35
+ */
36
+ export const SHORTCUTS = {
37
+ CREATE_TASK: "Alt+N",
38
+ CREATE_SPRINT: "Alt+S",
39
+ CLOSE_MODAL: "Escape",
40
+ SAVE: "Cmd+S",
41
+ SEARCH: "Cmd+K",
42
+ } as const;
43
+
44
+ /**
45
+ * API rate limiting
46
+ */
47
+ export const RATE_LIMITS = {
48
+ CREATE_SPRINT_MS: 1000,
49
+ CREATE_TASK_MS: 500,
50
+ UPDATE_TASK_MS: 500,
51
+ DELETE_TASK_MS: 500,
52
+ } as const;
53
+
54
+ /**
55
+ * Pagination and limits
56
+ */
57
+ export const PAGINATION = {
58
+ DEFAULT_LIMIT: 50,
59
+ DEFAULT_OFFSET: 0,
60
+ MAX_LIMIT: 100,
61
+ } as const;
62
+
63
+ /**
64
+ * Validation rules
65
+ */
66
+ export const VALIDATION = {
67
+ MIN_NAME_LENGTH: 1,
68
+ MAX_NAME_LENGTH: 255,
69
+ MIN_DESC_LENGTH: 0,
70
+ MAX_DESC_LENGTH: 10000,
71
+ MAX_COMMENT_LENGTH: 5000,
72
+ } as const;
73
+
74
+ /**
75
+ * React Query configuration
76
+ */
77
+ export const QUERY_CONFIG = {
78
+ DEFAULT_STALE_TIME: 0,
79
+ DEFAULT_GC_TIME: 0,
80
+ DEFAULT_RETRY: 1,
81
+ REFETCH_ON_WINDOW_FOCUS: false,
82
+ MUTATION_RETRY: 1,
83
+ } as const;
@@ -0,0 +1,96 @@
1
+ import { AssigneeRole, TaskPriority, TaskStatus } from "@locusai/shared";
2
+ import { capitalize } from "./utils";
3
+
4
+ export interface OptionItem<T extends string = string> {
5
+ value: T;
6
+ label: string;
7
+ color?: string;
8
+ }
9
+
10
+ /**
11
+ * Status color mapping for UI consistency.
12
+ */
13
+ const STATUS_COLOR_MAP: Record<TaskStatus, string> = {
14
+ [TaskStatus.BACKLOG]: "#64748b",
15
+ [TaskStatus.IN_PROGRESS]: "#f59e0b",
16
+ [TaskStatus.REVIEW]: "#a855f7",
17
+ [TaskStatus.VERIFICATION]: "#38bdf8",
18
+ [TaskStatus.DONE]: "#10b981",
19
+ [TaskStatus.BLOCKED]: "#ef4444",
20
+ };
21
+
22
+ /**
23
+ * Priority color mapping for UI consistency.
24
+ */
25
+ const PRIORITY_COLOR_MAP: Record<TaskPriority, string> = {
26
+ [TaskPriority.LOW]: "#64748b",
27
+ [TaskPriority.MEDIUM]: "#38bdf8",
28
+ [TaskPriority.HIGH]: "#f59e0b",
29
+ [TaskPriority.CRITICAL]: "#ef4444",
30
+ };
31
+
32
+ /**
33
+ * Get all available task status options.
34
+ */
35
+ export function getStatusOptions(): OptionItem<TaskStatus>[] {
36
+ return Object.values(TaskStatus).map((status) => ({
37
+ value: status,
38
+ label: status.replace(/_/g, " "),
39
+ color: STATUS_COLOR_MAP[status],
40
+ }));
41
+ }
42
+
43
+ /**
44
+ * Get status color for a specific status value.
45
+ */
46
+ export function getStatusColor(status: TaskStatus): string {
47
+ return STATUS_COLOR_MAP[status];
48
+ }
49
+
50
+ /**
51
+ * Get all available priority options.
52
+ */
53
+ export function getPriorityOptions(): OptionItem<TaskPriority>[] {
54
+ return [
55
+ {
56
+ value: TaskPriority.LOW,
57
+ label: "Low",
58
+ color: PRIORITY_COLOR_MAP[TaskPriority.LOW],
59
+ },
60
+ {
61
+ value: TaskPriority.MEDIUM,
62
+ label: "Medium",
63
+ color: PRIORITY_COLOR_MAP[TaskPriority.MEDIUM],
64
+ },
65
+ {
66
+ value: TaskPriority.HIGH,
67
+ label: "High",
68
+ color: PRIORITY_COLOR_MAP[TaskPriority.HIGH],
69
+ },
70
+ {
71
+ value: TaskPriority.CRITICAL,
72
+ label: "Critical",
73
+ color: PRIORITY_COLOR_MAP[TaskPriority.CRITICAL],
74
+ },
75
+ ];
76
+ }
77
+
78
+ /**
79
+ * Get all available assignee role options.
80
+ */
81
+ export function getAssigneeOptions(): OptionItem<AssigneeRole>[] {
82
+ return Object.values(AssigneeRole).map((role) => ({
83
+ value: role,
84
+ label: capitalize(role),
85
+ }));
86
+ }
87
+
88
+ /**
89
+ * Get all available membership role options.
90
+ */
91
+ export function getMembershipRoleOptions(): OptionItem[] {
92
+ return [
93
+ { value: "MEMBER", label: "Member" },
94
+ { value: "ADMIN", label: "Admin" },
95
+ ];
96
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Query Key Factory
3
+ *
4
+ * Centralizes all React Query keys to ensure consistent caching and invalidation.
5
+ * Follows the pattern: [entity, scope, ...params]
6
+ */
7
+
8
+ export const queryKeys = {
9
+ workspaces: {
10
+ all: () => ["workspaces"] as const,
11
+ list: () => [...queryKeys.workspaces.all(), "list"] as const,
12
+ detail: (id: string) =>
13
+ [...queryKeys.workspaces.all(), "detail", id] as const,
14
+ stats: (id: string) =>
15
+ [...queryKeys.workspaces.all(), "stats", id] as const,
16
+ activity: (id: string) =>
17
+ [...queryKeys.workspaces.all(), "activity", id] as const,
18
+ },
19
+ tasks: {
20
+ all: () => ["tasks"] as const,
21
+ list: (workspaceId?: string | null) =>
22
+ [
23
+ ...queryKeys.tasks.all(),
24
+ "list",
25
+ { workspaceId: workspaceId ?? undefined },
26
+ ] as const,
27
+ detail: (id: string, workspaceId?: string) =>
28
+ [...queryKeys.tasks.all(), "detail", id, { workspaceId }] as const,
29
+ backlog: (workspaceId?: string | null) =>
30
+ [
31
+ ...queryKeys.tasks.all(),
32
+ "backlog",
33
+ { workspaceId: workspaceId ?? undefined },
34
+ ] as const,
35
+ },
36
+ sprints: {
37
+ all: () => ["sprints"] as const,
38
+ list: (workspaceId?: string | null) =>
39
+ [
40
+ ...queryKeys.sprints.all(),
41
+ "list",
42
+ { workspaceId: workspaceId ?? undefined },
43
+ ] as const,
44
+ active: (workspaceId?: string | null) =>
45
+ [
46
+ ...queryKeys.sprints.all(),
47
+ "active",
48
+ { workspaceId: workspaceId ?? undefined },
49
+ ] as const,
50
+ detail: (id: string, workspaceId?: string) =>
51
+ [...queryKeys.sprints.all(), "detail", id, { workspaceId }] as const,
52
+ },
53
+ docs: {
54
+ all: () => ["docs"] as const,
55
+ list: (workspaceId?: string | null) =>
56
+ [
57
+ ...queryKeys.docs.all(),
58
+ "list",
59
+ { workspaceId: workspaceId ?? undefined },
60
+ ] as const,
61
+ detail: (id: string, workspaceId?: string | null) =>
62
+ [
63
+ ...queryKeys.docs.all(),
64
+ "detail",
65
+ id,
66
+ { workspaceId: workspaceId ?? undefined },
67
+ ] as const,
68
+ },
69
+ docGroups: {
70
+ all: () => ["doc-groups"] as const,
71
+ list: (workspaceId?: string | null) =>
72
+ [
73
+ ...queryKeys.docGroups.all(),
74
+ "list",
75
+ { workspaceId: workspaceId ?? undefined },
76
+ ] as const,
77
+ },
78
+ organizations: {
79
+ all: () => ["organizations"] as const,
80
+ list: () => [...queryKeys.organizations.all(), "list"] as const,
81
+ detail: (id: string) =>
82
+ [...queryKeys.organizations.all(), "detail", id] as const,
83
+ members: (id: string) =>
84
+ [...queryKeys.organizations.all(), "members", id] as const,
85
+ },
86
+ invitations: {
87
+ all: () => ["invitations"] as const,
88
+ list: (orgId: string) =>
89
+ [...queryKeys.invitations.all(), "list", orgId] as const,
90
+ },
91
+ };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Typography System
3
+ *
4
+ * Centralized typography scales and variants for brand-consistent design.
5
+ * All text components should consume from this system.
6
+ *
7
+ * Readability Standards:
8
+ * - Primary text: text-foreground (100%)
9
+ * - Secondary text: text-foreground/60-70% (better contrast than text-muted-foreground)
10
+ * - Tertiary text: text-foreground/40-50%
11
+ * - Labels: text-foreground/60% with uppercase
12
+ * - Disabled: text-muted-foreground
13
+ */
14
+
15
+ export const typographyConfig = {
16
+ // Heading scales
17
+ h1: {
18
+ className: "text-4xl font-black tracking-tight text-foreground",
19
+ description: "Page titles, major sections",
20
+ },
21
+ h2: {
22
+ className: "text-3xl font-bold tracking-tight text-foreground",
23
+ description: "Section headers",
24
+ },
25
+ h3: {
26
+ className: "text-2xl font-bold tracking-tight text-foreground",
27
+ description: "Subsection headers",
28
+ },
29
+ h4: {
30
+ className: "text-xl font-semibold tracking-tight text-foreground",
31
+ description: "Component titles",
32
+ },
33
+ h5: {
34
+ className: "text-lg font-semibold tracking-tight text-foreground",
35
+ description: "Card titles, labels",
36
+ },
37
+ h6: {
38
+ className: "text-base font-semibold tracking-tight text-foreground",
39
+ description: "Minor titles",
40
+ },
41
+
42
+ // Body text
43
+ bodyLg: {
44
+ className: "text-base font-normal leading-relaxed text-foreground",
45
+ description: "Large body text",
46
+ },
47
+ body: {
48
+ className: "text-sm font-normal leading-relaxed text-foreground",
49
+ description: "Standard body text",
50
+ },
51
+ bodySm: {
52
+ className: "text-xs font-normal leading-relaxed text-foreground/70",
53
+ description: "Small body text with better secondary contrast",
54
+ },
55
+
56
+ // Labels and captions - standardized for readability
57
+ label: {
58
+ className:
59
+ "text-[10px] font-bold uppercase tracking-widest text-foreground/60",
60
+ description: "Form labels, section labels - readable secondary text",
61
+ },
62
+ caption: {
63
+ className: "text-xs font-medium text-foreground/65",
64
+ description:
65
+ "Captions, helper text - improved contrast over muted-foreground",
66
+ },
67
+ captionSm: {
68
+ className: "text-[10px] font-medium text-foreground/60",
69
+ description: "Small captions - readable secondary text",
70
+ },
71
+
72
+ // Code and monospace
73
+ code: {
74
+ className:
75
+ "text-sm font-mono bg-secondary/30 px-2 py-1 rounded text-foreground",
76
+ description: "Inline code",
77
+ },
78
+ codeBlock: {
79
+ className: "text-sm font-mono leading-relaxed text-foreground",
80
+ description: "Code blocks",
81
+ },
82
+ } as const;
83
+
84
+ // Export for use in components
85
+ export type TypographyVariant = keyof typeof typographyConfig;
86
+
87
+ /**
88
+ * Get typography class name by variant
89
+ * @example
90
+ * const titleClass = getTypographyClass('h1');
91
+ */
92
+ export function getTypographyClass(variant: TypographyVariant): string {
93
+ return typographyConfig[variant].className;
94
+ }
95
+
96
+ /**
97
+ * Typography component props helper
98
+ */
99
+ export interface TypographyProps {
100
+ variant: TypographyVariant;
101
+ className?: string;
102
+ children: React.ReactNode;
103
+ }
package/src/lib/utils.ts CHANGED
@@ -4,3 +4,7 @@ import { twMerge } from "tailwind-merge";
4
4
  export function cn(...inputs: ClassValue[]) {
5
5
  return twMerge(clsx(inputs));
6
6
  }
7
+
8
+ export function capitalize(str: string) {
9
+ return str.charAt(0).toUpperCase() + str.slice(1);
10
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Authentication validation schemas and utilities
3
+ * Centralized validation for login, register, and invite flows
4
+ */
5
+
6
+ import { z } from "zod";
7
+
8
+ /**
9
+ * Email validation - used across all auth flows
10
+ */
11
+ export const EmailSchema = z
12
+ .string()
13
+ .email("Invalid email address")
14
+ .toLowerCase();
15
+
16
+ /**
17
+ * OTP validation - 6-digit code
18
+ */
19
+ export const OtpSchema = z.string().regex(/^\d{6}$/, "OTP must be 6 digits");
20
+
21
+ /**
22
+ * User name validation
23
+ */
24
+ export const NameSchema = z
25
+ .string()
26
+ .min(1, "Name is required")
27
+ .max(255, "Name must be less than 255 characters");
28
+
29
+ /**
30
+ * Company/organization name validation
31
+ */
32
+ export const CompanyNameSchema = z
33
+ .string()
34
+ .min(1, "Company name is required")
35
+ .max(255, "Company name must be less than 255 characters");
36
+
37
+ /**
38
+ * Workspace name validation
39
+ */
40
+ export const WorkspaceNameSchema = z
41
+ .string()
42
+ .min(1, "Workspace name is required")
43
+ .max(255, "Workspace name must be less than 255 characters");
44
+
45
+ /**
46
+ * Team size options
47
+ */
48
+ export const TeamSizeSchema = z.enum([
49
+ "solo",
50
+ "2-10",
51
+ "11-50",
52
+ "51-200",
53
+ "200+",
54
+ ]);
55
+
56
+ /**
57
+ * User role options
58
+ */
59
+ export const UserRoleSchema = z.enum([
60
+ "developer",
61
+ "designer",
62
+ "product_manager",
63
+ "other",
64
+ ]);
65
+
66
+ /**
67
+ * Login flow validation
68
+ */
69
+ export const LoginEmailSchema = z.object({
70
+ email: EmailSchema,
71
+ });
72
+
73
+ export const LoginOtpSchema = z.object({
74
+ email: EmailSchema,
75
+ otp: OtpSchema,
76
+ });
77
+
78
+ export type LoginEmail = z.infer<typeof LoginEmailSchema>;
79
+ export type LoginOtp = z.infer<typeof LoginOtpSchema>;
80
+
81
+ /**
82
+ * Registration flow validation
83
+ */
84
+ export const RegisterEmailSchema = z.object({
85
+ email: EmailSchema,
86
+ });
87
+
88
+ export const RegisterOtpSchema = z.object({
89
+ otp: OtpSchema,
90
+ });
91
+
92
+ export const RegisterProfileSchema = z.object({
93
+ name: NameSchema,
94
+ userRole: UserRoleSchema,
95
+ });
96
+
97
+ export const RegisterOrganizationSchema = z.object({
98
+ companyName: CompanyNameSchema,
99
+ teamSize: TeamSizeSchema,
100
+ });
101
+
102
+ export const RegisterWorkspaceSchema = z.object({
103
+ workspaceName: WorkspaceNameSchema,
104
+ });
105
+
106
+ export const RegisterInviteSchema = z.object({
107
+ invitedEmails: z.array(EmailSchema),
108
+ });
109
+
110
+ export const CompleteRegistrationSchema = z.object({
111
+ email: EmailSchema,
112
+ otp: OtpSchema,
113
+ name: NameSchema,
114
+ companyName: CompanyNameSchema.optional(),
115
+ teamSize: TeamSizeSchema,
116
+ userRole: UserRoleSchema,
117
+ workspaceName: WorkspaceNameSchema.optional(),
118
+ invitedEmails: z.array(EmailSchema).optional(),
119
+ });
120
+
121
+ export type RegisterProfile = z.infer<typeof RegisterProfileSchema>;
122
+ export type RegisterOrganization = z.infer<typeof RegisterOrganizationSchema>;
123
+ export type RegisterWorkspace = z.infer<typeof RegisterWorkspaceSchema>;
124
+ export type RegisterInvite = z.infer<typeof RegisterInviteSchema>;
125
+ export type CompleteRegistration = z.infer<typeof CompleteRegistrationSchema>;
126
+
127
+ /**
128
+ * Invitation acceptance validation
129
+ */
130
+ export const AcceptInvitationSchema = z.object({
131
+ email: EmailSchema,
132
+ name: NameSchema.optional(),
133
+ });
134
+
135
+ export type AcceptInvitation = z.infer<typeof AcceptInvitationSchema>;
136
+
137
+ /**
138
+ * Helper functions for validation
139
+ */
140
+
141
+ /**
142
+ * Validates email - simple utility
143
+ */
144
+ export function isValidEmail(email: string): boolean {
145
+ try {
146
+ EmailSchema.parse(email);
147
+ return true;
148
+ } catch {
149
+ return false;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Validates OTP format
155
+ */
156
+ export function isValidOtp(otp: string): boolean {
157
+ return /^\d{6}$/.test(otp);
158
+ }
159
+
160
+ /**
161
+ * Validates email array for invitations
162
+ */
163
+ export function validateInvitationEmails(emails: string[]): {
164
+ valid: string[];
165
+ invalid: string[];
166
+ } {
167
+ const valid: string[] = [];
168
+ const invalid: string[] = [];
169
+
170
+ emails.forEach((email) => {
171
+ if (isValidEmail(email)) {
172
+ valid.push(email);
173
+ } else {
174
+ invalid.push(email);
175
+ }
176
+ });
177
+
178
+ return { valid, invalid };
179
+ }
180
+
181
+ /**
182
+ * Removes duplicate emails (case-insensitive)
183
+ */
184
+ export function deduplicateEmails(emails: string[]): string[] {
185
+ const seen = new Set<string>();
186
+ return emails.filter((email) => {
187
+ const lower = email.toLowerCase();
188
+ if (seen.has(lower)) return false;
189
+ seen.add(lower);
190
+ return true;
191
+ });
192
+ }
@@ -1,3 +1,7 @@
1
- export * from "./doc.service";
2
- export * from "./sprint.service";
3
- export * from "./task.service";
1
+ /**
2
+ * Services Index
3
+ *
4
+ * Centralized service layer exports.
5
+ */
6
+
7
+ export * from "./notifications";
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Notification Service
3
+ *
4
+ * Centralized notification/toast handling using Sonner.
5
+ */
6
+
7
+ import { toast } from "sonner";
8
+ import { TIMING } from "@/lib/constants";
9
+
10
+ export type NotificationType =
11
+ | "default"
12
+ | "success"
13
+ | "error"
14
+ | "warning"
15
+ | "info";
16
+
17
+ interface NotificationOptions {
18
+ duration?: number;
19
+ action?: {
20
+ label: string;
21
+ onClick: () => void;
22
+ };
23
+ }
24
+
25
+ /**
26
+ * Show a notification
27
+ */
28
+ export function notify(
29
+ message: string,
30
+ type: NotificationType = "default",
31
+ options?: NotificationOptions
32
+ ) {
33
+ const duration = options?.duration || TIMING.TOAST_DURATION_MS;
34
+
35
+ const notificationProps = {
36
+ duration,
37
+ action: options?.action,
38
+ };
39
+
40
+ switch (type) {
41
+ case "success":
42
+ toast.success(message, notificationProps);
43
+ break;
44
+ case "error":
45
+ toast.error(message, notificationProps);
46
+ break;
47
+ case "warning":
48
+ toast(message, { ...notificationProps, icon: "⚠️" });
49
+ break;
50
+ case "info":
51
+ toast.info?.(message, notificationProps) ||
52
+ toast(message, notificationProps);
53
+ break;
54
+ default:
55
+ toast(message, notificationProps);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Specific notification helpers
61
+ */
62
+ export const notifications = {
63
+ success: (message: string, options?: NotificationOptions) =>
64
+ notify(message, "success", options),
65
+ error: (message: string, options?: NotificationOptions) =>
66
+ notify(message, "error", options),
67
+ warning: (message: string, options?: NotificationOptions) =>
68
+ notify(message, "warning", options),
69
+ info: (message: string, options?: NotificationOptions) =>
70
+ notify(message, "info", options),
71
+
72
+ // Common use cases
73
+ saved: () => notifications.success("Changes saved"),
74
+ deleted: () => notifications.success("Deleted successfully"),
75
+ created: (item: string) =>
76
+ notifications.success(`${item} created successfully`),
77
+ failed: (action: string) =>
78
+ notifications.error(`Failed to ${action}. Please try again.`),
79
+ loading: (message: string) => notify(message, "info"),
80
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Environment Utility Functions
3
+ */
4
+
5
+ export const isLocalMode = () => {
6
+ return process.env.NEXT_PUBLIC_LOCUS_MODE === "local";
7
+ };
8
+
9
+ export const isCloudMode = () => {
10
+ return process.env.NEXT_PUBLIC_LOCUS_MODE === "cloud";
11
+ };
12
+
13
+ export const getApiUrl = () => {
14
+ return process.env.NEXT_PUBLIC_API_URL || "http://localhost:3080/api";
15
+ };