@assistkick/create 1.7.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/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 +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/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 +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 +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/KanbanView.tsx +202 -171
- 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/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/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/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 +5 -0
- 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-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
package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Terminal view — multi-session manager with chat-like layout.
|
|
3
3
|
* Left sidebar lists all terminal sessions; right panel shows the active session's terminal.
|
|
4
4
|
* Each session is permanently bound to a project chosen at creation time.
|
|
5
|
+
* Sessions persist across server restarts — suspended sessions are auto-resumed on connect.
|
|
5
6
|
* Admin-only access.
|
|
6
7
|
*/
|
|
7
8
|
|
|
@@ -12,6 +13,10 @@ import { WebLinksAddon } from '@xterm/addon-web-links';
|
|
|
12
13
|
import '@xterm/xterm/css/xterm.css';
|
|
13
14
|
import { apiClient } from '../api/client';
|
|
14
15
|
import type { Project } from '../hooks/useProjects';
|
|
16
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
17
|
+
import { Button } from './ds/Button';
|
|
18
|
+
import { IconButton } from './ds/IconButton';
|
|
19
|
+
import { X, Plus, RefreshCw } from 'lucide-react';
|
|
15
20
|
|
|
16
21
|
interface TerminalViewProps {
|
|
17
22
|
visible: boolean;
|
|
@@ -20,11 +25,13 @@ interface TerminalViewProps {
|
|
|
20
25
|
|
|
21
26
|
interface SessionInfo {
|
|
22
27
|
id: string;
|
|
28
|
+
claudeSessionId: string;
|
|
23
29
|
name: string;
|
|
24
30
|
projectId: string;
|
|
25
31
|
projectName: string;
|
|
26
|
-
state: '
|
|
32
|
+
state: 'suspended' | 'running';
|
|
27
33
|
createdAt: string;
|
|
34
|
+
lastUsedAt: string;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
@@ -43,15 +50,11 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
43
50
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
44
51
|
const [status, setStatus] = useState<ConnectionStatus>('disconnected');
|
|
45
52
|
const [errorMsg, setErrorMsg] = useState('');
|
|
46
|
-
const [newSessionProjectId, setNewSessionProjectId] = useState<string>('');
|
|
47
53
|
const [creating, setCreating] = useState(false);
|
|
54
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
55
|
+
const dragCounterRef = useRef(0);
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (projects.length > 0 && !newSessionProjectId) {
|
|
52
|
-
setNewSessionProjectId(projects[0].id);
|
|
53
|
-
}
|
|
54
|
-
}, [projects, newSessionProjectId]);
|
|
57
|
+
const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
|
|
55
58
|
|
|
56
59
|
const fetchSessions = useCallback(async () => {
|
|
57
60
|
try {
|
|
@@ -120,8 +123,10 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
120
123
|
terminal.open(containerRef.current);
|
|
121
124
|
fitAddon.fit();
|
|
122
125
|
|
|
126
|
+
// Pass terminal dimensions so the server can resume with correct size
|
|
127
|
+
const { cols, rows } = terminal;
|
|
123
128
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
124
|
-
const wsUrl = `${protocol}//${window.location.host}/api/terminal?sessionId=${encodeURIComponent(sessionId)}`;
|
|
129
|
+
const wsUrl = `${protocol}//${window.location.host}/api/terminal?sessionId=${encodeURIComponent(sessionId)}&cols=${cols}&rows=${rows}`;
|
|
125
130
|
|
|
126
131
|
setStatus('connecting');
|
|
127
132
|
setErrorMsg('');
|
|
@@ -132,8 +137,10 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
132
137
|
ws.onopen = () => {
|
|
133
138
|
setStatus('connected');
|
|
134
139
|
fitAddon.fit();
|
|
135
|
-
const
|
|
136
|
-
ws.send(JSON.stringify({ type: 'resize', cols, rows }));
|
|
140
|
+
const dims = terminal;
|
|
141
|
+
ws.send(JSON.stringify({ type: 'resize', cols: dims.cols, rows: dims.rows }));
|
|
142
|
+
// Refresh sessions to update state from suspended → running
|
|
143
|
+
fetchSessions();
|
|
137
144
|
};
|
|
138
145
|
|
|
139
146
|
ws.onmessage = (event) => {
|
|
@@ -159,7 +166,7 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
159
166
|
setErrorMsg('Access denied. Admin privileges required.');
|
|
160
167
|
} else if (event.code === 4004) {
|
|
161
168
|
setStatus('error');
|
|
162
|
-
setErrorMsg('Session not found. It may have been
|
|
169
|
+
setErrorMsg('Session not found. It may have been deleted.');
|
|
163
170
|
fetchSessions();
|
|
164
171
|
} else {
|
|
165
172
|
setStatus('disconnected');
|
|
@@ -188,7 +195,7 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
188
195
|
}, [connectToSession]);
|
|
189
196
|
|
|
190
197
|
const handleCreateSession = useCallback(async () => {
|
|
191
|
-
const projectId =
|
|
198
|
+
const projectId = selectedProjectId || projects[0]?.id;
|
|
192
199
|
if (!projectId) return;
|
|
193
200
|
|
|
194
201
|
const project = projects.find(p => p.id === projectId) || projects[0];
|
|
@@ -207,7 +214,7 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
207
214
|
} finally {
|
|
208
215
|
setCreating(false);
|
|
209
216
|
}
|
|
210
|
-
}, [
|
|
217
|
+
}, [selectedProjectId, projects, fetchSessions, connectToSession]);
|
|
211
218
|
|
|
212
219
|
const handleKillSession = useCallback(async (sessionId: string, e: React.MouseEvent) => {
|
|
213
220
|
e.stopPropagation();
|
|
@@ -249,82 +256,144 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
|
|
|
249
256
|
};
|
|
250
257
|
}, [disconnectCurrent]);
|
|
251
258
|
|
|
259
|
+
// --- Image drag-and-drop handlers ---
|
|
260
|
+
const handleDragEnter = useCallback((e: React.DragEvent) => {
|
|
261
|
+
e.preventDefault();
|
|
262
|
+
e.stopPropagation();
|
|
263
|
+
dragCounterRef.current++;
|
|
264
|
+
if (e.dataTransfer.types.includes('Files')) {
|
|
265
|
+
setIsDragging(true);
|
|
266
|
+
}
|
|
267
|
+
}, []);
|
|
268
|
+
|
|
269
|
+
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
|
270
|
+
e.preventDefault();
|
|
271
|
+
e.stopPropagation();
|
|
272
|
+
dragCounterRef.current--;
|
|
273
|
+
if (dragCounterRef.current === 0) {
|
|
274
|
+
setIsDragging(false);
|
|
275
|
+
}
|
|
276
|
+
}, []);
|
|
277
|
+
|
|
278
|
+
const handleDragOver = useCallback((e: React.DragEvent) => {
|
|
279
|
+
e.preventDefault();
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
}, []);
|
|
282
|
+
|
|
283
|
+
const handleDrop = useCallback(async (e: React.DragEvent) => {
|
|
284
|
+
e.preventDefault();
|
|
285
|
+
e.stopPropagation();
|
|
286
|
+
dragCounterRef.current = 0;
|
|
287
|
+
setIsDragging(false);
|
|
288
|
+
|
|
289
|
+
const conn = connectionRef.current;
|
|
290
|
+
if (!conn || conn.ws.readyState !== WebSocket.OPEN) return;
|
|
291
|
+
|
|
292
|
+
const files = Array.from(e.dataTransfer.files);
|
|
293
|
+
const imageFiles = files.filter(f => f.type.startsWith('image/'));
|
|
294
|
+
if (imageFiles.length === 0) return;
|
|
295
|
+
|
|
296
|
+
for (const file of imageFiles) {
|
|
297
|
+
try {
|
|
298
|
+
const { path } = await apiClient.uploadTerminalImage(file);
|
|
299
|
+
conn.ws.send(JSON.stringify({ type: 'input', data: path + ' ' }));
|
|
300
|
+
} catch (err) {
|
|
301
|
+
console.error('Failed to upload image:', err);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}, []);
|
|
305
|
+
|
|
252
306
|
const activeSession = sessions.find(s => s.id === activeSessionId);
|
|
253
307
|
|
|
254
308
|
return (
|
|
255
|
-
<div className="
|
|
309
|
+
<div className="flex flex-row h-full w-full overflow-hidden" style={{ display: visible ? 'flex' : 'none' }}>
|
|
256
310
|
{/* Left sidebar — session list */}
|
|
257
|
-
<div className="
|
|
258
|
-
<div className="
|
|
259
|
-
<div className="
|
|
311
|
+
<div className="flex flex-col w-[280px] min-w-[280px] border-r border-edge bg-surface-alt overflow-hidden">
|
|
312
|
+
<div className="px-3 py-2.5 text-[11px] font-mono uppercase tracking-widest text-content-muted border-b border-edge shrink-0">Sessions</div>
|
|
313
|
+
<div className="flex-1 overflow-y-auto">
|
|
260
314
|
{sessions.length === 0 && (
|
|
261
|
-
<div className="
|
|
315
|
+
<div className="px-3 py-4 text-xs text-content-muted font-mono">No sessions yet</div>
|
|
262
316
|
)}
|
|
263
317
|
{sessions.map(session => (
|
|
264
318
|
<div
|
|
265
319
|
key={session.id}
|
|
266
|
-
className={`
|
|
320
|
+
className={`flex flex-col px-2.5 py-2 cursor-pointer border-b border-edge relative hover:bg-tab-hover${session.id === activeSessionId ? ' bg-tab-active border-l-2 border-l-accent pl-2' : ''}`}
|
|
267
321
|
onClick={() => handleSelectSession(session.id)}
|
|
268
322
|
>
|
|
269
|
-
<div className="
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
323
|
+
<div className="text-xs font-mono text-content whitespace-nowrap overflow-hidden text-ellipsis pr-5 flex items-center gap-1">
|
|
324
|
+
{session.state === 'suspended' && (
|
|
325
|
+
<span className="shrink-0 text-[10px] leading-none text-content-muted opacity-70" title="Suspended — will resume on connect">⏸</span>
|
|
326
|
+
)}
|
|
327
|
+
{session.name}
|
|
328
|
+
</div>
|
|
329
|
+
<div className="text-[11px] text-content-muted font-mono whitespace-nowrap overflow-hidden text-ellipsis pr-5 mt-0.5">{session.projectName}</div>
|
|
330
|
+
<IconButton
|
|
331
|
+
label="Kill session"
|
|
332
|
+
variant="danger"
|
|
333
|
+
size="sm"
|
|
334
|
+
className="absolute top-2 right-2"
|
|
273
335
|
onClick={(e) => handleKillSession(session.id, e)}
|
|
274
|
-
title="Kill session"
|
|
275
336
|
>
|
|
276
|
-
|
|
277
|
-
</
|
|
337
|
+
<X size={10} strokeWidth={2} />
|
|
338
|
+
</IconButton>
|
|
278
339
|
</div>
|
|
279
340
|
))}
|
|
280
341
|
</div>
|
|
281
342
|
|
|
282
|
-
{/* New session
|
|
283
|
-
<div className="
|
|
284
|
-
<
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
>
|
|
290
|
-
{projects.map(p => (
|
|
291
|
-
<option key={p.id} value={p.id}>{p.name}</option>
|
|
292
|
-
))}
|
|
293
|
-
</select>
|
|
294
|
-
<button
|
|
295
|
-
className="terminal-new-session-btn"
|
|
343
|
+
{/* New session — uses the project selected in the navbar */}
|
|
344
|
+
<div className="flex flex-col gap-1.5 p-2.5 border-t border-edge shrink-0">
|
|
345
|
+
<Button
|
|
346
|
+
variant="primary"
|
|
347
|
+
size="sm"
|
|
348
|
+
icon={!creating ? <Plus size={12} strokeWidth={2} /> : undefined}
|
|
349
|
+
className="w-full"
|
|
296
350
|
onClick={handleCreateSession}
|
|
297
|
-
disabled={creating ||
|
|
351
|
+
disabled={creating || !selectedProjectId}
|
|
298
352
|
>
|
|
299
|
-
{creating ? '
|
|
300
|
-
</
|
|
353
|
+
{creating ? '...' : 'New'}
|
|
354
|
+
</Button>
|
|
301
355
|
</div>
|
|
302
356
|
</div>
|
|
303
357
|
|
|
304
358
|
{/* Right panel — terminal content */}
|
|
305
|
-
<div
|
|
359
|
+
<div
|
|
360
|
+
className="flex-1 flex flex-col min-w-0 relative"
|
|
361
|
+
onDragEnter={handleDragEnter}
|
|
362
|
+
onDragLeave={handleDragLeave}
|
|
363
|
+
onDragOver={handleDragOver}
|
|
364
|
+
onDrop={handleDrop}
|
|
365
|
+
>
|
|
366
|
+
{isDragging && (
|
|
367
|
+
<div className="absolute inset-0 z-10 flex items-center justify-center bg-[rgba(30,30,46,0.85)] border-2 border-dashed border-[#89b4fa] rounded-lg pointer-events-none">
|
|
368
|
+
<div className="text-[#89b4fa] text-lg font-mono font-semibold">Drop image here</div>
|
|
369
|
+
</div>
|
|
370
|
+
)}
|
|
306
371
|
{!activeSessionId && (
|
|
307
|
-
<div className="
|
|
372
|
+
<div className="flex-1 flex items-center justify-center text-content-muted text-[13px] font-mono">
|
|
308
373
|
Select a session or create a new one
|
|
309
374
|
</div>
|
|
310
375
|
)}
|
|
311
376
|
{activeSession && errorMsg && (
|
|
312
|
-
<div className="
|
|
377
|
+
<div className="flex items-center gap-3 px-4 py-2 bg-error text-[#1e1e2e] text-[13px] font-mono shrink-0">
|
|
313
378
|
<span>{errorMsg}</span>
|
|
314
|
-
<
|
|
315
|
-
|
|
379
|
+
<Button
|
|
380
|
+
variant="secondary"
|
|
381
|
+
size="sm"
|
|
382
|
+
icon={<RefreshCw size={12} strokeWidth={2} />}
|
|
316
383
|
onClick={() => connectToSession(activeSessionId!)}
|
|
317
384
|
>
|
|
318
385
|
Reconnect
|
|
319
|
-
</
|
|
386
|
+
</Button>
|
|
320
387
|
</div>
|
|
321
388
|
)}
|
|
322
389
|
{activeSession && status === 'connecting' && (
|
|
323
|
-
<div className="
|
|
390
|
+
<div className="px-4 py-2 text-content-secondary text-[13px] font-mono shrink-0">
|
|
391
|
+
{activeSession.state === 'suspended' ? 'Resuming session...' : 'Connecting...'}
|
|
392
|
+
</div>
|
|
324
393
|
)}
|
|
325
394
|
<div
|
|
326
395
|
ref={containerRef}
|
|
327
|
-
className="terminal-container"
|
|
396
|
+
className="terminal-container flex-1 p-1 bg-[#1e1e2e] min-h-0"
|
|
328
397
|
style={{ display: activeSessionId ? 'flex' : 'none' }}
|
|
329
398
|
/>
|
|
330
399
|
</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useCallback, useEffect } from 'react';
|
|
2
2
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
3
3
|
import {
|
|
4
|
-
Columns3, MessageSquare, Network, ShieldCheck, Palette, Users,
|
|
5
|
-
Maximize, Settings, Sun, Moon,
|
|
4
|
+
Columns3, MessageSquare, Network, ShieldCheck, Palette, Users, Bot, Workflow,
|
|
5
|
+
FolderOpen, Maximize, Settings, Sun, Moon, Video,
|
|
6
6
|
} from 'lucide-react';
|
|
7
7
|
import { NavBarSidekick } from './ds/NavBarSidekick';
|
|
8
8
|
import type { NavItem } from './ds/NavBarSidekick';
|
|
@@ -15,7 +15,7 @@ import { useTheme } from '../hooks/useTheme';
|
|
|
15
15
|
import { useAuth } from '../hooks/useAuth';
|
|
16
16
|
import { apiClient } from '../api/client';
|
|
17
17
|
|
|
18
|
-
const VALID_TABS = new Set(['graph', 'kanban', 'coherence', 'users', 'terminal', 'design-system']);
|
|
18
|
+
const VALID_TABS = new Set(['graph', 'kanban', 'coherence', 'users', 'terminal', 'agents', 'workflows', 'design-system', 'files', 'videography']);
|
|
19
19
|
|
|
20
20
|
function tabFromPath(pathname: string): string {
|
|
21
21
|
const seg = pathname.replace(/^\//, '');
|
|
@@ -25,14 +25,18 @@ function tabFromPath(pathname: string): string {
|
|
|
25
25
|
const ICON_PROPS = { size: 14, strokeWidth: 2 } as const;
|
|
26
26
|
|
|
27
27
|
const TAB_ITEMS: NavItem[] = [
|
|
28
|
-
{ id: 'kanban', label: 'Kanban', icon: <Columns3 {...ICON_PROPS} />, description: 'Board view of features' },
|
|
29
|
-
{ id: 'terminal', label: 'Chat', icon: <MessageSquare {...ICON_PROPS} />, description: 'AI assistant terminal' },
|
|
30
|
-
{ id: 'graph', label: 'Graph', icon: <Network {...ICON_PROPS} />, description: 'Knowledge graph visualization' },
|
|
31
|
-
{ id: 'coherence', label: 'Coherence', icon: <ShieldCheck {...ICON_PROPS} />, description: 'Coherence analysis' },
|
|
32
|
-
{ id: '
|
|
28
|
+
{ id: 'kanban', label: 'Kanban', icon: <Columns3 {...ICON_PROPS} />, description: 'Board view of features', href: '/kanban' },
|
|
29
|
+
{ id: 'terminal', label: 'Chat', icon: <MessageSquare {...ICON_PROPS} />, description: 'AI assistant terminal', href: '/terminal' },
|
|
30
|
+
{ id: 'graph', label: 'Graph', icon: <Network {...ICON_PROPS} />, description: 'Knowledge graph visualization', href: '/graph' },
|
|
31
|
+
{ id: 'coherence', label: 'Coherence', icon: <ShieldCheck {...ICON_PROPS} />, description: 'Coherence analysis', href: '/coherence' },
|
|
32
|
+
{ id: 'agents', label: 'Agents', icon: <Bot {...ICON_PROPS} />, description: 'Manage agent prompts', href: '/agents' },
|
|
33
|
+
{ id: 'workflows', label: 'Workflows', icon: <Workflow {...ICON_PROPS} />, description: 'Visual workflow builder', href: '/workflows' },
|
|
34
|
+
{ id: 'files', label: 'Files', icon: <FolderOpen {...ICON_PROPS} />, description: 'Browse and edit project files', href: '/files' },
|
|
35
|
+
{ id: 'design-system', label: 'Design System', icon: <Palette {...ICON_PROPS} />, description: 'Living style guide', href: '/design-system' },
|
|
33
36
|
];
|
|
34
37
|
|
|
35
|
-
const
|
|
38
|
+
const VIDEOGRAPHY_ITEM: NavItem = { id: 'videography', label: 'Videography', icon: <Video {...ICON_PROPS} />, description: 'Video production pipeline', href: '/videography' };
|
|
39
|
+
const ADMIN_ITEM: NavItem = { id: 'users', label: 'Users', icon: <Users {...ICON_PROPS} />, description: 'User management', href: '/users' };
|
|
36
40
|
|
|
37
41
|
const iconBtnClass = (active?: boolean) => [
|
|
38
42
|
'flex h-8 w-8 items-center justify-center rounded-lg border',
|
|
@@ -69,11 +73,14 @@ export function Toolbar() {
|
|
|
69
73
|
? Math.round((graphData.nodes.reduce((acc: number, n: any) => acc + (n.completeness || 0), 0) / Math.max(graphData.nodes.length, 1)) * 100)
|
|
70
74
|
: 0;
|
|
71
75
|
|
|
72
|
-
const
|
|
76
|
+
const selectedProject = projects.find(p => p.id === selectedProjectId);
|
|
77
|
+
const isVideoProject = selectedProject?.type === 'video';
|
|
73
78
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
const items = [
|
|
80
|
+
...TAB_ITEMS,
|
|
81
|
+
...(isVideoProject ? [VIDEOGRAPHY_ITEM] : []),
|
|
82
|
+
...(isAdmin ? [ADMIN_ITEM] : []),
|
|
83
|
+
];
|
|
77
84
|
|
|
78
85
|
const handleLogout = useCallback(async () => {
|
|
79
86
|
try {
|
|
@@ -103,7 +110,6 @@ export function Toolbar() {
|
|
|
103
110
|
<NavBarSidekick
|
|
104
111
|
items={items}
|
|
105
112
|
activeId={activeTab}
|
|
106
|
-
onNavigate={handleNavigate}
|
|
107
113
|
brand={
|
|
108
114
|
<ProjectSelector
|
|
109
115
|
projects={projects}
|
|
@@ -110,43 +110,43 @@ export function UsersView({ visible }: UsersViewProps) {
|
|
|
110
110
|
if (!visible) return null;
|
|
111
111
|
|
|
112
112
|
return (
|
|
113
|
-
<div className="
|
|
114
|
-
<div className="
|
|
115
|
-
<h2>User Management</h2>
|
|
116
|
-
<button className="
|
|
113
|
+
<div className="p-6 max-w-[900px] mx-auto text-content">
|
|
114
|
+
<div className="flex justify-between items-center mb-5">
|
|
115
|
+
<h2 className="text-xl font-semibold">User Management</h2>
|
|
116
|
+
<button className="bg-accent text-white border-none rounded-md px-4 py-2 text-sm cursor-pointer font-mono hover:opacity-90" onClick={() => setInviteOpen(true)}>
|
|
117
117
|
+ Invite User
|
|
118
118
|
</button>
|
|
119
119
|
</div>
|
|
120
120
|
|
|
121
|
-
{error && <div className="
|
|
121
|
+
{error && <div className="bg-[rgba(255,107,107,0.15)] text-error px-3.5 py-2.5 rounded-md mb-4 text-sm flex justify-between items-center">{error}<button className="bg-none border-none text-error text-lg cursor-pointer px-1" onClick={() => setError('')}>×</button></div>}
|
|
122
122
|
|
|
123
123
|
{loading ? (
|
|
124
|
-
<div className="
|
|
124
|
+
<div className="text-content-secondary py-10 text-center">Loading...</div>
|
|
125
125
|
) : (
|
|
126
126
|
<>
|
|
127
|
-
<section className="
|
|
128
|
-
<h3>Registered Users ({users.length})</h3>
|
|
129
|
-
<table className="
|
|
127
|
+
<section className="mb-7">
|
|
128
|
+
<h3 className="text-[15px] font-semibold text-content-secondary mb-2.5">Registered Users ({users.length})</h3>
|
|
129
|
+
<table className="w-full border-collapse text-sm font-mono">
|
|
130
130
|
<thead>
|
|
131
131
|
<tr>
|
|
132
|
-
<th>Email</th>
|
|
133
|
-
<th>Role</th>
|
|
134
|
-
<th>Created</th>
|
|
135
|
-
<th></th>
|
|
132
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Email</th>
|
|
133
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Role</th>
|
|
134
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Created</th>
|
|
135
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide"></th>
|
|
136
136
|
</tr>
|
|
137
137
|
</thead>
|
|
138
138
|
<tbody>
|
|
139
139
|
{users.map(u => (
|
|
140
|
-
<tr key={u.id} className={u.id === currentUser?.id ? '
|
|
141
|
-
<td>{u.email}</td>
|
|
142
|
-
<td><span className={`
|
|
143
|
-
<td>{formatDate(u.createdAt)}</td>
|
|
144
|
-
<td>
|
|
140
|
+
<tr key={u.id} className={`hover:bg-surface-raised ${u.id === currentUser?.id ? 'bg-surface-alt' : ''}`}>
|
|
141
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{u.email}</td>
|
|
142
|
+
<td className="px-3 py-2.5 border-b border-edge text-content"><span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${u.role === 'admin' ? 'bg-[rgba(77,171,247,0.15)] text-accent' : 'bg-surface-raised text-content-secondary'}`}>{u.role}</span></td>
|
|
143
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(u.createdAt)}</td>
|
|
144
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">
|
|
145
145
|
{u.id === currentUser?.id ? (
|
|
146
|
-
<span className="
|
|
146
|
+
<span className="text-xs text-content-muted italic">you</span>
|
|
147
147
|
) : (
|
|
148
148
|
<button
|
|
149
|
-
className="
|
|
149
|
+
className="bg-none border border-edge text-error rounded px-2.5 py-1 text-xs cursor-pointer font-mono hover:bg-[rgba(255,107,107,0.1)] hover:border-error disabled:opacity-50 disabled:cursor-not-allowed"
|
|
150
150
|
onClick={() => handleDeleteUser(u.id, u.email)}
|
|
151
151
|
disabled={deletingId === u.id}
|
|
152
152
|
title={`Delete ${u.email}`}
|
|
@@ -161,31 +161,31 @@ export function UsersView({ visible }: UsersViewProps) {
|
|
|
161
161
|
</table>
|
|
162
162
|
</section>
|
|
163
163
|
|
|
164
|
-
<section className="
|
|
165
|
-
<h3>Pending Invitations ({pendingInvitations.length})</h3>
|
|
164
|
+
<section className="mb-7">
|
|
165
|
+
<h3 className="text-[15px] font-semibold text-content-secondary mb-2.5">Pending Invitations ({pendingInvitations.length})</h3>
|
|
166
166
|
{pendingInvitations.length === 0 ? (
|
|
167
|
-
<p className="
|
|
167
|
+
<p className="text-content-muted text-sm py-3">No pending invitations.</p>
|
|
168
168
|
) : (
|
|
169
|
-
<table className="
|
|
169
|
+
<table className="w-full border-collapse text-sm font-mono">
|
|
170
170
|
<thead>
|
|
171
171
|
<tr>
|
|
172
|
-
<th>Email</th>
|
|
173
|
-
<th>Invited By</th>
|
|
174
|
-
<th>Sent</th>
|
|
175
|
-
<th>Expires</th>
|
|
176
|
-
<th></th>
|
|
172
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Email</th>
|
|
173
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Invited By</th>
|
|
174
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Sent</th>
|
|
175
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Expires</th>
|
|
176
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide"></th>
|
|
177
177
|
</tr>
|
|
178
178
|
</thead>
|
|
179
179
|
<tbody>
|
|
180
180
|
{pendingInvitations.map(inv => (
|
|
181
|
-
<tr key={inv.id}>
|
|
182
|
-
<td>{inv.email}</td>
|
|
183
|
-
<td>{inv.invitedByEmail}</td>
|
|
184
|
-
<td>{formatDate(inv.createdAt)}</td>
|
|
185
|
-
<td>{formatDate(inv.expiresAt)}</td>
|
|
186
|
-
<td>
|
|
181
|
+
<tr key={inv.id} className="hover:bg-surface-raised">
|
|
182
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{inv.email}</td>
|
|
183
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{inv.invitedByEmail}</td>
|
|
184
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(inv.createdAt)}</td>
|
|
185
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(inv.expiresAt)}</td>
|
|
186
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">
|
|
187
187
|
<button
|
|
188
|
-
className="
|
|
188
|
+
className="bg-none border border-edge text-error rounded px-2.5 py-1 text-xs cursor-pointer font-mono hover:bg-[rgba(255,107,107,0.1)] hover:border-error disabled:opacity-50 disabled:cursor-not-allowed"
|
|
189
189
|
onClick={() => handleDeleteInvitation(inv.id, inv.email)}
|
|
190
190
|
disabled={deletingId === inv.id}
|
|
191
191
|
title={`Cancel invitation for ${inv.email}`}
|
|
@@ -201,32 +201,32 @@ export function UsersView({ visible }: UsersViewProps) {
|
|
|
201
201
|
</section>
|
|
202
202
|
|
|
203
203
|
{pastInvitations.length > 0 && (
|
|
204
|
-
<section className="
|
|
205
|
-
<h3>Past Invitations ({pastInvitations.length})</h3>
|
|
206
|
-
<table className="
|
|
204
|
+
<section className="mb-7">
|
|
205
|
+
<h3 className="text-[15px] font-semibold text-content-secondary mb-2.5">Past Invitations ({pastInvitations.length})</h3>
|
|
206
|
+
<table className="w-full border-collapse text-sm font-mono">
|
|
207
207
|
<thead>
|
|
208
208
|
<tr>
|
|
209
|
-
<th>Email</th>
|
|
210
|
-
<th>Invited By</th>
|
|
211
|
-
<th>Status</th>
|
|
212
|
-
<th>Date</th>
|
|
213
|
-
<th></th>
|
|
209
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Email</th>
|
|
210
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Invited By</th>
|
|
211
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Status</th>
|
|
212
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Date</th>
|
|
213
|
+
<th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide"></th>
|
|
214
214
|
</tr>
|
|
215
215
|
</thead>
|
|
216
216
|
<tbody>
|
|
217
217
|
{pastInvitations.map(inv => (
|
|
218
|
-
<tr key={inv.id} className="
|
|
219
|
-
<td>{inv.email}</td>
|
|
220
|
-
<td>{inv.invitedByEmail}</td>
|
|
221
|
-
<td>
|
|
222
|
-
<span className={`
|
|
218
|
+
<tr key={inv.id} className="opacity-70 hover:bg-surface-raised">
|
|
219
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{inv.email}</td>
|
|
220
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{inv.invitedByEmail}</td>
|
|
221
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">
|
|
222
|
+
<span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${inv.acceptedAt ? 'bg-[rgba(105,219,124,0.15)] text-completeness-fill' : 'bg-[rgba(255,107,107,0.15)] text-error'}`}>
|
|
223
223
|
{inv.acceptedAt ? 'Accepted' : 'Expired'}
|
|
224
224
|
</span>
|
|
225
225
|
</td>
|
|
226
|
-
<td>{formatDate(inv.acceptedAt || inv.expiresAt)}</td>
|
|
227
|
-
<td>
|
|
226
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(inv.acceptedAt || inv.expiresAt)}</td>
|
|
227
|
+
<td className="px-3 py-2.5 border-b border-edge text-content">
|
|
228
228
|
<button
|
|
229
|
-
className="
|
|
229
|
+
className="bg-none border border-edge text-error rounded px-2.5 py-1 text-xs cursor-pointer font-mono hover:bg-[rgba(255,107,107,0.1)] hover:border-error disabled:opacity-50 disabled:cursor-not-allowed"
|
|
230
230
|
onClick={() => handleDeleteInvitation(inv.id, inv.email)}
|
|
231
231
|
disabled={deletingId === inv.id}
|
|
232
232
|
title={`Delete invitation record for ${inv.email}`}
|