diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 6b303f3..ff57e8c 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -10,8 +10,10 @@ import { getProjects, deleteProject, updateProject, getProjectFolders, createProjectFolder, renameProjectFolder, deleteProjectFolder, moveProjectToFolder, moveProjectFolder, getProjectShares, getUsers, createProjectShare, + getDrawings, getElements, type Project, type ProjectFolder, type ProjectShare, type SimpleUser, } from '../services/api'; +import type { CADElement } from '../types/cad.types'; const API_BASE = import.meta.env.VITE_API_BASE || ''; const UNFILED_ID = '__unfiled__'; @@ -20,43 +22,96 @@ interface DashboardProps { onOpenProject: (projectId: string) => void; } -/** Render a static SVG preview of a project's first few elements */ -function CanvasPreview({ project }: { project: Project }) { - // Static preview: use project description or name hash to generate a deterministic mini-canvas - const seed = project.id.charCodeAt(0) + project.id.charCodeAt(project.id.length - 1); - const shapes = useMemo(() => { - const arr: React.ReactNode[] = []; - const colors = ['#2563eb', '#8b5cf6', '#10b981', '#f59e0b']; - for (let i = 0; i < 4; i++) { - const x = 10 + ((seed + i * 23) % 60); - const y = 5 + ((seed + i * 17) % 40); - const w = 8 + ((seed + i * 13) % 20); - const h = 8 + ((seed + i * 7) % 15); - const color = colors[i % colors.length]; - const shapeType = (seed + i) % 3; - if (shapeType === 0) { - arr.push( - - ); - } else if (shapeType === 1) { - arr.push( - - ); - } else { - arr.push( - - ); +/** Render a real SVG preview of a project's first drawing elements */ +function CanvasPreview({ project, token }: { project: Project; token: string }) { + const [elements, setElements] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + let cancelled = false; + async function loadPreview() { + try { + const drawings = await getDrawings(token, project.id); + if (drawings.length === 0) { if (!cancelled) setElements([]); return; } + const els = await getElements(token, drawings[0].id); + if (!cancelled) setElements(els); + } catch { + if (!cancelled) setElements([]); + } finally { + if (!cancelled) setLoading(false); } } - return arr; - }, [seed]); + loadPreview(); + return () => { cancelled = true; }; + }, [token, project.id]); + + // Compute bounding box of all elements for viewBox scaling + const bbox = useMemo(() => { + if (elements.length === 0) return null; + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; + for (const el of elements) { + minX = Math.min(minX, el.x); + minY = Math.min(minY, el.y); + maxX = Math.max(maxX, el.x + el.width); + maxY = Math.max(maxY, el.y + el.height); + } + return { minX, minY, w: Math.max(maxX - minX, 10), h: Math.max(maxY - minY, 10) }; + }, [elements]); + + const svgElements = useMemo(() => { + if (!bbox || elements.length === 0) return []; + return elements.slice(0, 50).map((el) => { + const stroke = el.properties.stroke || '#60a5fa'; + const fill = el.properties.fill || 'none'; + const sw = Math.max((el.properties.strokeWidth || 1), 0.5); + const x = el.x - bbox.minX; + const y = el.y - bbox.minY; + const w = el.width; + const h = el.height; + switch (el.type) { + case 'rect': + case 'table': + case 'stage': + return ; + case 'circle': + return ; + case 'line': + case 'leader': + return ; + case 'text': + return {el.properties.text || ''}; + case 'polygon': + case 'polyline': + if (el.properties.points && el.properties.points.length > 1) { + const pts = el.properties.points.map(p => `${p.x - bbox.minX},${p.y - bbox.minY}`).join(' '); + return ; + } + return null; + case 'chair': + return ; + case 'arc': + return ; + default: + return ; + } + }); + }, [elements, bbox]); return (
- - - {shapes} - + {loading ? ( +
...
+ ) : elements.length === 0 || !bbox ? ( + + + Leere Zeichnung + + ) : ( + + + {svgElements} + + )}
); } @@ -624,7 +679,7 @@ export function Dashboard({ onOpenProject }: DashboardProps) { onDragStart={(e) => handleProjectDragStart(e, project.id)} onDragEnd={() => { setDraggedProjectId(null); setDragOverFolderId(null); }} > - +

{project.name}

{project.description &&

{project.description}

}