task(CAD-12a+b): autosave service with visible status indicator + global keyboard shortcuts (ESC, Delete, Strg+A/D, F7/F8/F3) + BOM dialog wiring fix
This commit is contained in:
@@ -18,6 +18,10 @@ import StatusBar from './components/StatusBar';
|
||||
import LayoutBar from './components/LayoutBar';
|
||||
import MobileDrawers from './components/MobileDrawers';
|
||||
import BackgroundImport from './components/BackgroundImport';
|
||||
import BOMDialog from './components/BOMDialog';
|
||||
import { createAutosaveController } from './services/autosaveService';
|
||||
import { createKeyboardShortcuts } from './services/keyboardShortcuts';
|
||||
import { getActiveDocument } from './kernel/document/documentService';
|
||||
import HistoryPanel from './components/HistoryPanel';
|
||||
import V2HistoryPanel from './components/V2HistoryPanel';
|
||||
import GuestPanel from './components/GuestPanel';
|
||||
@@ -178,8 +182,78 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
|
||||
// History panel
|
||||
const [historyPanelOpen, setHistoryPanelOpen] = useState(false);
|
||||
const [bomDialogOpen, setBomDialogOpen] = useState(false);
|
||||
const [guestPanelOpen, setGuestPanelOpen] = useState(false);
|
||||
|
||||
// ── Task CAD-9: BOM-Action ──
|
||||
useEffect(() => {
|
||||
if (!drawingId || !token) return;
|
||||
// noop: BOM-Action wird über handleRibbonAction verdrahtet (unten)
|
||||
}, [drawingId, token]);
|
||||
|
||||
// ── Task CAD-12a/12b: Autosave + Keyboard-Shortcuts ──
|
||||
useEffect(() => {
|
||||
if (!drawingId || !token) return;
|
||||
|
||||
// Autosave: Status im Topbar sichtbar machen (CAD-12a)
|
||||
const autosave = createAutosaveController({
|
||||
isConnected: () => collab.status === 'connected',
|
||||
hasUnsavedChanges: () => elements.length > 0,
|
||||
performFullSave: async () => {
|
||||
await Promise.all(
|
||||
elements.map((el) =>
|
||||
updateElement(token, el.id, el).catch(() => undefined),
|
||||
),
|
||||
);
|
||||
},
|
||||
onStatusChange: (s) => {
|
||||
if (s.state === 'saving') setSavedStatus('Speichert…');
|
||||
else if (s.state === 'saved') setSavedStatus('Autom. gespeichert');
|
||||
else if (s.state === 'offline') setSavedStatus('Offline');
|
||||
},
|
||||
});
|
||||
|
||||
// Keyboard-Shortcuts (CAD-12b)
|
||||
const ks = createKeyboardShortcuts({
|
||||
escape: () => setActiveTool('select'),
|
||||
delete: () => {
|
||||
const ids = selectedElementIdsRef.current;
|
||||
if (ids.length > 0) handleElementsDeleted(ids);
|
||||
},
|
||||
selectAll: () => {
|
||||
setSelectedElementIds(elements.map((el) => el.id));
|
||||
},
|
||||
duplicate: () => {
|
||||
const ids = selectedElementIdsRef.current;
|
||||
if (ids.length > 0) {
|
||||
elements
|
||||
.filter((el) => ids.includes(el.id))
|
||||
.forEach((el) => {
|
||||
const copy = {
|
||||
...el,
|
||||
id: `el_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
||||
x: el.x + 20,
|
||||
y: el.y + 20,
|
||||
} as typeof el;
|
||||
collab.setElement(copy);
|
||||
});
|
||||
}
|
||||
},
|
||||
toggleGrid: () => setGridEnabled((g) => !g),
|
||||
toggleOrtho: () => setOrthoEnabled((o) => !o),
|
||||
toggleSnap: () => setSnapEnabled((s) => !s),
|
||||
});
|
||||
|
||||
const keyHandler = (e: KeyboardEvent) => ks.handler(e);
|
||||
document.addEventListener('keydown', keyHandler);
|
||||
|
||||
return () => {
|
||||
autosave.destroy();
|
||||
document.removeEventListener('keydown', keyHandler);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [drawingId, token, collab.status]);
|
||||
|
||||
// Export format selector (cross-browser replacement for nwsave)
|
||||
const [exportFormatOpen, setExportFormatOpen] = useState(false);
|
||||
|
||||
@@ -1164,6 +1238,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
|
||||
// ─── Background ───
|
||||
if (action === 'background-import') { setBgImportOpen(true); return; }
|
||||
if (action === 'bom') { setBomDialogOpen(true); return; }
|
||||
|
||||
// ─── KI actions ───
|
||||
if (action === 'ki') { setActiveRightPanel('ki'); setCommandHistory((prev) => [...prev, { prefix: '·', text: 'KI Copilot geöffnet', type: 'info' }]); return; }
|
||||
@@ -1749,6 +1824,12 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
onApply={handleBgApply}
|
||||
backgroundService={bgServiceRef.current}
|
||||
/>
|
||||
{bomDialogOpen && getActiveDocument() && (
|
||||
<BOMDialog
|
||||
elements={getActiveDocument()!.getAllElements()}
|
||||
onClose={() => setBomDialogOpen(false)}
|
||||
/>
|
||||
)}
|
||||
{historyPanelOpen && (V2_TOOLS_ENABLED ? (
|
||||
<V2HistoryPanel
|
||||
onClose={() => setHistoryPanelOpen(false)}
|
||||
|
||||
Reference in New Issue
Block a user