@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,142 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { SecondaryText } from "@/components/typography";
5
+ import { Button, Modal } from "@/components/ui";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ export interface CreateModalField {
9
+ /** Field identifier */
10
+ name: string;
11
+ /** Display label */
12
+ label: string;
13
+ /** Form component (Input, Textarea, etc) */
14
+ component: React.ReactNode;
15
+ /** Whether field is required */
16
+ required?: boolean;
17
+ /** Help text for field */
18
+ help?: string;
19
+ }
20
+
21
+ export interface CreateModalProps {
22
+ /** Whether modal is open */
23
+ isOpen: boolean;
24
+ /** Modal title */
25
+ title: string;
26
+ /** Array of form fields */
27
+ fields: CreateModalField[];
28
+ /** Form submission handler */
29
+ onSubmit: (e: React.FormEvent) => Promise<void> | void;
30
+ /** Called to close modal */
31
+ onClose: () => void;
32
+ /** Submit button text */
33
+ submitText?: string;
34
+ /** Submit button variant */
35
+ submitVariant?: "primary" | "secondary";
36
+ /** Cancel button text */
37
+ cancelText?: string;
38
+ /** Whether submission is in progress */
39
+ isPending?: boolean;
40
+ /** Whether submit button is disabled */
41
+ submitDisabled?: boolean;
42
+ /** Optional icon for modal header */
43
+ icon?: React.ReactNode;
44
+ /** Modal size */
45
+ size?: "sm" | "md" | "lg";
46
+ }
47
+
48
+ /**
49
+ * Create Modal Component
50
+ *
51
+ * Reusable compound component for create/edit modals.
52
+ * Provides consistent styling, field rendering, and form submission.
53
+ * Supports custom fields, icons, and submission handlers.
54
+ *
55
+ * Features:
56
+ * - Customizable form fields
57
+ * - Consistent styling and layout
58
+ * - Submit and cancel actions
59
+ * - Loading state handling
60
+ * - Optional icon display
61
+ * - Multiple size options
62
+ *
63
+ * @component
64
+ * @example
65
+ * <CreateModal
66
+ * isOpen={isOpen}
67
+ * title="Create Task"
68
+ * fields={[
69
+ * {
70
+ * name: "title",
71
+ * label: "Task Title",
72
+ * component: <Input {...titleProps} />,
73
+ * required: true,
74
+ * },
75
+ * ]}
76
+ * onSubmit={handleSubmit}
77
+ * onClose={handleClose}
78
+ * submitText="Create"
79
+ * isPending={isLoading}
80
+ * />
81
+ */
82
+ export function CreateModal({
83
+ isOpen,
84
+ title,
85
+ fields,
86
+ onSubmit,
87
+ onClose,
88
+ submitText = "Submit",
89
+ submitVariant = "primary",
90
+ cancelText = "Cancel",
91
+ isPending = false,
92
+ submitDisabled = false,
93
+ icon,
94
+ size = "md",
95
+ }: CreateModalProps) {
96
+ return (
97
+ <Modal isOpen={isOpen} onClose={onClose} title={title} size={size}>
98
+ <form
99
+ onSubmit={onSubmit}
100
+ className={cn("space-y-6 py-2", "md:space-y-8")}
101
+ >
102
+ {fields.map((field) => (
103
+ <div key={field.name} className="space-y-3">
104
+ <SecondaryText as="label" size="xs" className="block ml-1">
105
+ {field.label}
106
+ {field.required && <span className="text-destructive">*</span>}
107
+ </SecondaryText>
108
+ {field.component}
109
+ {field.help && (
110
+ <p className="text-xs text-muted-foreground/60 ml-1">
111
+ {field.help}
112
+ </p>
113
+ )}
114
+ </div>
115
+ ))}
116
+
117
+ <div className="flex justify-end gap-3 pt-6 border-t mt-4">
118
+ <Button
119
+ type="button"
120
+ onClick={onClose}
121
+ variant="ghost"
122
+ className="px-6"
123
+ disabled={isPending}
124
+ >
125
+ {cancelText}
126
+ </Button>
127
+ <Button
128
+ type="submit"
129
+ variant={submitVariant}
130
+ disabled={submitDisabled || isPending}
131
+ isLoading={isPending}
132
+ loadingText={`${submitText}...`}
133
+ className="px-8 shadow-lg shadow-primary/10"
134
+ >
135
+ {icon && <span className="mr-2">{icon}</span>}
136
+ {submitText}
137
+ </Button>
138
+ </div>
139
+ </form>
140
+ </Modal>
141
+ );
142
+ }
@@ -0,0 +1 @@
1
+ export * from "./CreateModal";
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Rich Text Editor Component
3
+ *
4
+ * TipTap-based rich text editor with markdown support.
5
+ * Includes formatting toolbar and multiple editing modes.
6
+ * Supports headings, lists, code blocks, links, and more.
7
+ *
8
+ * Features:
9
+ * - Markdown support with live preview
10
+ * - Rich formatting toolbar
11
+ * - Code syntax highlighting
12
+ * - Task lists and checkboxes
13
+ * - Link editing
14
+ * - Undo/redo functionality
15
+ * - Heading and list formatting
16
+ * - Quote and code block support
17
+ *
18
+ * @example
19
+ * <Editor
20
+ * value={markdown}
21
+ * onChange={handleChange}
22
+ * placeholder="Write your documentation..."
23
+ * />
24
+ */
25
+
26
+ "use client";
27
+
28
+ import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
29
+ import { Color } from "@tiptap/extension-color";
30
+ import Highlight from "@tiptap/extension-highlight";
31
+ import Link from "@tiptap/extension-link";
32
+ import Placeholder from "@tiptap/extension-placeholder";
33
+ import TaskItem from "@tiptap/extension-task-item";
34
+ import TaskList from "@tiptap/extension-task-list";
35
+ import { TextStyle } from "@tiptap/extension-text-style";
36
+ import Typography from "@tiptap/extension-typography";
37
+ import type { Editor as TiptapEditor } from "@tiptap/react";
38
+ import { EditorContent, useEditor } from "@tiptap/react";
39
+ import StarterKit from "@tiptap/starter-kit";
40
+ import { common, createLowlight } from "lowlight";
41
+ import {
42
+ Bold,
43
+ CheckSquare,
44
+ Code as CodeIcon,
45
+ Heading1,
46
+ Heading2,
47
+ Heading3,
48
+ Highlighter,
49
+ Italic,
50
+ List,
51
+ ListOrdered,
52
+ Quote,
53
+ Redo,
54
+ Terminal,
55
+ Undo,
56
+ } from "lucide-react";
57
+ import { useEffect } from "react";
58
+ import { Markdown } from "tiptap-markdown";
59
+ import { cn } from "@/lib/utils";
60
+
61
+ const lowlight = createLowlight(common);
62
+
63
+ interface EditorProps {
64
+ value: string;
65
+ onChange: (value: string) => void;
66
+ readOnly?: boolean;
67
+ }
68
+
69
+ const MenuBar = ({ editor }: { editor: TiptapEditor | null }) => {
70
+ if (!editor) {
71
+ return null;
72
+ }
73
+
74
+ const buttons = [
75
+ {
76
+ icon: Heading1,
77
+ onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
78
+ isActive: editor.isActive("heading", { level: 1 }),
79
+ label: "H1",
80
+ },
81
+ {
82
+ icon: Heading2,
83
+ onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
84
+ isActive: editor.isActive("heading", { level: 2 }),
85
+ label: "H2",
86
+ },
87
+ {
88
+ icon: Heading3,
89
+ onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
90
+ isActive: editor.isActive("heading", { level: 3 }),
91
+ label: "H3",
92
+ },
93
+ {
94
+ icon: Bold,
95
+ onClick: () => editor.chain().focus().toggleBold().run(),
96
+ isActive: editor.isActive("bold"),
97
+ label: "Bold",
98
+ },
99
+ {
100
+ icon: Italic,
101
+ onClick: () => editor.chain().focus().toggleItalic().run(),
102
+ isActive: editor.isActive("italic"),
103
+ label: "Italic",
104
+ },
105
+ {
106
+ icon: Highlighter,
107
+ onClick: () => editor.chain().focus().toggleHighlight().run(),
108
+ isActive: editor.isActive("highlight"),
109
+ label: "Highlight",
110
+ },
111
+ {
112
+ icon: CodeIcon,
113
+ onClick: () => editor.chain().focus().toggleCode().run(),
114
+ isActive: editor.isActive("code"),
115
+ label: "Code",
116
+ },
117
+ {
118
+ icon: Terminal,
119
+ onClick: () => editor.chain().focus().toggleCodeBlock().run(),
120
+ isActive: editor.isActive("codeBlock"),
121
+ label: "Code Block",
122
+ },
123
+ {
124
+ icon: List,
125
+ onClick: () => editor.chain().focus().toggleBulletList().run(),
126
+ isActive: editor.isActive("bulletList"),
127
+ label: "Bullet List",
128
+ },
129
+ {
130
+ icon: ListOrdered,
131
+ onClick: () => editor.chain().focus().toggleOrderedList().run(),
132
+ isActive: editor.isActive("orderedList"),
133
+ label: "Ordered List",
134
+ },
135
+ {
136
+ icon: CheckSquare,
137
+ onClick: () => editor.chain().focus().toggleTaskList().run(),
138
+ isActive: editor.isActive("taskList"),
139
+ label: "Task List",
140
+ },
141
+ {
142
+ icon: Quote,
143
+ onClick: () => editor.chain().focus().toggleBlockquote().run(),
144
+ isActive: editor.isActive("blockquote"),
145
+ label: "Quote",
146
+ },
147
+ ];
148
+
149
+ return (
150
+ <div className="flex flex-wrap items-center gap-1 p-2 bg-card/50 backdrop-blur-md border-b border-border/40 sticky top-0 z-10">
151
+ {buttons.map((btn, i) => (
152
+ <button
153
+ key={i}
154
+ onClick={(e) => {
155
+ e.preventDefault();
156
+ btn.onClick();
157
+ }}
158
+ className={cn(
159
+ "p-2 rounded-lg transition-all hover:bg-secondary/80",
160
+ btn.isActive
161
+ ? "bg-primary text-primary-foreground shadow-sm"
162
+ : "text-muted-foreground"
163
+ )}
164
+ title={btn.label}
165
+ >
166
+ <btn.icon size={16} />
167
+ </button>
168
+ ))}
169
+ <div className="w-px h-6 bg-border/40 mx-1" />
170
+ <button
171
+ onClick={(e) => {
172
+ e.preventDefault();
173
+ editor.chain().focus().undo().run();
174
+ }}
175
+ disabled={!editor.can().chain().focus().undo().run()}
176
+ className="p-2 rounded-lg text-muted-foreground hover:bg-secondary/80 disabled:opacity-30"
177
+ >
178
+ <Undo size={16} />
179
+ </button>
180
+ <button
181
+ onClick={(e) => {
182
+ e.preventDefault();
183
+ editor.chain().focus().redo().run();
184
+ }}
185
+ disabled={!editor.can().chain().focus().redo().run()}
186
+ className="p-2 rounded-lg text-muted-foreground hover:bg-secondary/80 disabled:opacity-30"
187
+ >
188
+ <Redo size={16} />
189
+ </button>
190
+ </div>
191
+ );
192
+ };
193
+
194
+ export function Editor({ value, onChange, readOnly = false }: EditorProps) {
195
+ const editor = useEditor({
196
+ extensions: [
197
+ StarterKit.configure({
198
+ bulletList: {
199
+ keepMarks: true,
200
+ keepAttributes: false,
201
+ },
202
+ orderedList: {
203
+ keepMarks: true,
204
+ keepAttributes: false,
205
+ },
206
+ codeBlock: false,
207
+ }),
208
+ Placeholder.configure({
209
+ placeholder: "Initialize content flow...",
210
+ }),
211
+ Markdown.configure({
212
+ html: true,
213
+ tightLists: true,
214
+ tightListClass: "tight",
215
+ bulletListMarker: "-",
216
+ linkify: true,
217
+ breaks: true,
218
+ }),
219
+ Highlight.configure({ multicolor: true }),
220
+ TaskList,
221
+ TaskItem.configure({
222
+ nested: true,
223
+ }),
224
+ Link.configure({
225
+ openOnClick: false,
226
+ HTMLAttributes: {
227
+ class: "text-primary underline underline-offset-4",
228
+ },
229
+ }),
230
+ Typography,
231
+ CodeBlockLowlight.configure({
232
+ lowlight,
233
+ }),
234
+ TextStyle,
235
+ Color,
236
+ ],
237
+ immediatelyRender: false,
238
+ content: value,
239
+ editable: !readOnly,
240
+ onUpdate: ({ editor }) => {
241
+ // biome-ignore lint/suspicious/noExplicitAny: Tiptap storage is not strictly typed
242
+ const markdown = (editor.storage as any).markdown.getMarkdown();
243
+ onChange(markdown);
244
+ },
245
+ editorProps: {
246
+ attributes: {
247
+ class: cn(
248
+ "prose prose-invert max-w-none min-h-[500px] p-8 focus:outline-none",
249
+ "prose-pre:bg-secondary/40 prose-pre:border prose-pre:border-border/40 prose-pre:rounded-xl",
250
+ "prose-p:text-muted-foreground prose-p:leading-relaxed",
251
+ "prose-li:text-muted-foreground",
252
+ "prose-strong:text-foreground prose-strong:font-bold",
253
+ "prose-code:text-primary prose-code:bg-primary/5 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:before:content-none prose-code:after:content-none",
254
+ "prose-blockquote:border-l-primary prose-blockquote:bg-primary/5 prose-blockquote:py-2 prose-blockquote:rounded-r-xl"
255
+ ),
256
+ },
257
+ },
258
+ });
259
+
260
+ // Handle value updates from outside
261
+ useEffect(() => {
262
+ if (editor) {
263
+ // biome-ignore lint/suspicious/noExplicitAny: Tiptap storage is not strictly typed
264
+ const currentMarkdown = (editor.storage as any).markdown.getMarkdown();
265
+ if (value !== currentMarkdown) {
266
+ editor.commands.setContent(value);
267
+ }
268
+ }
269
+ }, [value, editor]);
270
+
271
+ return (
272
+ <div className="w-full h-full flex flex-col">
273
+ {!readOnly && <MenuBar editor={editor} />}
274
+ <div className="flex-1 overflow-y-auto custom-scrollbar">
275
+ <EditorContent editor={editor} />
276
+ </div>
277
+ </div>
278
+ );
279
+ }
@@ -1,21 +1,108 @@
1
+ /**
2
+ * Page Header Component
3
+ *
4
+ * Displays page title, subtitle, icon, and actions.
5
+ * Supports optional search with keyboard shortcut (Cmd+K).
6
+ * Used on main application pages for consistent header layout.
7
+ *
8
+ * Features:
9
+ * - Title and subtitle display
10
+ * - Optional icon
11
+ * - Action buttons area
12
+ * - Search with Cmd+K keyboard shortcut
13
+ * - Flexible layout for custom content
14
+ *
15
+ * @example
16
+ * <PageHeader
17
+ * title="Dashboard"
18
+ * subtitle="Your workspace overview"
19
+ * icon={<LayoutDashboard />}
20
+ * actions={<Button>Export</Button>}
21
+ * />
22
+ */
23
+
1
24
  "use client";
2
25
 
3
26
  import { Search } from "lucide-react";
27
+ import { type ReactNode, useEffect, useRef } from "react";
28
+
29
+ interface PageHeaderProps {
30
+ title: string;
31
+ subtitle?: string;
32
+ icon?: ReactNode;
33
+ actions?: ReactNode;
34
+ children?: ReactNode;
35
+ searchValue?: string;
36
+ onSearchChange?: (value: string) => void;
37
+ }
38
+
39
+ export function PageHeader({
40
+ title,
41
+ subtitle,
42
+ icon,
43
+ actions,
44
+ children,
45
+ searchValue,
46
+ onSearchChange,
47
+ }: PageHeaderProps) {
48
+ const inputRef = useRef<HTMLInputElement>(null);
49
+
50
+ useEffect(() => {
51
+ const handleKeyDown = (e: KeyboardEvent) => {
52
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
53
+ e.preventDefault();
54
+ inputRef.current?.focus();
55
+ }
56
+ };
57
+ window.addEventListener("keydown", handleKeyDown);
58
+ return () => window.removeEventListener("keydown", handleKeyDown);
59
+ }, []);
4
60
 
5
- export function Header() {
6
61
  return (
7
- <header className="flex items-center mb-6 py-3">
8
- <div className="relative flex-1 max-w-md group">
9
- <Search
10
- size={16}
11
- className="absolute left-3.5 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors"
12
- />
13
- <input
14
- type="text"
15
- placeholder="Search tasks, docs... (⌘K)"
16
- className="w-full bg-secondary/40 border border-border/50 rounded-lg pl-10 pr-4 py-2.5 text-sm text-foreground placeholder:text-muted-foreground/70 outline-none transition-all focus:border-primary/50 focus:ring-2 focus:ring-primary/20 focus:bg-background hover:bg-secondary/60"
17
- />
62
+ <header className="mb-6">
63
+ <div className="flex items-start justify-between gap-4">
64
+ {/* Left: Title area */}
65
+ <div className="flex items-center gap-4">
66
+ {icon && (
67
+ <div className="p-2.5 rounded-xl bg-linear-to-br from-primary/20 to-primary/5 border border-primary/20">
68
+ {icon}
69
+ </div>
70
+ )}
71
+ <div>
72
+ <h1 className="text-xl font-bold text-foreground">{title}</h1>
73
+ {subtitle && (
74
+ <p className="text-sm text-muted-foreground mt-0.5">{subtitle}</p>
75
+ )}
76
+ </div>
77
+ </div>
78
+
79
+ {/* Right: Search + Actions */}
80
+ <div className="flex items-center gap-3">
81
+ {/* Search */}
82
+ {onSearchChange && (
83
+ <div className="relative group hidden sm:block">
84
+ <Search
85
+ size={14}
86
+ className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors"
87
+ />
88
+ <input
89
+ ref={inputRef}
90
+ type="text"
91
+ placeholder="Search... (⌘K)"
92
+ value={searchValue}
93
+ onChange={(e) => onSearchChange(e.target.value)}
94
+ className="w-48 lg:w-56 bg-secondary/30 border border-border/40 rounded-lg pl-9 pr-3 py-2 text-xs text-foreground placeholder:text-muted-foreground/60 outline-none transition-all focus:border-primary/50 focus:ring-2 focus:ring-primary/20 focus:bg-background hover:bg-secondary/50"
95
+ />
96
+ </div>
97
+ )}
98
+
99
+ {/* Actions */}
100
+ {actions}
101
+ </div>
18
102
  </div>
103
+
104
+ {/* Optional children (e.g., filter bar, inline forms) */}
105
+ {children}
19
106
  </header>
20
107
  );
21
108
  }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Page Layout Component
3
+ *
4
+ * Standard layout wrapper for content pages.
5
+ * Provides consistent header, title, description, and content area.
6
+ *
7
+ * @example
8
+ * <PageLayout
9
+ * title="Team Management"
10
+ * description="Manage your organization members"
11
+ * actions={<Button>Invite</Button>}
12
+ * >
13
+ * <TeamList />
14
+ * </PageLayout>
15
+ */
16
+
17
+ "use client";
18
+
19
+ import { getTypographyClass } from "@/lib/typography";
20
+ import { cn } from "@/lib/utils";
21
+
22
+ interface PageLayoutProps {
23
+ /** Page title */
24
+ title: string;
25
+ /** Page description or subtitle */
26
+ description?: React.ReactNode;
27
+ /** Action buttons/controls for header */
28
+ actions?: React.ReactNode;
29
+ /** Main page content */
30
+ children: React.ReactNode;
31
+ /** Optional className override */
32
+ className?: string;
33
+ /** Optional content className override */
34
+ contentClassName?: string;
35
+ }
36
+
37
+ export function PageLayout({
38
+ title,
39
+ description,
40
+ actions,
41
+ children,
42
+ className,
43
+ contentClassName,
44
+ }: PageLayoutProps) {
45
+ return (
46
+ <div className={cn("flex flex-col h-full overflow-hidden", className)}>
47
+ <div className="flex-none pb-2">
48
+ <div className="flex items-center justify-between mb-2">
49
+ <div>
50
+ <h1 className={cn(getTypographyClass("h1"), "text-foreground/90")}>
51
+ {title}
52
+ </h1>
53
+ {description && (
54
+ <div className={cn(getTypographyClass("caption"), "mt-1")}>
55
+ {description}
56
+ </div>
57
+ )}
58
+ </div>
59
+ {actions && <div className="flex items-center gap-3">{actions}</div>}
60
+ </div>
61
+ </div>
62
+ <div
63
+ className={cn("flex-1 overflow-y-auto pb-12 mt-4", contentClassName)}
64
+ >
65
+ {children}
66
+ </div>
67
+ </div>
68
+ );
69
+ }