navigationStore: rewritten off Neo4j db names — Supabase whiteboard_rooms table, setAuthInfo(token, userId) pattern, auto-creates default room per context on first use snapshotService: rewritten to Supabase Storage REST (/storage/v1/object/authenticated/cc.users/…), setAccessToken() instance method, static methods take accessToken not dbName AuthContext/NeoUserContext: auth injected into nav store, no Neo4j db names required singlePlayerPage: loadNodeData no longer calls Neo4j; snapshot wired via accessToken navigation types: NeoGraphNode updated for Supabase-backed tree structure transcriptionStore/Service: getSession() removed, accessToken via AuthContext LLMConfigModal: auth context wiring fixes GraphNavigator/GraphSidebar: updated nav components Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
122 lines
4.4 KiB
TypeScript
122 lines
4.4 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
IconButton, Tooltip, Box, Menu, MenuItem,
|
|
ListItemIcon, ListItemText, Chip, styled,
|
|
} from '@mui/material';
|
|
import {
|
|
ArrowBack as ArrowBackIcon,
|
|
ArrowForward as ArrowForwardIcon,
|
|
History as HistoryIcon,
|
|
Home as HomeIcon,
|
|
CalendarToday,
|
|
DateRange,
|
|
Event,
|
|
WorkspacesOutlined,
|
|
} from '@mui/icons-material';
|
|
import { useNavigationStore } from '../../stores/navigationStore';
|
|
|
|
const NavigationRoot = styled(Box)`
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
height: 100%;
|
|
`;
|
|
|
|
function getNodeIcon(nodeType: string) {
|
|
switch (nodeType) {
|
|
case 'User': return <HomeIcon fontSize="small" />;
|
|
case 'CalendarYear': return <CalendarToday fontSize="small" />;
|
|
case 'CalendarMonth': return <DateRange fontSize="small" />;
|
|
case 'CalendarDay': return <Event fontSize="small" />;
|
|
default: return <WorkspacesOutlined fontSize="small" />;
|
|
}
|
|
}
|
|
|
|
export const GraphNavigator: React.FC = () => {
|
|
const { context, goBack, goForward, isLoading } = useNavigationStore();
|
|
const [historyMenuAnchor, setHistoryMenuAnchor] = useState<null | HTMLElement>(null);
|
|
|
|
const { history } = context;
|
|
const canGoBack = history.currentIndex > 0;
|
|
const canGoForward = history.currentIndex < history.nodes.length - 1;
|
|
const currentNode = context.node;
|
|
|
|
const handleHistoryClick = (e: React.MouseEvent<HTMLElement>) => setHistoryMenuAnchor(e.currentTarget);
|
|
const handleHistoryClose = () => setHistoryMenuAnchor(null);
|
|
const handleHistoryItemClick = (index: number) => {
|
|
const delta = index - history.currentIndex;
|
|
if (delta < 0) for (let i = 0; i < -delta; i++) goBack();
|
|
else if (delta > 0) for (let i = 0; i < delta; i++) goForward();
|
|
handleHistoryClose();
|
|
};
|
|
|
|
return (
|
|
<NavigationRoot>
|
|
<Tooltip title="Back">
|
|
<span>
|
|
<IconButton onClick={goBack} disabled={!canGoBack || isLoading} size="small">
|
|
<ArrowBackIcon fontSize="small" />
|
|
</IconButton>
|
|
</span>
|
|
</Tooltip>
|
|
|
|
<Tooltip title="History">
|
|
<span>
|
|
<IconButton
|
|
onClick={handleHistoryClick}
|
|
disabled={!history.nodes.length}
|
|
size="small"
|
|
>
|
|
<HistoryIcon fontSize="small" />
|
|
</IconButton>
|
|
</span>
|
|
</Tooltip>
|
|
|
|
<Tooltip title="Forward">
|
|
<span>
|
|
<IconButton onClick={goForward} disabled={!canGoForward || isLoading} size="small">
|
|
<ArrowForwardIcon fontSize="small" />
|
|
</IconButton>
|
|
</span>
|
|
</Tooltip>
|
|
|
|
{currentNode && (
|
|
<Chip
|
|
size="small"
|
|
icon={getNodeIcon(currentNode.type)}
|
|
label={currentNode.label || currentNode.type}
|
|
variant="outlined"
|
|
sx={{ maxWidth: 200, fontSize: '0.75rem' }}
|
|
/>
|
|
)}
|
|
|
|
<Menu
|
|
anchorEl={historyMenuAnchor}
|
|
open={Boolean(historyMenuAnchor)}
|
|
onClose={handleHistoryClose}
|
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
|
transformOrigin={{ vertical: 'top', horizontal: 'center' }}
|
|
>
|
|
{history.nodes.map((node, index) => (
|
|
<MenuItem
|
|
key={`${node.id}-${index}`}
|
|
onClick={() => handleHistoryItemClick(index)}
|
|
selected={index === history.currentIndex}
|
|
dense
|
|
>
|
|
<ListItemIcon sx={{ minWidth: 32 }}>
|
|
{getNodeIcon(node.type)}
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary={node.label || node.id}
|
|
secondary={node.type}
|
|
primaryTypographyProps={{ fontSize: '0.8rem' }}
|
|
secondaryTypographyProps={{ fontSize: '0.7rem' }}
|
|
/>
|
|
</MenuItem>
|
|
))}
|
|
</Menu>
|
|
</NavigationRoot>
|
|
);
|
|
};
|