@assistkick/create 1.6.0 → 1.8.0
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/package.json +2 -2
- package/templates/assistkick-product-system/.env.example +1 -0
- package/templates/assistkick-product-system/local.db +0 -0
- package/templates/assistkick-product-system/package.json +4 -2
- package/templates/assistkick-product-system/packages/backend/package.json +2 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
- package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
- package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
- package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
- package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
- package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
- package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
- package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
- package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
- package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
- package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
- package/templates/assistkick-product-system/packages/frontend/index.html +3 -0
- package/templates/assistkick-product-system/packages/frontend/package-lock.json +800 -11
- package/templates/assistkick-product-system/packages/frontend/package.json +11 -1
- package/templates/assistkick-product-system/packages/frontend/src/App.tsx +24 -7
- package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +456 -16
- package/templates/assistkick-product-system/packages/frontend/src/api/client_files.test.ts +172 -0
- package/templates/assistkick-product-system/packages/frontend/src/api/client_video.test.ts +238 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +307 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx +82 -66
- package/templates/assistkick-product-system/packages/frontend/src/components/CompositionPlaceholder.tsx +97 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx +383 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +57 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +313 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeContextMenu.tsx +61 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +73 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +404 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +193 -64
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +71 -73
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphSettings.tsx +8 -8
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphView.tsx +1 -1
- package/templates/assistkick-product-system/packages/frontend/src/components/InviteUserDialog.tsx +15 -11
- package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +226 -291
- package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
- package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
- package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +40 -66
- package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +55 -115
- package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +155 -77
- package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +270 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +37 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +207 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +335 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +634 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +208 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +107 -0
- package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useGraph.ts +6 -21
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +15 -80
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
- package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
- package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
- package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
- package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
- package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +19 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +54 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +93 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +30 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +9 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useGitModalStore.ts +14 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphStore.ts +36 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphUIStore.ts +25 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +90 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useQaSheetStore.ts +27 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +76 -0
- package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +336 -3632
- package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
- package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
- package/templates/assistkick-product-system/packages/frontend/vite.config.ts +7 -1
- package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
- package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
- package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
- package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
- package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
- package/templates/assistkick-product-system/packages/shared/lib/graph.ts +16 -5
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
- package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
- package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
- package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
- package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
- package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
- package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
- package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
- package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
- package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
- package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
- package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
- package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
- package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
- package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
- package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
- package/templates/assistkick-product-system/packages/video/index.ts +4 -0
- package/templates/assistkick-product-system/packages/video/package.json +28 -0
- package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
- package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
- package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
- package/templates/assistkick-product-system/packages/video/style.css +1 -0
- package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
- package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
- package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
- package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
- package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
- package/templates/skills/assistkick-bootstrap/SKILL.md +3 -3
- package/templates/skills/assistkick-code-reviewer/SKILL.md +2 -2
- package/templates/skills/assistkick-debugger/SKILL.md +2 -2
- package/templates/skills/assistkick-developer/SKILL.md +6 -3
- package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
- package/templates/skills/assistkick-interview/SKILL.md +2 -2
- package/templates/skills/product-system/graph.json +1890 -0
- package/templates/skills/product-system/kanban.json +304 -0
- package/templates/skills/product-system/nodes/comp_001.md +56 -0
- package/templates/skills/product-system/nodes/comp_002.md +57 -0
- package/templates/skills/product-system/nodes/data_001.md +51 -0
- package/templates/skills/product-system/nodes/data_002.md +40 -0
- package/templates/skills/product-system/nodes/data_004.md +38 -0
- package/templates/skills/product-system/nodes/dec_001.md +34 -0
- package/templates/skills/product-system/nodes/dec_016.md +32 -0
- package/templates/skills/product-system/nodes/feat_008.md +30 -0
- package/templates/skills/video-composition-agent/SKILL.md +232 -0
- package/templates/skills/video-script-writer/SKILL.md +136 -0
package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx
CHANGED
|
@@ -9,6 +9,9 @@ import { useTheme } from '../hooks/useTheme';
|
|
|
9
9
|
import { apiClient } from '../api/client';
|
|
10
10
|
import { validatePassword } from '../utils/auth_validation';
|
|
11
11
|
|
|
12
|
+
const inputBase = 'w-full h-9 px-2.5 bg-surface-raised border border-edge rounded text-content font-mono text-[13px] outline-none transition-[border-color] duration-150 focus:border-accent placeholder:text-content-muted';
|
|
13
|
+
const inputError = 'border-error focus:border-error';
|
|
14
|
+
|
|
12
15
|
export function ResetPasswordPage() {
|
|
13
16
|
const { theme, toggleTheme } = useTheme();
|
|
14
17
|
const [searchParams] = useSearchParams();
|
|
@@ -55,14 +58,14 @@ export function ResetPasswordPage() {
|
|
|
55
58
|
|
|
56
59
|
if (!token) {
|
|
57
60
|
return (
|
|
58
|
-
<div className="
|
|
59
|
-
<div className="
|
|
60
|
-
<div className="
|
|
61
|
-
<h1 className="
|
|
61
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
62
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
63
|
+
<div className="mb-6">
|
|
64
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Invalid Link</h1>
|
|
62
65
|
</div>
|
|
63
|
-
<div className="
|
|
66
|
+
<div className="text-center py-4 text-content font-mono text-[13px] leading-relaxed">
|
|
64
67
|
<p>This reset link is invalid or missing a token.</p>
|
|
65
|
-
<Link to="/forgot-password" className="
|
|
68
|
+
<Link to="/forgot-password" className="inline-block mt-3 text-accent font-mono text-[13px] no-underline hover:underline">Request a new reset link</Link>
|
|
66
69
|
</div>
|
|
67
70
|
</div>
|
|
68
71
|
</div>
|
|
@@ -70,28 +73,28 @@ export function ResetPasswordPage() {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
return (
|
|
73
|
-
<div className="
|
|
74
|
-
<div className="
|
|
75
|
-
<div className="
|
|
76
|
-
<h1 className="
|
|
77
|
-
<p className="
|
|
76
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
77
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
78
|
+
<div className="mb-6">
|
|
79
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Set New Password</h1>
|
|
80
|
+
<p className="font-mono text-xs text-content-muted">Enter your new password below</p>
|
|
78
81
|
</div>
|
|
79
82
|
|
|
80
83
|
{success ? (
|
|
81
|
-
<div className="
|
|
84
|
+
<div className="text-center py-4 text-content font-mono text-[13px] leading-relaxed">
|
|
82
85
|
<p>Your password has been reset successfully.</p>
|
|
83
|
-
<Link to="/login" className="
|
|
86
|
+
<Link to="/login" className="inline-block mt-3 text-accent font-mono text-[13px] no-underline hover:underline">Sign in with your new password</Link>
|
|
84
87
|
</div>
|
|
85
88
|
) : (
|
|
86
89
|
<>
|
|
87
|
-
<form className="
|
|
88
|
-
{error && <div className="
|
|
90
|
+
<form className="flex flex-col gap-4" onSubmit={handleSubmit} noValidate>
|
|
91
|
+
{error && <div className="font-mono text-xs text-error bg-[rgba(255,107,107,0.08)] border border-[rgba(255,107,107,0.2)] rounded px-3 py-2">{error}</div>}
|
|
89
92
|
|
|
90
|
-
<div className="
|
|
91
|
-
<label className="
|
|
93
|
+
<div className="flex flex-col gap-1">
|
|
94
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="reset-password">New Password</label>
|
|
92
95
|
<input
|
|
93
96
|
id="reset-password"
|
|
94
|
-
className={
|
|
97
|
+
className={`${inputBase}${fieldError ? ` ${inputError}` : ''}`}
|
|
95
98
|
type="password"
|
|
96
99
|
value={password}
|
|
97
100
|
onChange={e => setPassword(e.target.value)}
|
|
@@ -102,11 +105,11 @@ export function ResetPasswordPage() {
|
|
|
102
105
|
/>
|
|
103
106
|
</div>
|
|
104
107
|
|
|
105
|
-
<div className="
|
|
106
|
-
<label className="
|
|
108
|
+
<div className="flex flex-col gap-1">
|
|
109
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="reset-confirm-password">Confirm Password</label>
|
|
107
110
|
<input
|
|
108
111
|
id="reset-confirm-password"
|
|
109
|
-
className={
|
|
112
|
+
className={`${inputBase}${fieldError ? ` ${inputError}` : ''}`}
|
|
110
113
|
type="password"
|
|
111
114
|
value={confirmPassword}
|
|
112
115
|
onChange={e => setConfirmPassword(e.target.value)}
|
|
@@ -114,11 +117,11 @@ export function ResetPasswordPage() {
|
|
|
114
117
|
autoComplete="new-password"
|
|
115
118
|
disabled={submitting}
|
|
116
119
|
/>
|
|
117
|
-
{fieldError && <span className="
|
|
120
|
+
{fieldError && <span className="font-mono text-[11px] text-error">{fieldError}</span>}
|
|
118
121
|
</div>
|
|
119
122
|
|
|
120
123
|
<button
|
|
121
|
-
className="
|
|
124
|
+
className="w-full h-9 mt-1 bg-transparent border border-accent rounded text-accent font-mono text-[13px] cursor-pointer transition-[background,color] duration-150 hover:enabled:bg-accent hover:enabled:text-white disabled:opacity-60 disabled:cursor-not-allowed"
|
|
122
125
|
type="submit"
|
|
123
126
|
disabled={submitting}
|
|
124
127
|
>
|
|
@@ -126,14 +129,14 @@ export function ResetPasswordPage() {
|
|
|
126
129
|
</button>
|
|
127
130
|
</form>
|
|
128
131
|
|
|
129
|
-
<p className="
|
|
132
|
+
<p className="font-mono text-xs text-content-secondary mt-4 text-center">
|
|
130
133
|
<Link to="/login">Back to login</Link>
|
|
131
134
|
</p>
|
|
132
135
|
</>
|
|
133
136
|
)}
|
|
134
137
|
|
|
135
138
|
<button
|
|
136
|
-
className="
|
|
139
|
+
className="block mx-auto mt-4 px-2.5 py-1 bg-transparent border border-edge rounded text-content-muted font-mono text-[11px] cursor-pointer transition-[border-color,color] duration-150 hover:border-content-secondary hover:text-content-secondary"
|
|
137
140
|
onClick={toggleTheme}
|
|
138
141
|
type="button"
|
|
139
142
|
title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CoherenceView } from '../components/CoherenceView';
|
|
3
|
+
import { useGraphStore } from '../stores/useGraphStore';
|
|
4
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
5
|
+
import { useSidePanelStore } from '../stores/useSidePanelStore';
|
|
6
|
+
|
|
7
|
+
export function CoherenceRoute() {
|
|
8
|
+
const graphData = useGraphStore((s) => s.graphData);
|
|
9
|
+
const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
|
|
10
|
+
const openSidePanel = useSidePanelStore((s) => s.open);
|
|
11
|
+
|
|
12
|
+
if (!graphData) return null;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="w-full h-[calc(100vh-44px)] overflow-y-auto p-6">
|
|
16
|
+
<CoherenceView graphData={graphData} onNodeClick={openSidePanel} projectId={selectedProjectId} />
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Outlet } from 'react-router-dom';
|
|
3
|
+
import { Toolbar } from '../components/Toolbar';
|
|
4
|
+
import { SidePanel } from '../components/SidePanel';
|
|
5
|
+
import { QaIssueSheet } from '../components/QaIssueSheet';
|
|
6
|
+
import { GitRepoModal } from '../components/GitRepoModal';
|
|
7
|
+
import { useSidePanelStore } from '../stores/useSidePanelStore';
|
|
8
|
+
import { useQaSheetStore } from '../stores/useQaSheetStore';
|
|
9
|
+
import { useGitModalStore } from '../stores/useGitModalStore';
|
|
10
|
+
import { useGraphUIStore } from '../stores/useGraphUIStore';
|
|
11
|
+
import { useProjects } from '../hooks/useProjects';
|
|
12
|
+
import { useGraph } from '../hooks/useGraph';
|
|
13
|
+
import { ToastProvider } from '../hooks/useToast';
|
|
14
|
+
|
|
15
|
+
export function DashboardLayout() {
|
|
16
|
+
const { selectedProjectId } = useProjects();
|
|
17
|
+
// Keep graph data fetching at layout level so it's available for all views
|
|
18
|
+
useGraph(selectedProjectId);
|
|
19
|
+
|
|
20
|
+
const closeSidePanel = useSidePanelStore((s) => s.close);
|
|
21
|
+
const closeQaSheet = useQaSheetStore((s) => s.close);
|
|
22
|
+
const gitModalProject = useGitModalStore((s) => s.project);
|
|
23
|
+
const setSettingsOpen = useGraphUIStore((s) => s.setSettingsOpen);
|
|
24
|
+
|
|
25
|
+
// Global keyboard handler
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const handler = (e: KeyboardEvent) => {
|
|
28
|
+
if (e.key === 'Escape') {
|
|
29
|
+
closeSidePanel();
|
|
30
|
+
setSettingsOpen(false);
|
|
31
|
+
closeQaSheet();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
document.addEventListener('keydown', handler);
|
|
35
|
+
return () => document.removeEventListener('keydown', handler);
|
|
36
|
+
}, [closeSidePanel, closeQaSheet, setSettingsOpen]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<ToastProvider>
|
|
40
|
+
<div className="flex flex-col h-full overflow-hidden">
|
|
41
|
+
<Toolbar />
|
|
42
|
+
|
|
43
|
+
<div className="flex-1 overflow-hidden relative">
|
|
44
|
+
<Outlet />
|
|
45
|
+
|
|
46
|
+
<SidePanel />
|
|
47
|
+
<QaIssueSheet />
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
{gitModalProject && <GitRepoModal />}
|
|
51
|
+
</div>
|
|
52
|
+
</ToastProvider>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FilesView } from '../components/FilesView';
|
|
3
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
4
|
+
|
|
5
|
+
export function FilesRoute() {
|
|
6
|
+
const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex w-full h-full overflow-hidden">
|
|
10
|
+
<FilesView projectId={selectedProjectId} />
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useRef, useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { GraphView } from '../components/GraphView';
|
|
3
|
+
import type { GraphViewHandle } from '../components/GraphView';
|
|
4
|
+
import { GraphLegend } from '../components/GraphLegend';
|
|
5
|
+
import { GraphSettings } from '../components/GraphSettings';
|
|
6
|
+
import { useGraphStore } from '../stores/useGraphStore';
|
|
7
|
+
import { useGraphUIStore } from '../stores/useGraphUIStore';
|
|
8
|
+
import { useSidePanelStore } from '../stores/useSidePanelStore';
|
|
9
|
+
|
|
10
|
+
export function GraphRoute() {
|
|
11
|
+
const graphData = useGraphStore((s) => s.graphData);
|
|
12
|
+
const graphError = useGraphStore((s) => s.error);
|
|
13
|
+
const openSidePanel = useSidePanelStore((s) => s.open);
|
|
14
|
+
const settingsOpen = useGraphUIStore((s) => s.settingsOpen);
|
|
15
|
+
const setSettingsOpen = useGraphUIStore((s) => s.setSettingsOpen);
|
|
16
|
+
const setOnFit = useGraphUIStore((s) => s.setOnFit);
|
|
17
|
+
const setOnEdgeClick = useGraphUIStore((s) => s.setOnEdgeClick);
|
|
18
|
+
|
|
19
|
+
const graphRef = useRef<GraphViewHandle>(null);
|
|
20
|
+
const [legendVisible] = useState(true);
|
|
21
|
+
|
|
22
|
+
// Register graph callbacks with the UI store
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setOnFit(() => graphRef.current?.fitToView());
|
|
25
|
+
setOnEdgeClick((neighborId: string) => {
|
|
26
|
+
const node = graphRef.current?.getNodeById(neighborId);
|
|
27
|
+
if (node) {
|
|
28
|
+
graphRef.current?.focusNode(neighborId);
|
|
29
|
+
openSidePanel(node);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return () => {
|
|
33
|
+
setOnFit(null);
|
|
34
|
+
setOnEdgeClick(null);
|
|
35
|
+
};
|
|
36
|
+
}, [setOnFit, setOnEdgeClick, openSidePanel]);
|
|
37
|
+
|
|
38
|
+
// Window resize
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const handler = () => graphRef.current?.fitToView();
|
|
41
|
+
window.addEventListener('resize', handler);
|
|
42
|
+
return () => window.removeEventListener('resize', handler);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
// Escape clears graph selection
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const handler = (e: KeyboardEvent) => {
|
|
48
|
+
if (e.key === 'Escape') {
|
|
49
|
+
graphRef.current?.clearSelection();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
document.addEventListener('keydown', handler);
|
|
53
|
+
return () => document.removeEventListener('keydown', handler);
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const handleNodeClick = useCallback(async (node: any) => {
|
|
57
|
+
openSidePanel(node);
|
|
58
|
+
}, [openSidePanel]);
|
|
59
|
+
|
|
60
|
+
const handleTypeToggle = useCallback((newHiddenTypes: Set<string>) => {
|
|
61
|
+
graphRef.current?.setHiddenTypes(newHiddenTypes);
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const handleSearchChange = useCallback((query: string) => {
|
|
65
|
+
graphRef.current?.highlightBySearch(query);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
if (graphError) {
|
|
69
|
+
return (
|
|
70
|
+
<div className="w-full h-[calc(100vh-44px)] overflow-hidden">
|
|
71
|
+
<div className="flex items-center justify-center h-full text-error text-sm">Failed to load graph data. Is the server running?</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!graphData) return null;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
<GraphView ref={graphRef} graphData={graphData} onNodeClick={handleNodeClick} visible={true} />
|
|
81
|
+
<GraphLegend
|
|
82
|
+
visible={legendVisible}
|
|
83
|
+
onTypeToggle={handleTypeToggle}
|
|
84
|
+
onSearchChange={handleSearchChange}
|
|
85
|
+
/>
|
|
86
|
+
<GraphSettings
|
|
87
|
+
isOpen={settingsOpen}
|
|
88
|
+
onClose={() => setSettingsOpen(false)}
|
|
89
|
+
graphRef={graphRef}
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { KanbanView } from '../components/KanbanView';
|
|
3
|
+
import { useGraphStore } from '../stores/useGraphStore';
|
|
4
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
5
|
+
import { useSidePanelStore } from '../stores/useSidePanelStore';
|
|
6
|
+
import { useQaSheetStore } from '../stores/useQaSheetStore';
|
|
7
|
+
|
|
8
|
+
export function KanbanRoute() {
|
|
9
|
+
const graphData = useGraphStore((s) => s.graphData);
|
|
10
|
+
const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
|
|
11
|
+
const openSidePanel = useSidePanelStore((s) => s.open);
|
|
12
|
+
const openQaSheet = useQaSheetStore((s) => s.open);
|
|
13
|
+
|
|
14
|
+
const handleIssuesClick = useCallback((featureId: string, featureName: string, column: string, notes: any[], reviews: any[]) => {
|
|
15
|
+
openQaSheet(featureId, featureName, column, notes, reviews);
|
|
16
|
+
}, [openQaSheet]);
|
|
17
|
+
|
|
18
|
+
if (!graphData) return null;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex w-full h-full overflow-hidden p-3">
|
|
22
|
+
<KanbanView
|
|
23
|
+
graphData={graphData}
|
|
24
|
+
projectId={selectedProjectId}
|
|
25
|
+
onCardClick={openSidePanel}
|
|
26
|
+
onIssuesClick={handleIssuesClick}
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TerminalView } from '../components/TerminalView';
|
|
3
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
4
|
+
|
|
5
|
+
export function TerminalRoute() {
|
|
6
|
+
const projects = useProjectStore((s) => s.projects);
|
|
7
|
+
|
|
8
|
+
return <TerminalView visible={true} projects={projects} />;
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { VideographyView } from '../components/VideographyView';
|
|
3
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
4
|
+
|
|
5
|
+
export function VideographyRoute() {
|
|
6
|
+
const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex w-full h-full overflow-hidden">
|
|
10
|
+
<VideographyView projectId={selectedProjectId} />
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import type { Project } from './useProjectStore';
|
|
3
|
+
|
|
4
|
+
interface GitModalState {
|
|
5
|
+
project: Project | null;
|
|
6
|
+
open: (project: Project) => void;
|
|
7
|
+
close: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const useGitModalStore = create<GitModalState>((set) => ({
|
|
11
|
+
project: null,
|
|
12
|
+
open: (project) => set({ project }),
|
|
13
|
+
close: () => set({ project: null }),
|
|
14
|
+
}));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { apiClient } from '../api/client';
|
|
3
|
+
import { useProjectStore } from './useProjectStore';
|
|
4
|
+
|
|
5
|
+
interface GraphState {
|
|
6
|
+
graphData: any | null;
|
|
7
|
+
error: string | null;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
|
|
10
|
+
fetchGraph: (projectId?: string | null) => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const useGraphStore = create<GraphState>((set) => ({
|
|
14
|
+
graphData: null,
|
|
15
|
+
error: null,
|
|
16
|
+
loading: true,
|
|
17
|
+
|
|
18
|
+
fetchGraph: async (projectId?: string | null) => {
|
|
19
|
+
try {
|
|
20
|
+
set({ loading: true });
|
|
21
|
+
const data = await apiClient.fetchGraph(projectId ?? undefined);
|
|
22
|
+
set({ graphData: data, error: null, loading: false });
|
|
23
|
+
} catch (err: any) {
|
|
24
|
+
set({ error: err.message, loading: false });
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// Auto-fetch when selectedProjectId changes
|
|
30
|
+
let prevProjectId = useProjectStore.getState().selectedProjectId;
|
|
31
|
+
useProjectStore.subscribe((state) => {
|
|
32
|
+
if (state.selectedProjectId !== prevProjectId) {
|
|
33
|
+
prevProjectId = state.selectedProjectId;
|
|
34
|
+
useGraphStore.getState().fetchGraph(state.selectedProjectId);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
interface GraphUIState {
|
|
4
|
+
settingsOpen: boolean;
|
|
5
|
+
legendVisible: boolean;
|
|
6
|
+
|
|
7
|
+
onFit: (() => void) | null;
|
|
8
|
+
onEdgeClick: ((neighborId: string) => void) | null;
|
|
9
|
+
onSettingsToggle: () => void;
|
|
10
|
+
setOnFit: (fn: (() => void) | null) => void;
|
|
11
|
+
setOnEdgeClick: (fn: ((neighborId: string) => void) | null) => void;
|
|
12
|
+
setSettingsOpen: (open: boolean) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const useGraphUIStore = create<GraphUIState>((set) => ({
|
|
16
|
+
settingsOpen: false,
|
|
17
|
+
legendVisible: true,
|
|
18
|
+
onFit: null,
|
|
19
|
+
onEdgeClick: null,
|
|
20
|
+
|
|
21
|
+
onSettingsToggle: () => set((s) => ({ settingsOpen: !s.settingsOpen })),
|
|
22
|
+
setOnFit: (fn) => set({ onFit: fn }),
|
|
23
|
+
setOnEdgeClick: (fn) => set({ onEdgeClick: fn }),
|
|
24
|
+
setSettingsOpen: (open) => set({ settingsOpen: open }),
|
|
25
|
+
}));
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { apiClient } from '../api/client';
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY = 'selectedProjectId';
|
|
5
|
+
|
|
6
|
+
export interface Project {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
isDefault: number;
|
|
11
|
+
archivedAt: string | null;
|
|
12
|
+
repoUrl: string | null;
|
|
13
|
+
githubInstallationId: string | null;
|
|
14
|
+
githubRepoFullName: string | null;
|
|
15
|
+
baseBranch: string | null;
|
|
16
|
+
gitAuthMethod: string | null;
|
|
17
|
+
sshPublicKey: string | null;
|
|
18
|
+
createdAt: string;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ProjectState {
|
|
23
|
+
projects: Project[];
|
|
24
|
+
selectedProjectId: string | null;
|
|
25
|
+
loading: boolean;
|
|
26
|
+
|
|
27
|
+
init: () => Promise<void>;
|
|
28
|
+
selectProject: (id: string) => void;
|
|
29
|
+
createProject: (name: string, type?: string) => Promise<Project>;
|
|
30
|
+
renameProject: (id: string, name: string) => Promise<void>;
|
|
31
|
+
archiveProject: (id: string) => Promise<void>;
|
|
32
|
+
refetchProjects: () => Promise<Project[]>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const useProjectStore = create<ProjectState>((set, get) => ({
|
|
36
|
+
projects: [],
|
|
37
|
+
selectedProjectId: localStorage.getItem(STORAGE_KEY),
|
|
38
|
+
loading: true,
|
|
39
|
+
|
|
40
|
+
init: async () => {
|
|
41
|
+
const fetched = await get().refetchProjects();
|
|
42
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
43
|
+
if (stored && fetched.some((p) => p.id === stored)) {
|
|
44
|
+
set({ selectedProjectId: stored });
|
|
45
|
+
} else if (fetched.length > 0) {
|
|
46
|
+
const defaultProject = fetched.find((p) => p.isDefault === 1);
|
|
47
|
+
const fallback = defaultProject ? defaultProject.id : fetched[0].id;
|
|
48
|
+
set({ selectedProjectId: fallback });
|
|
49
|
+
localStorage.setItem(STORAGE_KEY, fallback);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
selectProject: (id) => {
|
|
54
|
+
set({ selectedProjectId: id });
|
|
55
|
+
localStorage.setItem(STORAGE_KEY, id);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
refetchProjects: async () => {
|
|
59
|
+
try {
|
|
60
|
+
const data = await apiClient.fetchProjects();
|
|
61
|
+
set({ projects: data.projects, loading: false });
|
|
62
|
+
return data.projects as Project[];
|
|
63
|
+
} catch {
|
|
64
|
+
set({ projects: [], loading: false });
|
|
65
|
+
return [] as Project[];
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
createProject: async (name, type) => {
|
|
70
|
+
const data = await apiClient.createProject(name, type);
|
|
71
|
+
await get().refetchProjects();
|
|
72
|
+
get().selectProject(data.project.id);
|
|
73
|
+
return data.project;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
renameProject: async (id, name) => {
|
|
77
|
+
await apiClient.renameProject(id, name);
|
|
78
|
+
await get().refetchProjects();
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
archiveProject: async (id) => {
|
|
82
|
+
await apiClient.archiveProject(id);
|
|
83
|
+
const updated = await get().refetchProjects();
|
|
84
|
+
if (get().selectedProjectId === id && updated.length > 0) {
|
|
85
|
+
const defaultProject = updated.find((p) => p.isDefault === 1);
|
|
86
|
+
const fallback = defaultProject ? defaultProject.id : updated[0].id;
|
|
87
|
+
get().selectProject(fallback);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
interface QaSheetState {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
featureId: string | null;
|
|
6
|
+
featureName: string;
|
|
7
|
+
column: string;
|
|
8
|
+
notes: any[];
|
|
9
|
+
reviews: any[];
|
|
10
|
+
|
|
11
|
+
open: (featureId: string, featureName: string, column: string, notes: any[], reviews: any[]) => void;
|
|
12
|
+
close: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const useQaSheetStore = create<QaSheetState>((set) => ({
|
|
16
|
+
isOpen: false,
|
|
17
|
+
featureId: null,
|
|
18
|
+
featureName: '',
|
|
19
|
+
column: '',
|
|
20
|
+
notes: [],
|
|
21
|
+
reviews: [],
|
|
22
|
+
|
|
23
|
+
open: (featureId, featureName, column, notes, reviews) =>
|
|
24
|
+
set({ isOpen: true, featureId, featureName, column, notes, reviews }),
|
|
25
|
+
|
|
26
|
+
close: () => set({ isOpen: false }),
|
|
27
|
+
}));
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { apiClient } from '../api/client';
|
|
3
|
+
|
|
4
|
+
interface WorkSummary {
|
|
5
|
+
cycle: number;
|
|
6
|
+
filesCreated: string[];
|
|
7
|
+
filesUpdated: string[];
|
|
8
|
+
filesDeleted: string[];
|
|
9
|
+
approach: string;
|
|
10
|
+
decisions: string[];
|
|
11
|
+
timestamp: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Tasks {
|
|
15
|
+
total: number;
|
|
16
|
+
completed: number;
|
|
17
|
+
items: any[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SidePanelState {
|
|
21
|
+
isOpen: boolean;
|
|
22
|
+
node: any | null;
|
|
23
|
+
content: string;
|
|
24
|
+
workSummaries: WorkSummary[];
|
|
25
|
+
expandedSummaries: boolean;
|
|
26
|
+
tasks: Tasks | null;
|
|
27
|
+
expandedTasks: boolean;
|
|
28
|
+
|
|
29
|
+
open: (node: any) => Promise<void>;
|
|
30
|
+
close: () => void;
|
|
31
|
+
toggleSummaries: () => void;
|
|
32
|
+
toggleTasks: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const useSidePanelStore = create<SidePanelState>((set) => ({
|
|
36
|
+
isOpen: false,
|
|
37
|
+
node: null,
|
|
38
|
+
content: '',
|
|
39
|
+
workSummaries: [],
|
|
40
|
+
expandedSummaries: false,
|
|
41
|
+
tasks: null,
|
|
42
|
+
expandedTasks: false,
|
|
43
|
+
|
|
44
|
+
open: async (node: any) => {
|
|
45
|
+
set({
|
|
46
|
+
node,
|
|
47
|
+
workSummaries: [],
|
|
48
|
+
expandedSummaries: false,
|
|
49
|
+
tasks: null,
|
|
50
|
+
expandedTasks: false,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const detail = await apiClient.fetchNode(node.id);
|
|
55
|
+
set({ content: detail.content, isOpen: true });
|
|
56
|
+
|
|
57
|
+
if (node.type === 'feature' || node.id?.startsWith('feat_')) {
|
|
58
|
+
try {
|
|
59
|
+
const wStatus = await apiClient.getWorkflowStatus(node.id);
|
|
60
|
+
set({
|
|
61
|
+
workSummaries: wStatus?.workSummaries?.length > 0 ? wStatus.workSummaries : [],
|
|
62
|
+
tasks: wStatus?.tasks ?? null,
|
|
63
|
+
});
|
|
64
|
+
} catch {
|
|
65
|
+
// Workflow status may not exist for all features
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error('Failed to fetch node:', err);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
close: () => set({ isOpen: false }),
|
|
74
|
+
toggleSummaries: () => set((s) => ({ expandedSummaries: !s.expandedSummaries })),
|
|
75
|
+
toggleTasks: () => set((s) => ({ expandedTasks: !s.expandedTasks })),
|
|
76
|
+
}));
|