@assistkick/create 1.7.0 → 1.9.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/dist/bin/create.js +0 -0
- package/package.json +9 -7
- 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 +61 -6
- 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 +158 -0
- package/templates/assistkick-product-system/packages/backend/src/server.ts +60 -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 +43 -77
- 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 +245 -0
- package/templates/assistkick-product-system/packages/frontend/package-lock.json +3455 -0
- package/templates/assistkick-product-system/packages/frontend/package.json +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/App.tsx +8 -0
- package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +458 -18
- 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 +20 -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 +187 -56
- 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/IterationCommentModal.tsx +80 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +263 -167
- 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 +32 -49
- package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
- package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
- 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 +103 -87
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
- 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 +81 -37
- 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/GenerateTTSNode.tsx +52 -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/RebuildBundleNode.tsx +20 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RenderVideoNode.tsx +72 -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 +341 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +643 -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 +246 -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 +136 -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/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 +1 -1
- package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
- package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
- 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/useProjectStore.ts +6 -3
- package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
- package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
- 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 +5 -0
- 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/0014_nifty_punisher.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/0014_snapshot.json +1545 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +77 -0
- package/templates/assistkick-product-system/packages/shared/db/schema.ts +114 -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 +5 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1999 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1437 -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 +181 -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-developer/SKILL.md +3 -0
- package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
- 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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
export const NODE_COLORS: Record<string, string> = {
|
|
6
6
|
feature: '#4dabf7',
|
|
7
|
+
epic: '#e599f7',
|
|
7
8
|
component: '#69db7c',
|
|
8
9
|
data_entity: '#ffd43b',
|
|
9
10
|
decision: '#ff8787',
|
|
@@ -47,6 +48,7 @@ export const EDGE_COLORS: Record<string, string> = {
|
|
|
47
48
|
|
|
48
49
|
export const TYPE_LABELS: Record<string, string> = {
|
|
49
50
|
feature: 'FEAT',
|
|
51
|
+
epic: 'EPIC',
|
|
50
52
|
component: 'COMP',
|
|
51
53
|
data_entity: 'DATA',
|
|
52
54
|
decision: 'DEC',
|
|
@@ -62,6 +64,7 @@ export const TYPE_LABELS: Record<string, string> = {
|
|
|
62
64
|
|
|
63
65
|
export const LEGEND_LABELS: Record<string, string> = {
|
|
64
66
|
feature: 'Feature',
|
|
67
|
+
epic: 'Epic',
|
|
65
68
|
component: 'Component',
|
|
66
69
|
data_entity: 'Data Entity',
|
|
67
70
|
decision: 'Decision',
|
|
@@ -77,6 +80,7 @@ export const LEGEND_LABELS: Record<string, string> = {
|
|
|
77
80
|
|
|
78
81
|
export const NODE_SHAPES: Record<string, string> = {
|
|
79
82
|
feature: 'circle',
|
|
83
|
+
epic: 'shield',
|
|
80
84
|
component: 'rounded-rect',
|
|
81
85
|
data_entity: 'diamond',
|
|
82
86
|
decision: 'hexagon',
|
|
@@ -157,13 +161,18 @@ export const nodeShapePath = (shape: string, r: number): string => {
|
|
|
157
161
|
}
|
|
158
162
|
return `M${pts.join(' L')} Z`;
|
|
159
163
|
}
|
|
164
|
+
case 'shield': {
|
|
165
|
+
const w = r * 0.9;
|
|
166
|
+
const h = r * 1.2;
|
|
167
|
+
return `M${-w},${-h * 0.6} L${-w},${h * 0.2} Q${-w},${h * 0.7} 0,${h} Q${w},${h * 0.7} ${w},${h * 0.2} L${w},${-h * 0.6} Q${w * 0.3},${-h} ${-w * 0.3},${-h} Q${-w},${-h * 0.6} ${-w},${-h * 0.6} Z`;
|
|
168
|
+
}
|
|
160
169
|
default:
|
|
161
170
|
return `M${-r},0 A${r},${r} 0 1,1 ${r},0 A${r},${r} 0 1,1 ${-r},0 Z`;
|
|
162
171
|
}
|
|
163
172
|
};
|
|
164
173
|
|
|
165
174
|
export const ALL_NODE_TYPES = [
|
|
166
|
-
'feature', 'component', 'data_entity', 'decision', 'tech_choice',
|
|
175
|
+
'feature', 'epic', 'component', 'data_entity', 'decision', 'tech_choice',
|
|
167
176
|
'non_functional_requirement', 'design_token', 'design_pattern',
|
|
168
177
|
'user_role', 'flow', 'assumption', 'open_question',
|
|
169
178
|
];
|
|
@@ -177,16 +186,9 @@ export const COLUMNS = [
|
|
|
177
186
|
{ id: 'done', label: 'Done' },
|
|
178
187
|
];
|
|
179
188
|
|
|
180
|
-
export const
|
|
181
|
-
|
|
182
|
-
debugging: 'Debugging',
|
|
183
|
-
developing: 'Developing',
|
|
184
|
-
reviewing: 'Reviewing',
|
|
185
|
-
fixing_review: 'Fixing review',
|
|
186
|
-
fixing_merge: 'Fixing merge',
|
|
187
|
-
merging: 'Merging',
|
|
189
|
+
export const WORKFLOW_STATUS_LABELS: Record<string, string> = {
|
|
190
|
+
running: 'Running',
|
|
188
191
|
completed: 'Completed',
|
|
189
192
|
failed: 'Failed',
|
|
190
|
-
|
|
191
|
-
interrupted: 'Interrupted',
|
|
193
|
+
idle: 'Idle',
|
|
192
194
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useRef, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { AutoSaveService } from '../utils/auto_save_service';
|
|
3
|
+
import { apiClient } from '../api/client';
|
|
4
|
+
import { useToast } from './useToast';
|
|
5
|
+
import { getFileName } from '../utils/file_utils';
|
|
6
|
+
|
|
7
|
+
interface UseAutoSaveOptions {
|
|
8
|
+
projectId: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useAutoSave = ({ projectId }: UseAutoSaveOptions) => {
|
|
12
|
+
const { showToast } = useToast();
|
|
13
|
+
const [dirtyPaths, setDirtyPaths] = useState<Set<string>>(new Set());
|
|
14
|
+
const serviceRef = useRef<AutoSaveService | null>(null);
|
|
15
|
+
const projectIdRef = useRef(projectId);
|
|
16
|
+
projectIdRef.current = projectId;
|
|
17
|
+
|
|
18
|
+
// Keep showToast ref stable to avoid recreating service
|
|
19
|
+
const showToastRef = useRef(showToast);
|
|
20
|
+
showToastRef.current = showToast;
|
|
21
|
+
|
|
22
|
+
const getService = useCallback(() => {
|
|
23
|
+
if (!serviceRef.current) {
|
|
24
|
+
serviceRef.current = new AutoSaveService({
|
|
25
|
+
save: async (path: string, content: string) => {
|
|
26
|
+
const pid = projectIdRef.current;
|
|
27
|
+
if (!pid) throw new Error('No project selected');
|
|
28
|
+
await apiClient.saveFileContent(pid, path, content);
|
|
29
|
+
},
|
|
30
|
+
onDirtyChange: (path: string, dirty: boolean) => {
|
|
31
|
+
setDirtyPaths((prev) => {
|
|
32
|
+
const next = new Set(prev);
|
|
33
|
+
if (dirty) {
|
|
34
|
+
next.add(path);
|
|
35
|
+
} else {
|
|
36
|
+
next.delete(path);
|
|
37
|
+
}
|
|
38
|
+
return next;
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
onSaveError: (path: string, error: string) => {
|
|
42
|
+
const fileName = getFileName(path);
|
|
43
|
+
showToastRef.current(`Failed to save ${fileName}: ${error}`, 'error');
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return serviceRef.current;
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
// Dispose on unmount
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
return () => {
|
|
53
|
+
serviceRef.current?.dispose();
|
|
54
|
+
serviceRef.current = null;
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const handleChange = useCallback((path: string, content: string) => {
|
|
59
|
+
getService().handleChange(path, content);
|
|
60
|
+
}, [getService]);
|
|
61
|
+
|
|
62
|
+
const saveNow = useCallback((path: string) => {
|
|
63
|
+
getService().saveNow(path);
|
|
64
|
+
}, [getService]);
|
|
65
|
+
|
|
66
|
+
const saveAndDiscard = useCallback((path: string) => {
|
|
67
|
+
getService().saveAndDiscard(path);
|
|
68
|
+
}, [getService]);
|
|
69
|
+
|
|
70
|
+
const isDirty = useCallback((path: string): boolean => {
|
|
71
|
+
return dirtyPaths.has(path);
|
|
72
|
+
}, [dirtyPaths]);
|
|
73
|
+
|
|
74
|
+
return { handleChange, saveNow, saveAndDiscard, isDirty, dirtyPaths };
|
|
75
|
+
};
|
|
@@ -41,11 +41,24 @@ export function ToastProvider({ children }: { children: React.ReactNode }) {
|
|
|
41
41
|
<ToastContext.Provider value={{ showToast }}>
|
|
42
42
|
{children}
|
|
43
43
|
{createPortal(
|
|
44
|
-
<div className="
|
|
44
|
+
<div className="fixed bottom-5 right-5 z-[10000] flex flex-col-reverse gap-2 pointer-events-none">
|
|
45
45
|
{toasts.map(toast => (
|
|
46
|
-
<div
|
|
46
|
+
<div
|
|
47
|
+
key={toast.id}
|
|
48
|
+
className={[
|
|
49
|
+
'relative max-w-[480px] pointer-events-auto bg-surface-alt border border-edge rounded-lg pr-9 pl-4 py-3 shadow-[0_4px_16px_rgba(0,0,0,0.3)] font-mono text-xs text-content animate-[toast-slide-in_0.2s_ease-out]',
|
|
50
|
+
toast.type === 'error' && 'border-error border-l-4 text-error bg-gradient-to-br from-surface-alt to-[rgba(255,107,107,0.08)]',
|
|
51
|
+
toast.type === 'success' && 'border-[#4caf50] border-l-4 text-[#4caf50] bg-gradient-to-br from-surface-alt to-[rgba(76,175,80,0.08)]',
|
|
52
|
+
toast.type === 'info' && 'border-accent border-l-4',
|
|
53
|
+
].filter(Boolean).join(' ')}
|
|
54
|
+
>
|
|
47
55
|
{toast.message}
|
|
48
|
-
<button
|
|
56
|
+
<button
|
|
57
|
+
className="absolute top-2 right-2 bg-transparent border-none text-content-secondary text-base cursor-pointer px-1 leading-none hover:text-content"
|
|
58
|
+
onClick={() => dismiss(toast.id)}
|
|
59
|
+
>
|
|
60
|
+
×
|
|
61
|
+
</button>
|
|
49
62
|
</div>
|
|
50
63
|
))}
|
|
51
64
|
</div>,
|
package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx
CHANGED
|
@@ -13,6 +13,9 @@ interface AcceptInvitationPageProps {
|
|
|
13
13
|
onSuccess: () => void;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
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';
|
|
17
|
+
const inputError = 'border-error focus:border-error';
|
|
18
|
+
|
|
16
19
|
export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
17
20
|
const { theme, toggleTheme } = useTheme();
|
|
18
21
|
const [searchParams] = useSearchParams();
|
|
@@ -73,10 +76,10 @@ export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
|
73
76
|
|
|
74
77
|
if (validating) {
|
|
75
78
|
return (
|
|
76
|
-
<div className="
|
|
77
|
-
<div className="
|
|
78
|
-
<div className="
|
|
79
|
-
<h1 className="
|
|
79
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
80
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
81
|
+
<div className="mb-6">
|
|
82
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Validating Invitation...</h1>
|
|
80
83
|
</div>
|
|
81
84
|
</div>
|
|
82
85
|
</div>
|
|
@@ -85,14 +88,14 @@ export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
|
85
88
|
|
|
86
89
|
if (!token || !email || !valid) {
|
|
87
90
|
return (
|
|
88
|
-
<div className="
|
|
89
|
-
<div className="
|
|
90
|
-
<div className="
|
|
91
|
-
<h1 className="
|
|
91
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
92
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
93
|
+
<div className="mb-6">
|
|
94
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Invalid Invitation</h1>
|
|
92
95
|
</div>
|
|
93
|
-
<div className="
|
|
96
|
+
<div className="text-center py-4 text-content font-mono text-[13px] leading-relaxed">
|
|
94
97
|
<p>This invitation link is invalid or has expired.</p>
|
|
95
|
-
<Link to="/login" className="
|
|
98
|
+
<Link to="/login" className="inline-block mt-3 text-accent font-mono text-[13px] no-underline hover:underline">Go to login</Link>
|
|
96
99
|
</div>
|
|
97
100
|
</div>
|
|
98
101
|
</div>
|
|
@@ -100,21 +103,21 @@ export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
return (
|
|
103
|
-
<div className="
|
|
104
|
-
<div className="
|
|
105
|
-
<div className="
|
|
106
|
-
<h1 className="
|
|
107
|
-
<p className="
|
|
106
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
107
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
108
|
+
<div className="mb-6">
|
|
109
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Accept Invitation</h1>
|
|
110
|
+
<p className="font-mono text-xs text-content-muted">Set your password for {email}</p>
|
|
108
111
|
</div>
|
|
109
112
|
|
|
110
|
-
<form className="
|
|
111
|
-
{error && <div className="
|
|
113
|
+
<form className="flex flex-col gap-4" onSubmit={handleSubmit} noValidate>
|
|
114
|
+
{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>}
|
|
112
115
|
|
|
113
|
-
<div className="
|
|
114
|
-
<label className="
|
|
116
|
+
<div className="flex flex-col gap-1">
|
|
117
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="invite-password">Password</label>
|
|
115
118
|
<input
|
|
116
119
|
id="invite-password"
|
|
117
|
-
className={
|
|
120
|
+
className={`${inputBase}${fieldError ? ` ${inputError}` : ''}`}
|
|
118
121
|
type="password"
|
|
119
122
|
value={password}
|
|
120
123
|
onChange={e => setPassword(e.target.value)}
|
|
@@ -125,11 +128,11 @@ export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
|
125
128
|
/>
|
|
126
129
|
</div>
|
|
127
130
|
|
|
128
|
-
<div className="
|
|
129
|
-
<label className="
|
|
131
|
+
<div className="flex flex-col gap-1">
|
|
132
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="invite-confirm-password">Confirm Password</label>
|
|
130
133
|
<input
|
|
131
134
|
id="invite-confirm-password"
|
|
132
|
-
className={
|
|
135
|
+
className={`${inputBase}${fieldError ? ` ${inputError}` : ''}`}
|
|
133
136
|
type="password"
|
|
134
137
|
value={confirmPassword}
|
|
135
138
|
onChange={e => setConfirmPassword(e.target.value)}
|
|
@@ -137,11 +140,11 @@ export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
|
137
140
|
autoComplete="new-password"
|
|
138
141
|
disabled={submitting}
|
|
139
142
|
/>
|
|
140
|
-
{fieldError && <span className="
|
|
143
|
+
{fieldError && <span className="font-mono text-[11px] text-error">{fieldError}</span>}
|
|
141
144
|
</div>
|
|
142
145
|
|
|
143
146
|
<button
|
|
144
|
-
className="
|
|
147
|
+
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"
|
|
145
148
|
type="submit"
|
|
146
149
|
disabled={submitting}
|
|
147
150
|
>
|
|
@@ -149,12 +152,12 @@ export function AcceptInvitationPage({ onSuccess }: AcceptInvitationPageProps) {
|
|
|
149
152
|
</button>
|
|
150
153
|
</form>
|
|
151
154
|
|
|
152
|
-
<p className="
|
|
155
|
+
<p className="font-mono text-xs text-content-secondary mt-4 text-center">
|
|
153
156
|
<Link to="/login">Already have an account? Sign in</Link>
|
|
154
157
|
</p>
|
|
155
158
|
|
|
156
159
|
<button
|
|
157
|
-
className="
|
|
160
|
+
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"
|
|
158
161
|
onClick={toggleTheme}
|
|
159
162
|
type="button"
|
|
160
163
|
title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
|
package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx
CHANGED
|
@@ -9,6 +9,9 @@ import { useTheme } from '../hooks/useTheme';
|
|
|
9
9
|
import { apiClient } from '../api/client';
|
|
10
10
|
import { validateEmail } 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 ForgotPasswordPage() {
|
|
13
16
|
const { theme, toggleTheme } = useTheme();
|
|
14
17
|
const [email, setEmail] = useState('');
|
|
@@ -38,29 +41,29 @@ export function ForgotPasswordPage() {
|
|
|
38
41
|
}, [email]);
|
|
39
42
|
|
|
40
43
|
return (
|
|
41
|
-
<div className="
|
|
42
|
-
<div className="
|
|
43
|
-
<div className="
|
|
44
|
-
<h1 className="
|
|
45
|
-
<p className="
|
|
44
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
45
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
46
|
+
<div className="mb-6">
|
|
47
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Reset Password</h1>
|
|
48
|
+
<p className="font-mono text-xs text-content-muted">Enter your email to receive a reset link</p>
|
|
46
49
|
</div>
|
|
47
50
|
|
|
48
51
|
{sent ? (
|
|
49
|
-
<div className="
|
|
52
|
+
<div className="text-center py-4 text-content font-mono text-[13px] leading-relaxed">
|
|
50
53
|
<p>If an account with that email exists, a reset link has been sent.</p>
|
|
51
54
|
<p>Check your inbox and follow the link to reset your password.</p>
|
|
52
|
-
<Link to="/login" className="
|
|
55
|
+
<Link to="/login" className="inline-block mt-3 text-accent font-mono text-[13px] no-underline hover:underline">Back to login</Link>
|
|
53
56
|
</div>
|
|
54
57
|
) : (
|
|
55
58
|
<>
|
|
56
|
-
<form className="
|
|
57
|
-
{error && <div className="
|
|
59
|
+
<form className="flex flex-col gap-4" onSubmit={handleSubmit} noValidate>
|
|
60
|
+
{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>}
|
|
58
61
|
|
|
59
|
-
<div className="
|
|
60
|
-
<label className="
|
|
62
|
+
<div className="flex flex-col gap-1">
|
|
63
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="forgot-email">Email</label>
|
|
61
64
|
<input
|
|
62
65
|
id="forgot-email"
|
|
63
|
-
className={
|
|
66
|
+
className={`${inputBase}${error ? ` ${inputError}` : ''}`}
|
|
64
67
|
type="email"
|
|
65
68
|
value={email}
|
|
66
69
|
onChange={e => setEmail(e.target.value)}
|
|
@@ -72,7 +75,7 @@ export function ForgotPasswordPage() {
|
|
|
72
75
|
</div>
|
|
73
76
|
|
|
74
77
|
<button
|
|
75
|
-
className="
|
|
78
|
+
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"
|
|
76
79
|
type="submit"
|
|
77
80
|
disabled={submitting}
|
|
78
81
|
>
|
|
@@ -80,14 +83,14 @@ export function ForgotPasswordPage() {
|
|
|
80
83
|
</button>
|
|
81
84
|
</form>
|
|
82
85
|
|
|
83
|
-
<p className="
|
|
86
|
+
<p className="font-mono text-xs text-content-secondary mt-4 text-center">
|
|
84
87
|
Remember your password? <Link to="/login">Sign in</Link>
|
|
85
88
|
</p>
|
|
86
89
|
</>
|
|
87
90
|
)}
|
|
88
91
|
|
|
89
92
|
<button
|
|
90
|
-
className="
|
|
93
|
+
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"
|
|
91
94
|
onClick={toggleTheme}
|
|
92
95
|
type="button"
|
|
93
96
|
title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
|
|
@@ -21,6 +21,9 @@ interface FormErrors {
|
|
|
21
21
|
general?: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
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';
|
|
25
|
+
const inputError = 'border-error focus:border-error';
|
|
26
|
+
|
|
24
27
|
export function RegisterPage({ onSuccess }: RegisterPageProps) {
|
|
25
28
|
const { theme, toggleTheme } = useTheme();
|
|
26
29
|
const [email, setEmail] = useState('');
|
|
@@ -67,23 +70,23 @@ export function RegisterPage({ onSuccess }: RegisterPageProps) {
|
|
|
67
70
|
}, [errors.password]);
|
|
68
71
|
|
|
69
72
|
return (
|
|
70
|
-
<div className="
|
|
71
|
-
<div className="
|
|
72
|
-
<div className="
|
|
73
|
-
<h1 className="
|
|
74
|
-
<p className="
|
|
73
|
+
<div className="flex items-center justify-center min-h-screen bg-surface p-4">
|
|
74
|
+
<div className="w-full max-w-[380px] bg-surface-alt border border-edge rounded-md px-7 pt-8 pb-6">
|
|
75
|
+
<div className="mb-6">
|
|
76
|
+
<h1 className="font-mono text-base font-semibold text-content mb-1">Create Admin Account</h1>
|
|
77
|
+
<p className="font-mono text-xs text-content-muted">Set up the first user for this system</p>
|
|
75
78
|
</div>
|
|
76
79
|
|
|
77
|
-
<form className="
|
|
80
|
+
<form className="flex flex-col gap-4" onSubmit={handleSubmit} noValidate>
|
|
78
81
|
{errors.general && (
|
|
79
|
-
<div className="
|
|
82
|
+
<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">{errors.general}</div>
|
|
80
83
|
)}
|
|
81
84
|
|
|
82
|
-
<div className="
|
|
83
|
-
<label className="
|
|
85
|
+
<div className="flex flex-col gap-1">
|
|
86
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="register-email">Email</label>
|
|
84
87
|
<input
|
|
85
88
|
id="register-email"
|
|
86
|
-
className={
|
|
89
|
+
className={`${inputBase}${errors.email ? ` ${inputError}` : ''}`}
|
|
87
90
|
type="email"
|
|
88
91
|
value={email}
|
|
89
92
|
onChange={handleEmailChange}
|
|
@@ -92,14 +95,14 @@ export function RegisterPage({ onSuccess }: RegisterPageProps) {
|
|
|
92
95
|
autoFocus
|
|
93
96
|
disabled={submitting}
|
|
94
97
|
/>
|
|
95
|
-
{errors.email && <span className="
|
|
98
|
+
{errors.email && <span className="font-mono text-[11px] text-error">{errors.email}</span>}
|
|
96
99
|
</div>
|
|
97
100
|
|
|
98
|
-
<div className="
|
|
99
|
-
<label className="
|
|
101
|
+
<div className="flex flex-col gap-1">
|
|
102
|
+
<label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="register-password">Password</label>
|
|
100
103
|
<input
|
|
101
104
|
id="register-password"
|
|
102
|
-
className={
|
|
105
|
+
className={`${inputBase}${errors.password ? ` ${inputError}` : ''}`}
|
|
103
106
|
type="password"
|
|
104
107
|
value={password}
|
|
105
108
|
onChange={handlePasswordChange}
|
|
@@ -107,11 +110,11 @@ export function RegisterPage({ onSuccess }: RegisterPageProps) {
|
|
|
107
110
|
autoComplete="new-password"
|
|
108
111
|
disabled={submitting}
|
|
109
112
|
/>
|
|
110
|
-
{errors.password && <span className="
|
|
113
|
+
{errors.password && <span className="font-mono text-[11px] text-error">{errors.password}</span>}
|
|
111
114
|
</div>
|
|
112
115
|
|
|
113
116
|
<button
|
|
114
|
-
className="
|
|
117
|
+
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"
|
|
115
118
|
type="submit"
|
|
116
119
|
disabled={submitting}
|
|
117
120
|
>
|
|
@@ -119,12 +122,12 @@ export function RegisterPage({ onSuccess }: RegisterPageProps) {
|
|
|
119
122
|
</button>
|
|
120
123
|
</form>
|
|
121
124
|
|
|
122
|
-
<p className="
|
|
125
|
+
<p className="font-mono text-xs text-content-secondary mt-4 text-center">
|
|
123
126
|
Already have an account? <Link to="/login">Sign in</Link>
|
|
124
127
|
</p>
|
|
125
128
|
|
|
126
129
|
<button
|
|
127
|
-
className="
|
|
130
|
+
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"
|
|
128
131
|
onClick={toggleTheme}
|
|
129
132
|
type="button"
|
|
130
133
|
title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
|
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`}
|
|
@@ -12,7 +12,7 @@ export function CoherenceRoute() {
|
|
|
12
12
|
if (!graphData) return null;
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
|
-
<div
|
|
15
|
+
<div className="w-full h-[calc(100vh-44px)] overflow-y-auto p-6">
|
|
16
16
|
<CoherenceView graphData={graphData} onNodeClick={openSidePanel} projectId={selectedProjectId} />
|
|
17
17
|
</div>
|
|
18
18
|
);
|
package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx
CHANGED
|
@@ -37,10 +37,10 @@ export function DashboardLayout() {
|
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
39
|
<ToastProvider>
|
|
40
|
-
<div className="
|
|
40
|
+
<div className="flex flex-col h-full overflow-hidden">
|
|
41
41
|
<Toolbar />
|
|
42
42
|
|
|
43
|
-
<div className="
|
|
43
|
+
<div className="flex-1 overflow-hidden relative">
|
|
44
44
|
<Outlet />
|
|
45
45
|
|
|
46
46
|
<SidePanel />
|
|
@@ -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
|
+
}
|