diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 61c5eda..a170e4d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -18,6 +18,9 @@ import StatusBar from './components/StatusBar'; import MobileDrawers from './components/MobileDrawers'; import BackgroundImport from './components/BackgroundImport'; import HistoryPanel from './components/HistoryPanel'; +import ShareDialog from './components/ShareDialog'; +import HelpPanel from './components/HelpPanel'; +import NotificationPanel from './components/NotificationPanel'; import { BackgroundService, type BackgroundConfig } from './services/backgroundService'; import { HistoryManager, type CADStateSnapshot, type HistoryEntry } from './history'; import { getCommandRegistry } from './services/commandRegistry'; @@ -162,6 +165,9 @@ const CADEditor: React.FC = ({ projectId, token, onNavigateBack // History panel const [historyPanelOpen, setHistoryPanelOpen] = useState(false); + const [shareOpen, setShareOpen] = useState(false); + const [helpOpen, setHelpOpen] = useState(false); + const [notifOpen, setNotifOpen] = useState(false); // Elements + undo/redo (HistoryManager) const [elements, setElements] = useState([]); @@ -947,6 +953,10 @@ const CADEditor: React.FC = ({ projectId, token, onNavigateBack theme={theme} onOpenLeftDrawer={() => setMobileLeftOpen(true)} onNavigateBack={onNavigateBack} + onOpenHistory={() => setHistoryPanelOpen(true)} + onOpenShare={() => setShareOpen(true)} + onOpenHelp={() => setHelpOpen(true)} + onOpenNotifications={() => setNotifOpen(true)} /> = ({ projectId, token, onNavigateBack onClose={() => setHistoryPanelOpen(false)} /> )} + setShareOpen(false)} + projectName={projectName} + /> + setHelpOpen(false)} + /> + setNotifOpen(false)} + /> ); }; diff --git a/frontend/src/components/HelpPanel.tsx b/frontend/src/components/HelpPanel.tsx new file mode 100644 index 0000000..3101275 --- /dev/null +++ b/frontend/src/components/HelpPanel.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import type { HelpPanelProps } from '../types/ui.types'; + +const shortcuts: { key: string; label: string }[] = [ + { key: 'V', label: 'Auswählen' }, + { key: 'L', label: 'Linie' }, + { key: 'C', label: 'Kreis' }, + { key: 'R', label: 'Rechteck' }, + { key: 'T', label: 'Text' }, + { key: 'P', label: 'Pan' }, + { key: 'Z', label: 'Zoom' }, + { key: 'M', label: 'Messen' }, + { key: 'Strg+Z', label: 'Rückgängig' }, + { key: 'Strg+Y', label: 'Wiederherstellen' }, + { key: 'Entf', label: 'Löschen' }, + { key: 'Strg+C', label: 'Kopieren' }, + { key: 'Strg+V', label: 'Einfügen' }, +]; + +const quickStart: { step: number; text: string }[] = [ + { step: 1, text: 'Projekt öffnen oder neues Projekt erstellen' }, + { step: 2, text: 'Werkzeug aus der linken Sidebar wählen' }, + { step: 3, text: 'Auf der Zeichenfläche klicken und zeichnen' }, + { step: 4, text: 'Projekt speichern mit Strg+S' }, +]; + +const HelpPanel: React.FC = ({ open, onClose }) => { + if (!open) return null; + + return ( +
+
e.stopPropagation()}> +
+

Hilfe

+ +
+
+
+

Tastenkürzel

+
+ {shortcuts.map((s) => ( +
+ {s.key} + {s.label} +
+ ))} +
+
+
+

Schnellstart

+
    + {quickStart.map((q) => ( +
  1. + {q.step} + {q.text} +
  2. + ))} +
+
+
+
+
+ ); +}; + +export default HelpPanel; diff --git a/frontend/src/components/NotificationPanel.tsx b/frontend/src/components/NotificationPanel.tsx new file mode 100644 index 0000000..2a1d3bc --- /dev/null +++ b/frontend/src/components/NotificationPanel.tsx @@ -0,0 +1,78 @@ +import React, { useState, useEffect, useRef } from 'react'; +import type { NotificationPanelProps } from '../types/ui.types'; + +interface Notification { + id: string; + icon: string; + title: string; + timestamp: string; +} + +const initialNotifications: Notification[] = [ + { id: 'n1', icon: '💾', title: 'Projekt automatisch gespeichert', timestamp: 'vor 2 Min.' }, + { id: 'n2', icon: '🔄', title: 'Neue Version verfügbar', timestamp: 'vor 15 Min.' }, + { id: 'n3', icon: '👤', title: 'Benutzer ist beigetreten', timestamp: 'vor 1 Std.' }, +]; + +const NotificationPanel: React.FC = ({ open, onClose }) => { + const [notifications, setNotifications] = useState(initialNotifications); + const [allRead, setAllRead] = useState(false); + const ref = useRef(null); + + useEffect(() => { + if (!open) return; + const handleClickOutside = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + onClose(); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [open, onClose]); + + if (!open) return null; + + const handleDismiss = (id: string) => { + setNotifications((prev) => prev.filter((n) => n.id !== id)); + }; + + const handleMarkAllRead = () => { + setAllRead(true); + }; + + return ( +
+
+

Benachrichtigungen

+ +
+
+ {notifications.length === 0 ? ( +

Keine Benachrichtigungen

+ ) : ( + notifications.map((n) => ( +
+ {n.icon} +
+ {n.title} + {n.timestamp} +
+ +
+ )) + )} +
+ {notifications.length > 0 && ( + + )} +
+ ); +}; + +export default NotificationPanel; diff --git a/frontend/src/components/ShareDialog.tsx b/frontend/src/components/ShareDialog.tsx new file mode 100644 index 0000000..7966904 --- /dev/null +++ b/frontend/src/components/ShareDialog.tsx @@ -0,0 +1,71 @@ +import React, { useState } from 'react'; +import type { ShareDialogProps } from '../types/ui.types'; + +const ShareDialog: React.FC = ({ open, onClose, projectName }) => { + const [email, setEmail] = useState(''); + const [copied, setCopied] = useState(false); + const [invited, setInvited] = useState(false); + + if (!open) return null; + + const shareLink = `${window.location.origin}/?project=${encodeURIComponent(projectName)}`; + + const handleCopyLink = () => { + navigator.clipboard.writeText(shareLink).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + }; + + const handleInvite = () => { + if (email.trim()) { + setInvited(true); + setEmail(''); + setTimeout(() => setInvited(false), 3000); + } + }; + + return ( +
+
e.stopPropagation()}> +
+

Projekt teilen

+ +
+
+

{projectName}

+
+ +
+ + +
+
+
+ +
+ setEmail(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleInvite()} + /> + +
+ {invited &&

Einladung gesendet!

} +
+
+
+
+ ); +}; + +export default ShareDialog; diff --git a/frontend/src/components/Topbar.tsx b/frontend/src/components/Topbar.tsx index 0515f24..844d95b 100644 --- a/frontend/src/components/Topbar.tsx +++ b/frontend/src/components/Topbar.tsx @@ -1,7 +1,7 @@ import React from 'react'; import type { TopbarProps } from '../types/ui.types'; -const Topbar: React.FC = ({ projectName, savedStatus, onUndo, onRedo, onThemeToggle, theme, onOpenSettings, onOpenLeftDrawer, onNavigateBack }) => { +const Topbar: React.FC = ({ projectName, savedStatus, onUndo, onRedo, onThemeToggle, theme, onOpenSettings, onOpenLeftDrawer, onNavigateBack, onOpenHistory, onOpenShare, onOpenHelp, onOpenNotifications }) => { return (
@@ -28,17 +28,17 @@ const Topbar: React.FC = ({ projectName, savedStatus, onUndo, onRed - - - @@ -46,7 +46,7 @@ const Topbar: React.FC = ({ projectName, savedStatus, onUndo, onRed -