@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
@@ -1,19 +1,50 @@
1
+ /**
2
+ * Task Card Component
3
+ *
4
+ * Displays a task in card or list format.
5
+ * Shows title, status, priority, deadline, and actions.
6
+ * Supports drag-drop and delete operations.
7
+ *
8
+ * Features:
9
+ * - Card and list display variants
10
+ * - Priority color coding
11
+ * - Deadline display
12
+ * - Delete action menu
13
+ * - Click handler for selection
14
+ * - Drag state styling
15
+ *
16
+ * @example
17
+ * <TaskCard
18
+ * task={task}
19
+ * onClick={handleSelect}
20
+ * onDelete={handleDelete}
21
+ * isDragging={isDragging}
22
+ * variant="card"
23
+ * />
24
+ */
25
+
1
26
  "use client";
2
27
 
3
28
  import { type Task, TaskPriority } from "@locusai/shared";
4
- import { Calendar, Lock, MoreHorizontal, Tag, Trash2 } from "lucide-react";
29
+ import { Calendar, MoreHorizontal, Tag, Trash2 } from "lucide-react";
5
30
  import { useEffect, useRef, useState } from "react";
6
31
  import { cn } from "@/lib/utils";
7
32
 
8
33
  interface TaskCardProps {
34
+ /** Task to display */
9
35
  task: Task;
10
- onClick: () => void;
11
- onDelete: (id: number) => void;
36
+ /** Called when card is clicked */
37
+ onClick?: () => void;
38
+ /** Called when delete action is triggered */
39
+ onDelete?: (id: string) => void;
40
+ /** Whether task is being dragged */
12
41
  isDragging?: boolean;
42
+ /** Display variant (card or list) */
43
+ variant?: "card" | "list";
13
44
  }
14
45
 
15
46
  const PRIORITY_COLORS: Record<TaskPriority, string> = {
16
- [TaskPriority.LOW]: "var(--text-muted)",
47
+ [TaskPriority.LOW]: "#94a3b8",
17
48
  [TaskPriority.MEDIUM]: "#38bdf8",
18
49
  [TaskPriority.HIGH]: "#f59e0b",
19
50
  [TaskPriority.CRITICAL]: "#ef4444",
@@ -24,6 +55,7 @@ export function TaskCard({
24
55
  onClick,
25
56
  onDelete,
26
57
  isDragging,
58
+ variant = "card",
27
59
  }: TaskCardProps) {
28
60
  const [showMenu, setShowMenu] = useState(false);
29
61
  const menuRef = useRef<HTMLDivElement>(null);
@@ -38,101 +70,187 @@ export function TaskCard({
38
70
  return () => document.removeEventListener("mousedown", handleClickOutside);
39
71
  }, []);
40
72
 
41
- const isLocked =
42
- task.lockedBy && (!task.lockExpiresAt || task.lockExpiresAt > Date.now());
43
73
  const priority = (task.priority as TaskPriority) || TaskPriority.MEDIUM;
44
74
 
75
+ if (variant === "list") {
76
+ return (
77
+ <div
78
+ className={cn(
79
+ "group relative bg-card/40 border border-border/40 rounded-lg overflow-hidden transition-all hover:bg-secondary/20 hover:border-border cursor-pointer flex items-center h-10 px-3 gap-4",
80
+ isDragging && "opacity-50 scale-[0.98] shadow-lg"
81
+ )}
82
+ onClick={onClick}
83
+ >
84
+ <div className="flex items-center gap-2 min-w-[70px]">
85
+ <div
86
+ className="w-1.5 h-1.5 rounded-full"
87
+ style={{ backgroundColor: PRIORITY_COLORS[priority] }}
88
+ />
89
+ <span className="text-[9px] font-bold text-muted-foreground/60 uppercase tracking-tighter">
90
+ {priority.slice(0, 3)}
91
+ </span>
92
+ </div>
93
+
94
+ <h4 className="text-[13px] font-medium text-foreground/90 truncate flex-1">
95
+ {task.title}
96
+ </h4>
97
+
98
+ <div className="flex items-center gap-4 text-muted-foreground/50">
99
+ {task.acceptanceChecklist?.length > 0 && (
100
+ <div className="flex items-center gap-1 text-[10px] font-bold">
101
+ <span
102
+ className={cn(
103
+ "h-1 w-1 rounded-full",
104
+ task.acceptanceChecklist.every((i) => i.done)
105
+ ? "bg-emerald-500"
106
+ : "bg-primary/40"
107
+ )}
108
+ />
109
+ {task.acceptanceChecklist.filter((i) => i.done).length}/
110
+ {task.acceptanceChecklist.length}
111
+ </div>
112
+ )}
113
+
114
+ {task.assigneeRole && (
115
+ <div className="text-[9px] bg-primary/10 text-primary px-1.5 py-0.5 rounded font-bold border border-primary/20">
116
+ {task.assigneeRole}
117
+ </div>
118
+ )}
119
+
120
+ <div className="flex items-center gap-1 text-[10px]">
121
+ <Calendar size={10} />
122
+ <span>
123
+ {new Date(task.createdAt).toLocaleDateString(undefined, {
124
+ month: "short",
125
+ day: "numeric",
126
+ })}
127
+ </span>
128
+ </div>
129
+ </div>
130
+
131
+ {onDelete && (
132
+ <div className="flex items-center ml-2" ref={menuRef}>
133
+ <button
134
+ className="p-1 rounded-md text-muted-foreground opacity-0 group-hover:opacity-100 hover:bg-black/5 dark:hover:bg-white/5 transition-all"
135
+ onClick={(e) => {
136
+ e.stopPropagation();
137
+ if (confirm("Delete task?")) onDelete(task.id);
138
+ }}
139
+ >
140
+ <Trash2 size={13} />
141
+ </button>
142
+ </div>
143
+ )}
144
+ </div>
145
+ );
146
+ }
147
+
45
148
  return (
46
149
  <div
47
150
  className={cn(
48
151
  "group relative bg-card border rounded-xl overflow-hidden shadow-sm transition-all hover:shadow-md hover:border-muted-foreground/20 cursor-pointer",
49
152
  isDragging && "opacity-50 scale-95 rotate-1 shadow-lg"
50
153
  )}
51
- draggable
52
- onDragStart={(e) => {
53
- e.dataTransfer.setData("taskId", String(task.id));
54
- e.dataTransfer.effectAllowed = "move";
55
- }}
154
+ onClick={onClick}
56
155
  >
57
- <div className="p-4" onClick={onClick}>
58
- <div className="flex items-start gap-2 mb-3">
59
- <div className="flex flex-col items-center gap-1.5 pt-1">
60
- <span
61
- className="h-2 w-2 rounded-full shrink-0"
62
- style={{ background: PRIORITY_COLORS[priority] }}
63
- title={`Priority: ${priority}`}
64
- />
65
- {isLocked && (
66
- <span title={`Locked by ${task.lockedBy}`}>
67
- <Lock size={12} className="text-muted-foreground" />
68
- </span>
69
- )}
156
+ <div className="p-3.5">
157
+ <div className="flex items-start justify-between gap-3 mb-2">
158
+ <div className="flex items-center gap-2">
159
+ <div
160
+ className="px-1.5 py-0.5 rounded-sm text-[9px] font-bold uppercase tracking-wider"
161
+ style={{
162
+ backgroundColor: `${PRIORITY_COLORS[priority]}20`,
163
+ color: PRIORITY_COLORS[priority],
164
+ border: `1px solid ${PRIORITY_COLORS[priority]}30`,
165
+ }}
166
+ >
167
+ {priority}
168
+ </div>
70
169
  </div>
71
- <h4 className="text-[14px] font-semibold leading-tight text-foreground flex-1">
72
- {task.title}
73
- </h4>
170
+ {task.assigneeRole && (
171
+ <div className="text-[10px] bg-primary/10 text-primary px-1.5 py-0.5 rounded font-mono font-bold border border-primary/20">
172
+ {task.assigneeRole}
173
+ </div>
174
+ )}
74
175
  </div>
75
176
 
76
- {task.labels.length > 0 && (
77
- <div className="flex flex-wrap gap-1.5 mb-4">
78
- {task.labels.slice(0, 3).map((l) => (
79
- <span
80
- key={l}
81
- className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-sm bg-secondary text-[10px] font-bold uppercase tracking-wider text-muted-foreground"
82
- >
83
- <Tag size={10} /> {l}
84
- </span>
85
- ))}
86
- {task.labels.length > 3 && (
87
- <span className="px-1.5 py-0.5 rounded-sm bg-secondary text-[10px] font-bold text-muted-foreground">
88
- +{task.labels.length - 3}
177
+ <h4 className="font-semibold leading-snug text-foreground mb-1.5 text-[14px]">
178
+ {task.title}
179
+ </h4>
180
+
181
+ <div className="flex items-center justify-between mt-auto pt-2.5 border-t border-border/40">
182
+ <div className="flex items-center gap-3">
183
+ <div className="flex items-center gap-1 text-[10px] text-muted-foreground font-medium">
184
+ <Calendar size={10} />
185
+ <span>
186
+ {new Date(task.createdAt).toLocaleDateString(undefined, {
187
+ month: "short",
188
+ day: "numeric",
189
+ })}
89
190
  </span>
191
+ </div>
192
+ {task.acceptanceChecklist?.length > 0 && (
193
+ <div className="flex items-center gap-1 text-[10px] text-muted-foreground font-bold">
194
+ <span
195
+ className={cn(
196
+ "h-1.5 w-1.5 rounded-full",
197
+ task.acceptanceChecklist.every((i) => i.done)
198
+ ? "bg-emerald-500"
199
+ : "bg-primary/40"
200
+ )}
201
+ />
202
+ {task.acceptanceChecklist.filter((i) => i.done).length}/
203
+ {task.acceptanceChecklist.length}
204
+ </div>
90
205
  )}
91
206
  </div>
92
- )}
93
207
 
94
- <div className="flex justify-between items-center pt-2 border-t mt-auto">
95
- <div className="flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
96
- <Calendar size={12} />
97
- <span>{new Date(task.createdAt).toLocaleDateString()}</span>
98
- </div>
99
- {task.assigneeRole && (
100
- <div className="h-6 w-6 rounded-full bg-primary text-primary-foreground text-[10px] font-bold flex items-center justify-center border shadow-sm">
101
- {task.assigneeRole.charAt(0).toUpperCase()}
208
+ {task.labels.length > 0 && (
209
+ <div className="flex gap-1">
210
+ {task.labels.slice(0, 1).map((l) => (
211
+ <span
212
+ key={l}
213
+ className="text-[9px] font-medium text-muted-foreground/70 flex items-center gap-1"
214
+ >
215
+ <Tag size={9} /> {l}
216
+ </span>
217
+ ))}
102
218
  </div>
103
219
  )}
104
220
  </div>
105
221
  </div>
106
222
 
107
- <div className="absolute top-2 right-2" ref={menuRef}>
108
- <button
109
- className="p-1 rounded-md text-muted-foreground opacity-0 group-hover:opacity-100 hover:bg-secondary hover:text-foreground transition-all"
110
- onClick={(e) => {
111
- e.stopPropagation();
112
- setShowMenu(!showMenu);
113
- }}
114
- >
115
- <MoreHorizontal size={14} />
116
- </button>
117
-
118
- {showMenu && (
119
- <div className="absolute top-full right-0 mt-1 w-32 bg-popover border rounded-md p-1 shadow-lg z-50 animate-in fade-in slide-in-from-top-1 duration-200">
120
- <button
121
- className="flex items-center gap-2 w-full px-2 py-1.5 text-xs font-medium text-destructive rounded-sm hover:bg-destructive/10 transition-colors"
122
- onClick={(e) => {
123
- e.stopPropagation();
124
- if (confirm("Are you sure you want to delete this task?")) {
125
- onDelete(task.id);
126
- }
127
- setShowMenu(false);
128
- }}
129
- >
130
- <Trash2 size={14} />
131
- Delete
132
- </button>
133
- </div>
134
- )}
135
- </div>
223
+ {onDelete && (
224
+ <div className="absolute top-2 right-2" ref={menuRef}>
225
+ <button
226
+ className="p-1 rounded-md text-muted-foreground opacity-0 group-hover:opacity-100 hover:bg-secondary hover:text-foreground transition-all"
227
+ onClick={(e) => {
228
+ e.stopPropagation();
229
+ setShowMenu(!showMenu);
230
+ }}
231
+ >
232
+ <MoreHorizontal size={14} />
233
+ </button>
234
+
235
+ {showMenu && (
236
+ <div className="absolute top-full right-0 mt-1 w-32 bg-popover border rounded-md p-1 shadow-lg z-50 animate-in fade-in slide-in-from-top-1 duration-200">
237
+ <button
238
+ className="flex items-center gap-2 w-full px-2 py-1.5 text-xs font-medium text-destructive rounded-sm hover:bg-destructive/10 transition-colors"
239
+ onClick={(e) => {
240
+ e.stopPropagation();
241
+ if (confirm("Are you sure you want to delete this task?")) {
242
+ onDelete(task.id);
243
+ }
244
+ setShowMenu(false);
245
+ }}
246
+ >
247
+ <Trash2 size={14} />
248
+ Delete
249
+ </button>
250
+ </div>
251
+ )}
252
+ </div>
253
+ )}
136
254
  </div>
137
255
  );
138
256
  }