feat: implement project management features with routing, including project creation, deletion, renaming, and graph persistence

This commit is contained in:
2026-03-09 22:10:32 +01:00
parent c73aaca45a
commit e441d36cf4
12 changed files with 740 additions and 142 deletions

View File

@@ -57,6 +57,12 @@ import {
isConnectionAllowed,
} from '@/lib/nodeRegistry'
import type { AppNode, AppEdge } from '@/lib/nodeTypes'
import {
loadGraphFromStorage,
saveGraphToStorage,
PROJECT_FILE_EXT,
PROJECT_VERSION,
} from '@/app/platform/projectGraphStorage'
const SNAP_GRID: [number, number] = [15, 15]
const snapToGrid = (x: number, y: number): { x: number; y: number } => ({
@@ -110,9 +116,6 @@ function getExampleGraph(): { nodes: AppNode[]; edges: AppEdge[] } {
}
}
const PROJECT_FILE_EXT = '.zui.json'
const PROJECT_VERSION = 1
export type CanvasMessage = { type: 'success' | 'error'; text: string }
function FlowFitViewOnLoad() {
@@ -129,8 +132,19 @@ export type CanvasPageProps = {
projectId?: string
}
export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
function getInitialGraph(projectId: string | undefined): { nodes: AppNode[]; edges: AppEdge[] } {
if (projectId) {
const stored = loadGraphFromStorage(projectId)
if (stored && (stored.nodes.length > 0 || stored.edges.length > 0)) {
return { nodes: stored.nodes as AppNode[], edges: stored.edges as AppEdge[] }
}
}
return getExampleGraph()
}
export function CanvasPage({ projectId }: CanvasPageProps) {
const { theme } = useTheme()
const initialGraph = useMemo(() => getInitialGraph(projectId), [projectId])
const {
nodes,
edges,
@@ -145,7 +159,20 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
canUndo,
canRedo,
setStateImmediate,
} = useGraphStateWithHistory(getExampleGraph().nodes, getExampleGraph().edges)
} = useGraphStateWithHistory(initialGraph.nodes, initialGraph.edges)
// Persist graph to localStorage when projectId is set (debounced)
const saveTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
React.useEffect(() => {
if (!projectId) return
const save = () => {
saveGraphToStorage(projectId, { version: PROJECT_VERSION, nodes, edges })
}
saveTimeoutRef.current = setTimeout(save, 500)
return () => {
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
}
}, [projectId, nodes, edges])
const importInputRef = useRef<HTMLInputElement | null>(null)
const [rfInstance, setRfInstance] = React.useState<unknown>(null)