@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.
- package/CHANGELOG.md +26 -0
- package/next.config.js +15 -2
- package/package.json +26 -3
- package/src/app/(auth)/invite/page.tsx +109 -0
- package/src/app/(auth)/layout.tsx +19 -0
- package/src/app/(auth)/login/page.tsx +65 -0
- package/src/app/(auth)/onboarding/workspace/page.tsx +46 -0
- package/src/app/(auth)/register/page.tsx +165 -0
- package/src/app/(dashboard)/activity/page.tsx +7 -0
- package/src/app/(dashboard)/backlog/page.tsx +195 -0
- package/src/app/(dashboard)/board/page.tsx +141 -0
- package/src/app/(dashboard)/layout.tsx +32 -0
- package/src/app/(dashboard)/page.tsx +14 -0
- package/src/app/(dashboard)/settings/page.tsx +161 -0
- package/src/app/(dashboard)/settings/team/page.tsx +75 -0
- package/src/app/globals.css +259 -0
- package/src/app/layout.tsx +10 -20
- package/src/app/providers.tsx +26 -3
- package/src/components/AuthLayoutUI.tsx +53 -0
- package/src/components/BoardFilter.tsx +75 -74
- package/src/components/CreateModal/CreateModal.tsx +142 -0
- package/src/components/CreateModal/index.ts +1 -0
- package/src/components/Editor.tsx +279 -0
- package/src/components/Header.tsx +99 -12
- package/src/components/PageLayout.tsx +69 -0
- package/src/components/PropertyItem.tsx +55 -9
- package/src/components/Sidebar.tsx +280 -36
- package/src/components/SprintCreateModal.tsx +84 -0
- package/src/components/TaskCard.tsx +196 -78
- package/src/components/TaskCreateModal.tsx +181 -178
- package/src/components/TaskPanel.tsx +140 -692
- package/src/components/WorkspaceCreateModal.tsx +97 -0
- package/src/components/WorkspaceProtected.tsx +91 -0
- package/src/components/auth/InviteSteps.tsx +220 -0
- package/src/components/auth/LoginSteps.tsx +86 -0
- package/src/components/auth/RegisterSteps.tsx +371 -0
- package/src/components/auth/index.ts +3 -0
- package/src/components/backlog/BacklogList.tsx +92 -0
- package/src/components/backlog/BacklogSection.tsx +137 -0
- package/src/components/backlog/CompletedSprintItem.tsx +95 -0
- package/src/components/backlog/CompletedSprintsSection.tsx +77 -0
- package/src/components/backlog/SprintSection.tsx +155 -0
- package/src/components/backlog/constants.ts +26 -0
- package/src/components/board/BoardColumn.tsx +97 -0
- package/src/components/board/BoardContent.tsx +84 -0
- package/src/components/board/BoardEmptyState.tsx +82 -0
- package/src/components/board/BoardHeader.tsx +47 -0
- package/src/components/board/SprintMindmap.tsx +65 -0
- package/src/components/board/constants.ts +40 -0
- package/src/components/board/index.ts +5 -0
- package/src/components/common/ErrorState.tsx +124 -0
- package/src/components/common/LoadingState.tsx +83 -0
- package/src/components/common/index.ts +40 -0
- package/src/components/dashboard/ActivityFeed.tsx +77 -0
- package/src/components/dashboard/ActivityItem.tsx +207 -0
- package/src/components/dashboard/QuickActions.tsx +50 -0
- package/src/components/dashboard/StatCard.tsx +79 -0
- package/src/components/dnd/index.tsx +51 -0
- package/src/components/docs/DocsEditorArea.tsx +87 -0
- package/src/components/docs/DocsHeaderActions.tsx +121 -0
- package/src/components/docs/DocsSidebar.tsx +351 -0
- package/src/components/index.ts +7 -0
- package/src/components/onboarding/StepProgress.tsx +21 -0
- package/src/components/onboarding/index.ts +1 -0
- package/src/components/settings/ApiKeyConfirmationModal.tsx +81 -0
- package/src/components/settings/ApiKeyCreatedModal.tsx +96 -0
- package/src/components/settings/ApiKeysList.tsx +143 -0
- package/src/components/settings/ApiKeysSettings.tsx +144 -0
- package/src/components/settings/InviteMemberModal.tsx +106 -0
- package/src/components/settings/ProjectSetupGuide.tsx +147 -0
- package/src/components/settings/SettingItem.tsx +32 -0
- package/src/components/settings/SettingSection.tsx +50 -0
- package/src/components/settings/TeamInvitationsList.tsx +90 -0
- package/src/components/settings/TeamMembersList.tsx +95 -0
- package/src/components/settings/index.ts +8 -0
- package/src/components/task-panel/TaskActivity.tsx +127 -0
- package/src/components/task-panel/TaskChecklist.tsx +142 -0
- package/src/components/task-panel/TaskDescription.tsx +201 -0
- package/src/components/task-panel/TaskDocs.tsx +137 -0
- package/src/components/task-panel/TaskHeader.tsx +125 -0
- package/src/components/task-panel/TaskProperties.tsx +111 -0
- package/src/components/task-panel/index.ts +12 -0
- package/src/components/typography/EmptyStateText.tsx +59 -0
- package/src/components/typography/MetadataText.tsx +65 -0
- package/src/components/typography/SecondaryText.tsx +60 -0
- package/src/components/typography/SectionLabel.tsx +60 -0
- package/src/components/typography/index.ts +14 -0
- package/src/components/typography-scales.tsx +218 -0
- package/src/components/ui/Avatar.tsx +123 -0
- package/src/components/ui/Badge.tsx +69 -2
- package/src/components/ui/Button.tsx +71 -30
- package/src/components/ui/Checkbox.tsx +34 -0
- package/src/components/ui/Dropdown.tsx +67 -1
- package/src/components/ui/EmptyState.tsx +129 -0
- package/src/components/ui/Input.tsx +53 -6
- package/src/components/ui/Modal.tsx +45 -12
- package/src/components/ui/OtpInput.tsx +148 -0
- package/src/components/ui/Skeleton.tsx +36 -0
- package/src/components/ui/Spinner.tsx +112 -0
- package/src/components/ui/Textarea.tsx +28 -3
- package/src/components/ui/Toast.tsx +99 -0
- package/src/components/ui/Toggle.tsx +63 -0
- package/src/components/ui/constants.ts +108 -0
- package/src/components/ui/index.ts +7 -0
- package/src/context/AuthContext.tsx +140 -0
- package/src/context/index.ts +1 -0
- package/src/hooks/backlog/index.ts +13 -0
- package/src/hooks/backlog/useBacklogActions.ts +144 -0
- package/src/hooks/backlog/useBacklogComposite.ts +73 -0
- package/src/hooks/backlog/useBacklogData.ts +74 -0
- package/src/hooks/backlog/useBacklogDragDrop.ts +118 -0
- package/src/hooks/backlog/useBacklogUI.ts +74 -0
- package/src/hooks/index.ts +22 -0
- package/src/hooks/task-panel/index.ts +13 -0
- package/src/hooks/task-panel/useTaskActions.ts +200 -0
- package/src/hooks/task-panel/useTaskComputedValues.ts +30 -0
- package/src/hooks/task-panel/useTaskData.ts +78 -0
- package/src/hooks/task-panel/useTaskPanelComposite.ts +161 -0
- package/src/hooks/task-panel/useTaskUIState.ts +80 -0
- package/src/hooks/useAuthLayoutLogic.ts +43 -0
- package/src/hooks/useAuthenticatedUser.ts +36 -0
- package/src/hooks/useAuthenticatedUserWithOrg.ts +41 -0
- package/src/hooks/useBacklog.ts +303 -0
- package/src/hooks/useBoard.ts +230 -0
- package/src/hooks/useDashboardLayout.ts +49 -0
- package/src/hooks/useDocs.ts +279 -0
- package/src/hooks/useDocsQuery.ts +99 -0
- package/src/hooks/useDocsSidebarState.ts +104 -0
- package/src/hooks/useFormState.ts +40 -0
- package/src/hooks/useGlobalKeydowns.ts +52 -0
- package/src/hooks/useInviteForm.ts +122 -0
- package/src/hooks/useLoginForm.ts +84 -0
- package/src/hooks/useMutationWithToast.ts +56 -0
- package/src/hooks/useOrganizationQuery.ts +55 -0
- package/src/hooks/useRegisterForm.ts +216 -0
- package/src/hooks/useSprintsQuery.ts +38 -0
- package/src/hooks/useTaskDescription.ts +102 -0
- package/src/hooks/useTaskPanel.ts +341 -0
- package/src/hooks/useTasksQuery.ts +39 -0
- package/src/hooks/useTeamManagement.ts +92 -0
- package/src/hooks/useWorkspaceCreateForm.ts +70 -0
- package/src/hooks/useWorkspaceId.ts +29 -0
- package/src/lib/api-client.ts +40 -23
- package/src/lib/config.ts +25 -0
- package/src/lib/constants.ts +83 -0
- package/src/lib/options.ts +96 -0
- package/src/lib/query-keys.ts +91 -0
- package/src/lib/typography.ts +103 -0
- package/src/lib/utils.ts +4 -0
- package/src/lib/validation.ts +192 -0
- package/src/services/index.ts +7 -3
- package/src/services/notifications.ts +80 -0
- package/src/utils/env.utils.ts +15 -0
- package/src/views/ActivityView.tsx +123 -0
- package/src/views/Dashboard.tsx +126 -0
- package/src/views/Docs.tsx +98 -612
- package/tsconfig.tsbuildinfo +1 -1
- package/.next/BUILD_ID +0 -1
- package/.next/app-build-manifest.json +0 -55
- package/.next/app-path-routes-manifest.json +0 -8
- package/.next/build-manifest.json +0 -33
- package/.next/cache/.previewinfo +0 -1
- package/.next/cache/.rscinfo +0 -1
- package/.next/cache/.tsbuildinfo +0 -1
- package/.next/cache/config.json +0 -7
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/diagnostics/build-diagnostics.json +0 -6
- package/.next/diagnostics/framework.json +0 -1
- package/.next/export-detail.json +0 -5
- package/.next/export-marker.json +0 -6
- package/.next/images-manifest.json +0 -57
- package/.next/next-minimal-server.js.nft.json +0 -1
- package/.next/next-server.js.nft.json +0 -1
- package/.next/package.json +0 -1
- package/.next/prerender-manifest.json +0 -137
- package/.next/react-loadable-manifest.json +0 -32
- package/.next/required-server-files.json +0 -324
- package/.next/routes-manifest.json +0 -77
- package/.next/server/app/_not-found/page.js +0 -2
- package/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/.next/server/app/_not-found.html +0 -1
- package/.next/server/app/_not-found.meta +0 -8
- package/.next/server/app/_not-found.rsc +0 -21
- package/.next/server/app/backlog/page.js +0 -2
- package/.next/server/app/backlog/page.js.nft.json +0 -1
- package/.next/server/app/backlog/page_client-reference-manifest.js +0 -1
- package/.next/server/app/backlog.html +0 -1
- package/.next/server/app/backlog.meta +0 -7
- package/.next/server/app/backlog.rsc +0 -25
- package/.next/server/app/docs/page.js +0 -97
- package/.next/server/app/docs/page.js.nft.json +0 -1
- package/.next/server/app/docs/page_client-reference-manifest.js +0 -1
- package/.next/server/app/docs.html +0 -1
- package/.next/server/app/docs.meta +0 -7
- package/.next/server/app/docs.rsc +0 -26
- package/.next/server/app/favicon.ico/route.js +0 -1
- package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
- package/.next/server/app/favicon.ico.body +0 -0
- package/.next/server/app/favicon.ico.meta +0 -1
- package/.next/server/app/index.html +0 -1
- package/.next/server/app/index.meta +0 -7
- package/.next/server/app/index.rsc +0 -25
- package/.next/server/app/page.js +0 -2
- package/.next/server/app/page.js.nft.json +0 -1
- package/.next/server/app/page_client-reference-manifest.js +0 -1
- package/.next/server/app/settings/page.js +0 -2
- package/.next/server/app/settings/page.js.nft.json +0 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +0 -1
- package/.next/server/app/settings.html +0 -1
- package/.next/server/app/settings.meta +0 -7
- package/.next/server/app/settings.rsc +0 -25
- package/.next/server/app-paths-manifest.json +0 -8
- package/.next/server/chunks/496.js +0 -6
- package/.next/server/chunks/585.js +0 -1
- package/.next/server/chunks/665.js +0 -1
- package/.next/server/chunks/699.js +0 -22
- package/.next/server/chunks/852.js +0 -7
- package/.next/server/functions-config-manifest.json +0 -4
- package/.next/server/interception-route-rewrite-manifest.js +0 -1
- package/.next/server/middleware-build-manifest.js +0 -1
- package/.next/server/middleware-manifest.json +0 -6
- package/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/.next/server/next-font-manifest.js +0 -1
- package/.next/server/next-font-manifest.json +0 -1
- package/.next/server/pages/404.html +0 -1
- package/.next/server/pages/500.html +0 -1
- package/.next/server/pages/_app.js +0 -1
- package/.next/server/pages/_app.js.nft.json +0 -1
- package/.next/server/pages/_document.js +0 -1
- package/.next/server/pages/_document.js.nft.json +0 -1
- package/.next/server/pages/_error.js +0 -19
- package/.next/server/pages/_error.js.nft.json +0 -1
- package/.next/server/pages-manifest.json +0 -6
- package/.next/server/server-reference-manifest.js +0 -1
- package/.next/server/server-reference-manifest.json +0 -1
- package/.next/server/webpack-runtime.js +0 -1
- package/.next/static/D0NXe04ZCLNDckV_quc8g/_buildManifest.js +0 -1
- package/.next/static/D0NXe04ZCLNDckV_quc8g/_ssgManifest.js +0 -1
- package/.next/static/chunks/138.b98511c56423f8bb.js +0 -1
- package/.next/static/chunks/146-34259952c594a3b0.js +0 -1
- package/.next/static/chunks/337-d3bb75304d130513.js +0 -1
- package/.next/static/chunks/477.1a6ecfe53375bd9c.js +0 -1
- package/.next/static/chunks/487-1808785ba665f784.js +0 -1
- package/.next/static/chunks/544.a9569941cc886e9d.js +0 -1
- package/.next/static/chunks/87c73c54-1f4741035a95c140.js +0 -1
- package/.next/static/chunks/902-d6926825a9fe8784.js +0 -1
- package/.next/static/chunks/955-c8f8f6235ae8f8c6.js +0 -1
- package/.next/static/chunks/996.e0a334e6ae90900e.js +0 -1
- package/.next/static/chunks/app/_not-found/page-44b1804abb44a34d.js +0 -1
- package/.next/static/chunks/app/backlog/page-dce1450769bfae8f.js +0 -1
- package/.next/static/chunks/app/docs/page-1efee819f25492cb.js +0 -1
- package/.next/static/chunks/app/layout-05f504c042b9f7ee.js +0 -1
- package/.next/static/chunks/app/page-3fd91aaaa4776ced.js +0 -1
- package/.next/static/chunks/app/settings/page-84e16c9638d657e4.js +0 -1
- package/.next/static/chunks/framework-152a1bc8c81c7458.js +0 -1
- package/.next/static/chunks/main-843ab130fc1be309.js +0 -1
- package/.next/static/chunks/main-app-123e879c5a937a00.js +0 -1
- package/.next/static/chunks/pages/_app-a050a8e6e4fb04cf.js +0 -1
- package/.next/static/chunks/pages/_error-3e422ffd891594de.js +0 -1
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/.next/static/chunks/webpack-99a10a055b5bb9c4.js +0 -1
- package/.next/static/css/13e8617b72f9d3aa.css +0 -1
- package/.next/static/css/8aea088cdc4338f0.css +0 -1
- package/.next/static/css/b301ab0424111664.css +0 -1
- package/.next/static/media/24c15609eaa28576-s.woff2 +0 -0
- package/.next/static/media/2c07349e02a7b712-s.woff2 +0 -0
- package/.next/static/media/456105d6ea6d39e0-s.woff2 +0 -0
- package/.next/static/media/47cbc4e2adbc5db9-s.p.woff2 +0 -0
- package/.next/static/media/4f77bef990aad698-s.woff2 +0 -0
- package/.next/static/media/627d916fd739a539-s.woff2 +0 -0
- package/.next/static/media/63b255f18bea0ca9-s.woff2 +0 -0
- package/.next/static/media/70bd82ac89b4fa42-s.woff2 +0 -0
- package/.next/static/media/84602850c8fd81c3-s.woff2 +0 -0
- package/.next/trace +0 -46
- package/.next/types/app/backlog/page.ts +0 -84
- package/.next/types/app/docs/page.ts +0 -84
- package/.next/types/app/layout.ts +0 -84
- package/.next/types/app/page.ts +0 -84
- package/.next/types/app/settings/page.ts +0 -84
- package/.next/types/cache-life.d.ts +0 -141
- package/.next/types/package.json +0 -1
- package/next-env.d.ts +0 -5
- package/out/404.html +0 -1
- package/out/_next/static/D0NXe04ZCLNDckV_quc8g/_buildManifest.js +0 -1
- package/out/_next/static/D0NXe04ZCLNDckV_quc8g/_ssgManifest.js +0 -1
- package/out/_next/static/chunks/138.b98511c56423f8bb.js +0 -1
- package/out/_next/static/chunks/146-34259952c594a3b0.js +0 -1
- package/out/_next/static/chunks/337-d3bb75304d130513.js +0 -1
- package/out/_next/static/chunks/477.1a6ecfe53375bd9c.js +0 -1
- package/out/_next/static/chunks/487-1808785ba665f784.js +0 -1
- package/out/_next/static/chunks/544.a9569941cc886e9d.js +0 -1
- package/out/_next/static/chunks/87c73c54-1f4741035a95c140.js +0 -1
- package/out/_next/static/chunks/902-d6926825a9fe8784.js +0 -1
- package/out/_next/static/chunks/955-c8f8f6235ae8f8c6.js +0 -1
- package/out/_next/static/chunks/996.e0a334e6ae90900e.js +0 -1
- package/out/_next/static/chunks/app/_not-found/page-44b1804abb44a34d.js +0 -1
- package/out/_next/static/chunks/app/backlog/page-dce1450769bfae8f.js +0 -1
- package/out/_next/static/chunks/app/docs/page-1efee819f25492cb.js +0 -1
- package/out/_next/static/chunks/app/layout-05f504c042b9f7ee.js +0 -1
- package/out/_next/static/chunks/app/page-3fd91aaaa4776ced.js +0 -1
- package/out/_next/static/chunks/app/settings/page-84e16c9638d657e4.js +0 -1
- package/out/_next/static/chunks/framework-152a1bc8c81c7458.js +0 -1
- package/out/_next/static/chunks/main-843ab130fc1be309.js +0 -1
- package/out/_next/static/chunks/main-app-123e879c5a937a00.js +0 -1
- package/out/_next/static/chunks/pages/_app-a050a8e6e4fb04cf.js +0 -1
- package/out/_next/static/chunks/pages/_error-3e422ffd891594de.js +0 -1
- package/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/out/_next/static/chunks/webpack-99a10a055b5bb9c4.js +0 -1
- package/out/_next/static/css/13e8617b72f9d3aa.css +0 -1
- package/out/_next/static/css/8aea088cdc4338f0.css +0 -1
- package/out/_next/static/css/b301ab0424111664.css +0 -1
- package/out/_next/static/media/24c15609eaa28576-s.woff2 +0 -0
- package/out/_next/static/media/2c07349e02a7b712-s.woff2 +0 -0
- package/out/_next/static/media/456105d6ea6d39e0-s.woff2 +0 -0
- package/out/_next/static/media/47cbc4e2adbc5db9-s.p.woff2 +0 -0
- package/out/_next/static/media/4f77bef990aad698-s.woff2 +0 -0
- package/out/_next/static/media/627d916fd739a539-s.woff2 +0 -0
- package/out/_next/static/media/63b255f18bea0ca9-s.woff2 +0 -0
- package/out/_next/static/media/70bd82ac89b4fa42-s.woff2 +0 -0
- package/out/_next/static/media/84602850c8fd81c3-s.woff2 +0 -0
- package/out/backlog.html +0 -1
- package/out/backlog.txt +0 -25
- package/out/docs.html +0 -1
- package/out/docs.txt +0 -26
- package/out/favicon.ico +0 -0
- package/out/index.html +0 -1
- package/out/index.txt +0 -25
- package/out/logo.png +0 -0
- package/out/settings.html +0 -1
- package/out/settings.txt +0 -25
- package/src/app/backlog/page.tsx +0 -19
- package/src/app/page.tsx +0 -16
- package/src/app/settings/page.tsx +0 -194
- package/src/hooks/useTasks.ts +0 -119
- package/src/services/doc.service.ts +0 -27
- package/src/services/sprint.service.ts +0 -24
- package/src/services/task.service.ts +0 -75
- package/src/views/Backlog.tsx +0 -691
- package/src/views/Board.tsx +0 -306
- /package/src/app/{docs → (dashboard)/docs}/page.tsx +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completed Sprints Section Component
|
|
3
|
+
*
|
|
4
|
+
* Displays a collapsible section containing all completed sprints.
|
|
5
|
+
* Allows viewing historical sprint data and completed tasks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use client";
|
|
9
|
+
|
|
10
|
+
import { type Sprint, type Task } from "@locusai/shared";
|
|
11
|
+
import { motion } from "framer-motion";
|
|
12
|
+
import { Archive } from "lucide-react";
|
|
13
|
+
import { BacklogSection } from "./BacklogSection";
|
|
14
|
+
import { CompletedSprintItem } from "./CompletedSprintItem";
|
|
15
|
+
|
|
16
|
+
interface CompletedSprintsSectionProps {
|
|
17
|
+
/** Completed sprints */
|
|
18
|
+
sprints: Sprint[];
|
|
19
|
+
/** Whether section is expanded */
|
|
20
|
+
isExpanded: boolean;
|
|
21
|
+
/** Called when toggling section */
|
|
22
|
+
onToggle: () => void;
|
|
23
|
+
/** Function to get tasks for a sprint */
|
|
24
|
+
getSprintTasks: (sprintId: string) => Task[];
|
|
25
|
+
/** Set of expanded sprint IDs */
|
|
26
|
+
expandedSprints: Set<string>;
|
|
27
|
+
/** Called when toggling sprint item */
|
|
28
|
+
onToggleSprint: (section: string) => void;
|
|
29
|
+
/** Called when task is clicked */
|
|
30
|
+
onTaskClick: (taskId: string) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function CompletedSprintsSection({
|
|
34
|
+
sprints,
|
|
35
|
+
isExpanded,
|
|
36
|
+
onToggle,
|
|
37
|
+
getSprintTasks,
|
|
38
|
+
expandedSprints,
|
|
39
|
+
onToggleSprint,
|
|
40
|
+
onTaskClick,
|
|
41
|
+
}: CompletedSprintsSectionProps) {
|
|
42
|
+
if (sprints.length === 0) return null;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<motion.div
|
|
46
|
+
layout="position"
|
|
47
|
+
initial={{ opacity: 0 }}
|
|
48
|
+
animate={{ opacity: 1 }}
|
|
49
|
+
transition={{ duration: 0.15, delay: 0.1 }}
|
|
50
|
+
className="pt-4 border-t border-border/50"
|
|
51
|
+
>
|
|
52
|
+
<BacklogSection
|
|
53
|
+
id="completed"
|
|
54
|
+
title="Completed Sprints"
|
|
55
|
+
icon={<Archive size={18} className="text-emerald-500/80" />}
|
|
56
|
+
count={sprints.length}
|
|
57
|
+
isExpanded={isExpanded}
|
|
58
|
+
onToggle={onToggle}
|
|
59
|
+
accentColor="green"
|
|
60
|
+
>
|
|
61
|
+
<div className="space-y-2 mt-2">
|
|
62
|
+
{sprints.map((sprint) => (
|
|
63
|
+
<CompletedSprintItem
|
|
64
|
+
key={sprint.id}
|
|
65
|
+
name={sprint.name}
|
|
66
|
+
taskCount={getSprintTasks(sprint.id).length}
|
|
67
|
+
tasks={getSprintTasks(sprint.id)}
|
|
68
|
+
isExpanded={expandedSprints.has(`completed-sprint-${sprint.id}`)}
|
|
69
|
+
onToggle={() => onToggleSprint(`completed-sprint-${sprint.id}`)}
|
|
70
|
+
onTaskClick={onTaskClick}
|
|
71
|
+
/>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
</BacklogSection>
|
|
75
|
+
</motion.div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint Section Component
|
|
3
|
+
*
|
|
4
|
+
* Displays a sprint with its tasks in the backlog view.
|
|
5
|
+
* Supports start/complete actions and drag-drop task management.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <SprintSection
|
|
9
|
+
* sprint={sprint}
|
|
10
|
+
* tasks={sprintTasks}
|
|
11
|
+
* isExpanded={true}
|
|
12
|
+
* onToggle={handleToggle}
|
|
13
|
+
* isActive={true}
|
|
14
|
+
* onStart={handleStart}
|
|
15
|
+
* onComplete={handleComplete}
|
|
16
|
+
* onTaskClick={handleSelectTask}
|
|
17
|
+
* onTaskDelete={handleDeleteTask}
|
|
18
|
+
* />
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
"use client";
|
|
22
|
+
|
|
23
|
+
import { type Sprint, type Task } from "@locusai/shared";
|
|
24
|
+
import { motion } from "framer-motion";
|
|
25
|
+
import { CheckCircle, Flag, Layers, Play } from "lucide-react";
|
|
26
|
+
import { DraggableTask, DroppableSection } from "@/components/dnd";
|
|
27
|
+
import { TaskCard } from "@/components/TaskCard";
|
|
28
|
+
import { Button } from "@/components/ui";
|
|
29
|
+
import { BacklogSection } from "./BacklogSection";
|
|
30
|
+
|
|
31
|
+
interface SprintSectionProps {
|
|
32
|
+
/** Sprint data */
|
|
33
|
+
sprint: Sprint;
|
|
34
|
+
/** Tasks in this sprint */
|
|
35
|
+
tasks: Task[];
|
|
36
|
+
/** Whether section is expanded */
|
|
37
|
+
isExpanded: boolean;
|
|
38
|
+
/** Called when toggling expand state */
|
|
39
|
+
onToggle: () => void;
|
|
40
|
+
/** Whether this is the active sprint */
|
|
41
|
+
isActive?: boolean;
|
|
42
|
+
/** Called when completing sprint */
|
|
43
|
+
onComplete?: (sprintId: string) => void;
|
|
44
|
+
/** Called when starting sprint */
|
|
45
|
+
onStart?: (sprintId: string) => void;
|
|
46
|
+
/** Called when task is selected */
|
|
47
|
+
onTaskClick: (taskId: string) => void;
|
|
48
|
+
/** Called when delete action is triggered */
|
|
49
|
+
onTaskDelete: (taskId: string) => void;
|
|
50
|
+
/** Whether an action is in progress */
|
|
51
|
+
isSubmitting?: boolean;
|
|
52
|
+
/** Whether sprint can be started */
|
|
53
|
+
canStart?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function SprintSection({
|
|
57
|
+
sprint,
|
|
58
|
+
tasks,
|
|
59
|
+
isExpanded,
|
|
60
|
+
onToggle,
|
|
61
|
+
isActive,
|
|
62
|
+
onComplete,
|
|
63
|
+
onStart,
|
|
64
|
+
onTaskClick,
|
|
65
|
+
onTaskDelete,
|
|
66
|
+
isSubmitting,
|
|
67
|
+
canStart,
|
|
68
|
+
}: SprintSectionProps) {
|
|
69
|
+
const accentColor = isActive ? "emerald" : "amber";
|
|
70
|
+
const icon = isActive ? (
|
|
71
|
+
<Flag size={18} className="text-emerald-500" />
|
|
72
|
+
) : (
|
|
73
|
+
<Layers size={18} className="text-amber-500/80" />
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<motion.div
|
|
78
|
+
layout="position"
|
|
79
|
+
initial={{ opacity: 0 }}
|
|
80
|
+
animate={{ opacity: 1 }}
|
|
81
|
+
exit={{ opacity: 0 }}
|
|
82
|
+
transition={{ duration: 0.15 }}
|
|
83
|
+
>
|
|
84
|
+
<BacklogSection
|
|
85
|
+
id={`sprint-${sprint.id}`}
|
|
86
|
+
title={sprint.name}
|
|
87
|
+
icon={icon}
|
|
88
|
+
count={tasks.length}
|
|
89
|
+
isExpanded={isExpanded}
|
|
90
|
+
onToggle={onToggle}
|
|
91
|
+
accentColor={accentColor}
|
|
92
|
+
badge={isActive ? "Active" : "Planned"}
|
|
93
|
+
actions={
|
|
94
|
+
isActive ? (
|
|
95
|
+
<Button
|
|
96
|
+
size="sm"
|
|
97
|
+
variant="emerald-subtle"
|
|
98
|
+
onClick={() => onComplete?.(sprint.id)}
|
|
99
|
+
isLoading={isSubmitting}
|
|
100
|
+
>
|
|
101
|
+
<CheckCircle size={14} className="mr-1" />
|
|
102
|
+
Complete
|
|
103
|
+
</Button>
|
|
104
|
+
) : (
|
|
105
|
+
canStart && (
|
|
106
|
+
<Button
|
|
107
|
+
size="sm"
|
|
108
|
+
variant="amber"
|
|
109
|
+
onClick={() => onStart?.(sprint.id)}
|
|
110
|
+
isLoading={isSubmitting}
|
|
111
|
+
>
|
|
112
|
+
<Play size={12} className="mr-1.5 fill-current" />
|
|
113
|
+
Start
|
|
114
|
+
</Button>
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
>
|
|
119
|
+
<DroppableSection id={`sprint-${sprint.id}`}>
|
|
120
|
+
{tasks.length === 0 ? (
|
|
121
|
+
<div
|
|
122
|
+
className={`py-8 border-2 border-dashed rounded-xl text-center text-[11px] font-medium ${
|
|
123
|
+
isActive
|
|
124
|
+
? "border-emerald-500/10 text-emerald-500/50 bg-emerald-500/5"
|
|
125
|
+
: "border-amber-500/10 text-amber-500/50 bg-amber-500/5"
|
|
126
|
+
}`}
|
|
127
|
+
>
|
|
128
|
+
Drag tasks here to add to this sprint
|
|
129
|
+
</div>
|
|
130
|
+
) : (
|
|
131
|
+
<div className="space-y-1.5 pt-1">
|
|
132
|
+
{tasks.map((task) => (
|
|
133
|
+
<motion.div
|
|
134
|
+
key={task.id}
|
|
135
|
+
layout="position"
|
|
136
|
+
initial={{ opacity: 0 }}
|
|
137
|
+
animate={{ opacity: 1 }}
|
|
138
|
+
>
|
|
139
|
+
<DraggableTask task={task}>
|
|
140
|
+
<TaskCard
|
|
141
|
+
task={task}
|
|
142
|
+
variant="list"
|
|
143
|
+
onClick={() => onTaskClick(task.id)}
|
|
144
|
+
onDelete={onTaskDelete}
|
|
145
|
+
/>
|
|
146
|
+
</DraggableTask>
|
|
147
|
+
</motion.div>
|
|
148
|
+
))}
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
</DroppableSection>
|
|
152
|
+
</BacklogSection>
|
|
153
|
+
</motion.div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const SPRINT_STATUS_COLORS = {
|
|
2
|
+
active: {
|
|
3
|
+
accent: "primary",
|
|
4
|
+
border: "border-l-primary",
|
|
5
|
+
bg: "bg-primary/5",
|
|
6
|
+
badge: "bg-primary/10 text-primary",
|
|
7
|
+
},
|
|
8
|
+
planned: {
|
|
9
|
+
accent: "amber",
|
|
10
|
+
border: "border-l-amber-400",
|
|
11
|
+
bg: "bg-amber-50/50",
|
|
12
|
+
badge: "bg-amber-100 text-amber-700",
|
|
13
|
+
},
|
|
14
|
+
completed: {
|
|
15
|
+
accent: "green",
|
|
16
|
+
border: "border-l-green-400",
|
|
17
|
+
bg: "bg-green-50/50",
|
|
18
|
+
badge: "bg-green-100 text-green-700",
|
|
19
|
+
},
|
|
20
|
+
backlog: {
|
|
21
|
+
accent: "slate",
|
|
22
|
+
border: "border-l-slate-400",
|
|
23
|
+
bg: "bg-slate-50/50",
|
|
24
|
+
badge: "bg-slate-100 text-slate-700",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board Column Component
|
|
3
|
+
*
|
|
4
|
+
* Displays a single kanban column for a task status.
|
|
5
|
+
* Supports drag-and-drop task movement between columns.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <BoardColumn
|
|
9
|
+
* statusKey="IN_PROGRESS"
|
|
10
|
+
* title="In Progress"
|
|
11
|
+
* tasks={tasksInProgress}
|
|
12
|
+
* onTaskClick={handleSelectTask}
|
|
13
|
+
* onTaskDelete={handleDeleteTask}
|
|
14
|
+
* />
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
"use client";
|
|
18
|
+
|
|
19
|
+
import { type Task } from "@locusai/shared";
|
|
20
|
+
import { DraggableTask, DroppableSection } from "@/components/dnd";
|
|
21
|
+
import { TaskCard } from "@/components/TaskCard";
|
|
22
|
+
import { cn } from "@/lib/utils";
|
|
23
|
+
import { BOARD_STATUSES } from "./constants";
|
|
24
|
+
|
|
25
|
+
interface BoardColumnProps {
|
|
26
|
+
/** Unique key identifying the status */
|
|
27
|
+
statusKey: string;
|
|
28
|
+
/** Display title for the column */
|
|
29
|
+
title: string;
|
|
30
|
+
/** Tasks to display in this column */
|
|
31
|
+
tasks: Task[];
|
|
32
|
+
/** Called when a task card is clicked */
|
|
33
|
+
onTaskClick: (taskId: string) => void;
|
|
34
|
+
/** Called when delete action is triggered */
|
|
35
|
+
onTaskDelete: (taskId: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Board Column Component
|
|
40
|
+
*
|
|
41
|
+
* Features:
|
|
42
|
+
* - Displays tasks in a kanban column
|
|
43
|
+
* - Drag-drop enabled via DroppableSection
|
|
44
|
+
* - Shows task count
|
|
45
|
+
* - Empty state message
|
|
46
|
+
* - Color-coded by status
|
|
47
|
+
*
|
|
48
|
+
* @component
|
|
49
|
+
*/
|
|
50
|
+
export function BoardColumn({
|
|
51
|
+
statusKey,
|
|
52
|
+
title,
|
|
53
|
+
tasks,
|
|
54
|
+
onTaskClick,
|
|
55
|
+
onTaskDelete,
|
|
56
|
+
}: BoardColumnProps) {
|
|
57
|
+
const statusConfig = BOARD_STATUSES.find((s) => s.key === statusKey);
|
|
58
|
+
const colorClass = statusConfig?.className || "bg-slate-500";
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="flex flex-col w-80 shrink-0 h-full">
|
|
62
|
+
{/* Column Header */}
|
|
63
|
+
<div className="flex items-center gap-2 mb-3 px-1">
|
|
64
|
+
<div className={cn("w-2 h-2 rounded-full", colorClass)} />
|
|
65
|
+
<span className="text-sm font-semibold text-foreground tracking-tight">
|
|
66
|
+
{title}
|
|
67
|
+
</span>
|
|
68
|
+
<span className="text-xs text-muted-foreground bg-muted/50 px-2 py-0.5 rounded-full font-medium">
|
|
69
|
+
{tasks.length}
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Column Content */}
|
|
74
|
+
<DroppableSection id={statusKey}>
|
|
75
|
+
<div className="flex-1 rounded-xl bg-secondary/10 border border-border/40 p-2 min-h-[calc(100vh-220px)] backdrop-blur-sm transition-colors hover:bg-secondary/20">
|
|
76
|
+
{tasks.length === 0 ? (
|
|
77
|
+
<div className="h-full flex flex-col items-center justify-center text-xs text-muted-foreground/60 gap-2 opacity-0 hover:opacity-100 transition-opacity">
|
|
78
|
+
<span>Drop tasks here</span>
|
|
79
|
+
</div>
|
|
80
|
+
) : (
|
|
81
|
+
<div className="space-y-3">
|
|
82
|
+
{tasks.map((task) => (
|
|
83
|
+
<DraggableTask key={task.id} task={task}>
|
|
84
|
+
<TaskCard
|
|
85
|
+
task={task}
|
|
86
|
+
onClick={() => onTaskClick(task.id)}
|
|
87
|
+
onDelete={onTaskDelete}
|
|
88
|
+
/>
|
|
89
|
+
</DraggableTask>
|
|
90
|
+
))}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
</DroppableSection>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board main content component
|
|
3
|
+
* Renders columns, empty state, or drag overlay
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
closestCenter,
|
|
8
|
+
DndContext,
|
|
9
|
+
type DndContextProps,
|
|
10
|
+
DragOverlay,
|
|
11
|
+
} from "@dnd-kit/core";
|
|
12
|
+
import { type Task } from "@locusai/shared";
|
|
13
|
+
import { BoardColumn, BoardEmptyState } from "@/components/board";
|
|
14
|
+
import { BOARD_STATUSES } from "@/components/board/constants";
|
|
15
|
+
import { TaskCard } from "@/components/TaskCard";
|
|
16
|
+
|
|
17
|
+
interface BoardContentProps {
|
|
18
|
+
filteredTasks: Task[];
|
|
19
|
+
tasksByStatus: Record<string, Task[]>;
|
|
20
|
+
shouldShowEmptyState: boolean;
|
|
21
|
+
activeTask: Task | null;
|
|
22
|
+
sensors: DndContextProps["sensors"];
|
|
23
|
+
onDragStart: DndContextProps["onDragStart"];
|
|
24
|
+
onDragEnd: DndContextProps["onDragEnd"];
|
|
25
|
+
onTaskClick: (taskId: string) => void;
|
|
26
|
+
onTaskDelete: (taskId: string) => void;
|
|
27
|
+
onNewTask: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function BoardContent({
|
|
31
|
+
filteredTasks,
|
|
32
|
+
tasksByStatus,
|
|
33
|
+
shouldShowEmptyState,
|
|
34
|
+
activeTask,
|
|
35
|
+
sensors,
|
|
36
|
+
onDragStart,
|
|
37
|
+
onDragEnd,
|
|
38
|
+
onTaskClick,
|
|
39
|
+
onTaskDelete,
|
|
40
|
+
onNewTask,
|
|
41
|
+
}: BoardContentProps) {
|
|
42
|
+
return (
|
|
43
|
+
<DndContext
|
|
44
|
+
sensors={sensors}
|
|
45
|
+
collisionDetection={closestCenter}
|
|
46
|
+
onDragStart={onDragStart}
|
|
47
|
+
onDragEnd={onDragEnd}
|
|
48
|
+
>
|
|
49
|
+
{shouldShowEmptyState ? (
|
|
50
|
+
<div className="flex-1">
|
|
51
|
+
<BoardEmptyState hasActiveSprint={false} onNewTask={onNewTask} />
|
|
52
|
+
</div>
|
|
53
|
+
) : filteredTasks.length === 0 ? (
|
|
54
|
+
<div className="flex-1">
|
|
55
|
+
<BoardEmptyState hasActiveSprint={true} onNewTask={onNewTask} />
|
|
56
|
+
</div>
|
|
57
|
+
) : (
|
|
58
|
+
<div className="flex-1 overflow-x-auto min-h-0">
|
|
59
|
+
<div className="flex gap-4 h-full min-w-max pb-4">
|
|
60
|
+
{BOARD_STATUSES.map((status) => (
|
|
61
|
+
<BoardColumn
|
|
62
|
+
key={status.key}
|
|
63
|
+
statusKey={status.key}
|
|
64
|
+
title={status.label}
|
|
65
|
+
tasks={tasksByStatus[status.key] || []}
|
|
66
|
+
onTaskClick={onTaskClick}
|
|
67
|
+
onTaskDelete={onTaskDelete}
|
|
68
|
+
/>
|
|
69
|
+
))}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{/* Drag Overlay */}
|
|
75
|
+
<DragOverlay>
|
|
76
|
+
{activeTask && (
|
|
77
|
+
<div className="opacity-90 rotate-2 shadow-2xl cursor-grabbing w-72">
|
|
78
|
+
<TaskCard task={activeTask} />
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</DragOverlay>
|
|
82
|
+
</DndContext>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board Empty State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays contextual empty state based on sprint status.
|
|
5
|
+
* Guides users to create sprints or add tasks.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <BoardEmptyState hasActiveSprint={false} onNewTask={handleCreateTask} />
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
"use client";
|
|
12
|
+
|
|
13
|
+
import { Inbox, Layers, Plus } from "lucide-react";
|
|
14
|
+
import { useRouter } from "next/navigation";
|
|
15
|
+
import { Button, EmptyState } from "@/components/ui";
|
|
16
|
+
|
|
17
|
+
interface BoardEmptyStateProps {
|
|
18
|
+
/** Whether there's an active sprint */
|
|
19
|
+
hasActiveSprint: boolean;
|
|
20
|
+
/** Callback to create a new task */
|
|
21
|
+
onNewTask: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Board Empty State Component
|
|
26
|
+
*
|
|
27
|
+
* Features:
|
|
28
|
+
* - Shows "no active sprint" state with backlog navigation
|
|
29
|
+
* - Shows "no tasks" state with create task button
|
|
30
|
+
* - Navigation to backlog and sprint creation
|
|
31
|
+
*
|
|
32
|
+
* @component
|
|
33
|
+
*/
|
|
34
|
+
export function BoardEmptyState({
|
|
35
|
+
hasActiveSprint,
|
|
36
|
+
onNewTask,
|
|
37
|
+
}: BoardEmptyStateProps) {
|
|
38
|
+
const router = useRouter();
|
|
39
|
+
|
|
40
|
+
if (!hasActiveSprint) {
|
|
41
|
+
return (
|
|
42
|
+
<EmptyState
|
|
43
|
+
icon={Layers}
|
|
44
|
+
title="No Active Sprint"
|
|
45
|
+
description="There is no active sprint currently. Go to the backlog to plan and start a new sprint."
|
|
46
|
+
action={
|
|
47
|
+
<div className="flex gap-3">
|
|
48
|
+
<Button
|
|
49
|
+
variant="outline"
|
|
50
|
+
className="gap-2"
|
|
51
|
+
onClick={() => router.push("/backlog")}
|
|
52
|
+
>
|
|
53
|
+
<Layers size={16} />
|
|
54
|
+
Go to Backlog
|
|
55
|
+
</Button>
|
|
56
|
+
<Button
|
|
57
|
+
className="gap-2"
|
|
58
|
+
onClick={() => router.push("/backlog?createSprint=true")}
|
|
59
|
+
>
|
|
60
|
+
<Plus size={16} />
|
|
61
|
+
Create Sprint
|
|
62
|
+
</Button>
|
|
63
|
+
</div>
|
|
64
|
+
}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<EmptyState
|
|
71
|
+
icon={Inbox}
|
|
72
|
+
title="No tasks in Sprint"
|
|
73
|
+
description="The active sprint has no tasks yet. Add tasks from the backlog or create new ones."
|
|
74
|
+
action={
|
|
75
|
+
<Button onClick={onNewTask} className="shadow-lg shadow-primary/20">
|
|
76
|
+
<Plus size={18} className="mr-2" />
|
|
77
|
+
New Task
|
|
78
|
+
</Button>
|
|
79
|
+
}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board page header component
|
|
3
|
+
* Displays sprint info and new task button
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type Sprint } from "@locusai/shared";
|
|
7
|
+
import { Plus } from "lucide-react";
|
|
8
|
+
import { Button } from "@/components/ui";
|
|
9
|
+
|
|
10
|
+
interface BoardHeaderProps {
|
|
11
|
+
activeSprint?: Sprint | null;
|
|
12
|
+
filteredTasksCount: number;
|
|
13
|
+
onNewTask: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function BoardHeader({
|
|
17
|
+
activeSprint,
|
|
18
|
+
filteredTasksCount,
|
|
19
|
+
onNewTask,
|
|
20
|
+
}: BoardHeaderProps) {
|
|
21
|
+
return {
|
|
22
|
+
title: "Board",
|
|
23
|
+
description: (
|
|
24
|
+
<div className="flex items-center gap-2">
|
|
25
|
+
{activeSprint ? (
|
|
26
|
+
<>
|
|
27
|
+
<span className="text-primary font-bold">{activeSprint.name}</span>
|
|
28
|
+
<span className="w-1 h-1 rounded-full bg-muted-foreground/30" />
|
|
29
|
+
<span>{filteredTasksCount} tasks</span>
|
|
30
|
+
</>
|
|
31
|
+
) : (
|
|
32
|
+
<span>No active sprint</span>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
),
|
|
36
|
+
actions: (
|
|
37
|
+
<Button
|
|
38
|
+
onClick={onNewTask}
|
|
39
|
+
size="md"
|
|
40
|
+
className="shadow-lg shadow-primary/20"
|
|
41
|
+
>
|
|
42
|
+
<Plus size={18} className="mr-2" />
|
|
43
|
+
New Task
|
|
44
|
+
</Button>
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { motion } from "framer-motion";
|
|
4
|
+
import { Editor } from "@/components/Editor";
|
|
5
|
+
|
|
6
|
+
interface SprintMindmapProps {
|
|
7
|
+
mindmap: string | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SprintMindmap({ mindmap }: SprintMindmapProps) {
|
|
11
|
+
if (!mindmap) {
|
|
12
|
+
return (
|
|
13
|
+
<div className="flex flex-col items-center justify-center h-[60vh] text-muted-foreground border-2 border-dashed rounded-xl bg-muted/10">
|
|
14
|
+
<p className="text-lg font-medium text-foreground">
|
|
15
|
+
No mindmap generated yet
|
|
16
|
+
</p>
|
|
17
|
+
<p className="text-sm opacity-70 max-w-xs text-center mt-2">
|
|
18
|
+
The agent will analyze your sprint and generate a strategic mindmap
|
|
19
|
+
once it starts working.
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<motion.div
|
|
27
|
+
initial={{ opacity: 0, y: 20 }}
|
|
28
|
+
animate={{ opacity: 1, y: 0 }}
|
|
29
|
+
className="flex-1 relative min-h-[70vh] bg-[#09090b] rounded-xl border border-white/5 overflow-hidden"
|
|
30
|
+
>
|
|
31
|
+
{/* Canvas Grid Background */}
|
|
32
|
+
<div
|
|
33
|
+
className="absolute inset-0 z-0 pointer-events-none opacity-20"
|
|
34
|
+
style={{
|
|
35
|
+
backgroundImage:
|
|
36
|
+
"radial-gradient(circle, #3f3f46 1px, transparent 1px)",
|
|
37
|
+
backgroundSize: "24px 24px",
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<div className="relative z-10 p-8 md:p-12 h-full max-h-[80vh] overflow-hidden">
|
|
42
|
+
<div className="max-w-5xl mx-auto h-full">
|
|
43
|
+
<div className="bg-black/40 backdrop-blur-md border border-white/10 rounded-2xl shadow-2xl shadow-black/50 h-full flex flex-col overflow-hidden">
|
|
44
|
+
<div className="flex-1 overflow-hidden">
|
|
45
|
+
<Editor
|
|
46
|
+
value={mindmap}
|
|
47
|
+
onChange={() => {
|
|
48
|
+
/* Read-only */
|
|
49
|
+
}}
|
|
50
|
+
readOnly={true}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
{/* Floating Badge */}
|
|
58
|
+
<div className="absolute top-6 right-6 z-20">
|
|
59
|
+
<div className="px-3 py-1 bg-indigo-500/10 border border-indigo-500/20 rounded-full text-[10px] font-bold tracking-widest text-indigo-400 uppercase backdrop-blur-sm">
|
|
60
|
+
Agent Strategy Core
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</motion.div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { TaskStatus } from "@locusai/shared";
|
|
2
|
+
|
|
3
|
+
export const BOARD_STATUSES = [
|
|
4
|
+
{
|
|
5
|
+
key: TaskStatus.BACKLOG,
|
|
6
|
+
label: "Backlog",
|
|
7
|
+
colorVar: "var(--color-status-backlog)",
|
|
8
|
+
className: "bg-[var(--color-status-backlog)]",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
key: TaskStatus.IN_PROGRESS,
|
|
12
|
+
label: "In Progress",
|
|
13
|
+
colorVar: "var(--color-status-todo)",
|
|
14
|
+
className: "bg-[var(--color-status-todo)]",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: TaskStatus.REVIEW,
|
|
18
|
+
label: "Review",
|
|
19
|
+
colorVar: "var(--color-status-review)",
|
|
20
|
+
className: "bg-[var(--color-status-review)]",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: TaskStatus.VERIFICATION,
|
|
24
|
+
label: "Verification",
|
|
25
|
+
colorVar: "var(--color-status-verification)",
|
|
26
|
+
className: "bg-[var(--color-status-verification)]",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: TaskStatus.DONE,
|
|
30
|
+
label: "Done",
|
|
31
|
+
colorVar: "var(--color-status-done)",
|
|
32
|
+
className: "bg-[var(--color-status-done)]",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: TaskStatus.BLOCKED,
|
|
36
|
+
label: "Blocked",
|
|
37
|
+
colorVar: "var(--color-status-blocked)",
|
|
38
|
+
className: "bg-[var(--color-status-blocked)]",
|
|
39
|
+
},
|
|
40
|
+
];
|