diff --git a/ROADMAP.md b/ROADMAP.md index ce4cca9..13e2df7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -718,6 +718,7 @@ Format: `YYYY-MM-DD | TASK-ID | was getan | Tests Ergebnis | commit | Notizen/of | Datum | Task | Zusammenfassung | Tests | Commit | Notizen | |---|---|---|---|---|---| +| 2026-09-16 | CAD-14 Phase 4a+4b | WERKZEUG-OPTIONEN + SIDEBAR-ZUGANG: (1) KARTIERUNGSFUND: Der InteractionDispatcher bekam KEIN getOptions — V2-Werkzeuge erhielten IMMER leere Optionen; chordCount/Laenge/Breite/Winkel liefen still ins Leere (NUR in bomService referenziert, keine UI, kein Store) — die in CAD-8/11 gemeldeten einstellbaren Optionen waren im V2-Pfad nicht erreichbar; (2) PHASE 4a: toolOptionsService NEU (zentraler Store: set/get/getOptions, defaultsFromSchema, subscribe, reset — 6 Tests), Dispatcher-Deps.getOptions verdrahtet, Registry-Defaults beim Mount aus optionsSchemas gefuellt, ToolOptionsPanel NEU (rendert optionsSchema des aktiven V2-Werkzeugs: number/color/select/boolean, schreibt in den Store) in RightSidebar tool-Tab ueber PropertiesPanel; (3) PHASE 4b: LeftSidebar dynamische Sektion Werkzeuge (Plugins) NEU — ALLE Registry-Werkzeuge ohne statischen Button (truss, boxcorner, circle-truss, measure-area, block-edit...) sind wieder erreichbar nach dem Ribbon-Rueckbau (Funktionsverlust behoben), ein Ort fuer ALLE Werkzeuge; (4) BEWEIS-TEST erweitert um ABNAHME 4: chordCount=3 im Options-Panel setzen, Traverse ziehen, properties_json in DB muss chordCount=3 enthalten — GRUEN; Test-Lesefehler behoben: rohe DB-API liefert properties_json (String, listElements = SELECT *) statt properties; Timeout 120s | Frontend: 899 Tests (+6 toolOptionsService), tsc 0, Build 0, Beweis-Test GRUEN mit 4 Abnahmen | 2a05257, d5acf76 | PHASE 4a+4b COMPLETE: Optionen erreichen die Werkzeuge, alle Tools in der Sidebar erreichbar | | 2026-09-16 | CAD-14 Phase 3 | V2-SELECT + APP-KANAL: selectTool-Plugin NEU (core-drawing): Klick waehlt via Bridge-hitTest, Leerklick leert Auswahl, Shift additiv, Drag = Box-Select (Zentrum-in-BBox ueber doc.getAllElements), gestrichelte Box-Vorschau; ToolSelectionBridge erweitert um onChange (App-Kanal); CanvasArea: Bridge-onChange — onSelectionChangeRef (Stale-Closure-Sicher wie onElementCreatedRef), Pixi-Handles-Hook bleibt an selectByIds; Beweis-Test erweitert um ABNAHME 3: V2-Select klickt auf die gezeichnete Linie — __v2GetSelection meldet genau 1 selektiert; Test-Timeout 60s; Verbleibend Phase 4: Legacy-InteractionEngine-Code loeschen (1173 Zeilen, nach Layer-Panel-Anpassung) | Frontend: 893 Tests (+6 selectToolV2), tsc 0, Build 0, Beweis-Test GRUEN (3 Abnahmen: Pixi=1, DB=1, Auswahl=1) | 1d12904 | PHASE 3 COMPLETE | | 2026-09-16 | CAD-14 Phase 1+2 | EINSYSTEM-SCHNITT (Nutzer-Anweisung: "Natuerlich die neue Engine, bau den alten Scheiss aus"): KOMPLETTE KARTIERUNG vorab (das fehlte zuvor): (1) SMOKING GUN im Code: Tool-Sync gab BEIDEN Engines dasselbe Werkzeug (Kommentar: "Legacy bleibt parallel aktiv") — jeder Klick lief doppelt, auf zwei Canvas mit unterschiedlichem Größen/DPR-Handling — doppelte Elemente UND Positionsdrift; (2) Beweis-Test E2E (playwright-tests/one-engine-proof.test.ts): echtes Drag-Protokoll, misst Pixi-cachedElements UND DB — ROT-Zustand belegte 4 Elemente statt 1; (3) PHASE 1 Exklusivitaet: V2-Dispatcher exklusiv fuer V2-Werkzeuge, Legacy-Engine bekommt setTool(null) (ToolState.activeTool null-faehig), Doppelverarbeitung am Ursprung getoetet — Messung danach: 2 statt 4 (zweite Quelle: Pixi-Doppelpfad); (4) PHASE 2 Stale-Closure-Fix: onElementCreatedRef (Ref-Muster wie selectedElementIdsRef) — der einmal registrierte v2Doc.onChanged-Handler hielt eine alte drawingId-Closure (still kein DB-Save); (5) Debug-Expose __v2Debug.docCount/announcedCount: Kette live gemessen doc=1 — ann=1 — cached=1 — DB=1 VOLLSTAENDIG; (6) ABNAHME-TEST GRUEN: echtes Drag erzeugt GENAU 1 Element im Renderer UND genau 1 in der DB (frisches Projekt+Zeichnung per API, eindeutige Zuordnung). Verbleibend Phase 3: Legacy-InteractionEngine-Code loeschen (1173 Zeilen), V2-Select-Tool (Auswahl direkt auf V2-Dokument), Layer-Panel an v2Doc | Frontend: 887 Tests, tsc 0, Build 0, E2E-Beweis GRUEN | 02ed6a5, f9c67a7 | PHASE 1+2 COMPLETE: Zeichnen = 1 Element an Klick-Position, in DB persistiert | | 2026-09-16 | CAD-13-ui | DOPPELTE WERKZEUGE entfernt (Nutzer-Meldung: "Werkzeuge doppelt oben, die sind schon in der Sidebar"): Der CAD-10-Zeichnen-Tab im Ribbon war Redundanz zur linken Werkzeug-Sidebar. FIX: Zeichnen-Tab komplett aus Ribbon entfernt (tabs-Array, renderV2Group, V2_GROUP_LABELS, render-Block — 87 Zeilen geloescht), RibbonTab-Typ auf start/view/ki, Stueckliste-Button in Ansicht-Tab-Extras verschoben, uiMode-Tests auf Registry-Filterlogik verlagert (Sidebar ist statisch, Filter lebt in pluginRegistry-tags). LIVE VERIFIZIERT: Ribbon-Tags = [Start, Ansicht, KI], Werkzeuge nur noch in der Sidebar wie in echten CADs (AutoCAD-Muster), visuell bestaetigt | Frontend: 884 Tests, tsc 0, Build 0 | 6857573 | LIVE VERIFIZIERT: keine Doppelung mehr | diff --git a/frontend/playwright-tests/one-engine-proof.test.ts b/frontend/playwright-tests/one-engine-proof.test.ts index fae3632..3fbffb5 100644 --- a/frontend/playwright-tests/one-engine-proof.test.ts +++ b/frontend/playwright-tests/one-engine-proof.test.ts @@ -23,7 +23,7 @@ interface VerifyWindow { } test('zeichnen erzeugt GENAU EIN Element an der Klick-Position', async ({ page }) => { - test.setTimeout(60000); + test.setTimeout(120000); await page.goto(BASE_URL); await page.fill('input[type=email]', 'admin@media-on.de'); await page.fill('input[type=password]', 'WebCAD2026!'); @@ -106,12 +106,19 @@ test('zeichnen erzeugt GENAU EIN Element an der Klick-Position', async ({ page } const drs = await (await fetch(`${BASE_URL}/api/projects/${proj.id}/drawings`, { headers: { Authorization: `Bearer ${token}` } })).json(); let total = 0; for (const d of drs) { - const els = await (await fetch(`${BASE_URL}/api/drawings/${d.id}/elements`, { headers: { Authorization: `Bearer ${token}` } })).json(); - total += els.length; - if (els.length > 0) console.log(`DRAWING ${d.id}: ${els.length} Elemente`); + const raw = await (await fetch(`${BASE_URL}/api/drawings/${d.id}/elements`, { headers: { Authorization: `Bearer ${token}` } })).json(); + total += raw.length; + // CAD-14: rohe DB-Zeile hat properties_json (String) — parse und + // melde Elemente mit chordCount (fuer die Options-Abnahme unten). + for (const e of raw) { + const parsed = typeof e.properties_json === 'string' ? JSON.parse(e.properties_json) : (e.properties_json ?? {}); + if (parsed && parsed.chordCount !== undefined) { + console.log(`ELEMENT MIT CHORDCOUNT in ${d.id}:`, parsed.chordCount); + } + } } dbCount = total; - if (total >= 1) { console.log('GEFUNDEN in drawings:', drs.map((d: any) => d.id)); break; } + if (total >= 1) break; } expect(dbCount, `Genau 1 Element muss in der DB landen (vor ${beforeDbCount}, nach ${dbCount})`).toBe(1); @@ -129,4 +136,49 @@ test('zeichnen erzeugt GENAU EIN Element an der Klick-Position', async ({ page } }); expect(sel, 'Selection-Bridge muss erreichbar sein').toBeTruthy(); expect(sel!.ids.length, `Genau 1 Element muss ausgewaehlt sein, got ${sel!.ids.length}`).toBe(1); + + // ABNAHME 4: Tool-Optionen erreichen das Werkzeug (Phase 4a). + // Traversen-Werkzeug waehlen, chordCount=3 im Options-Panel setzen, + // Traverse ziehen, properties.chordCount muss 3 sein. + await page.locator('[data-v2tool="truss"], [data-tool="truss"]').first().click(); + await page.waitForTimeout(300); + const chordInput = page.locator('.tool-options-panel input[type=number]').first(); + // chordCount-Feld ueber Label finden (Gurtrohre) + const chordRow = page.locator('.toolopt-row', { hasText: 'Gurtrohre' }); + await chordRow.locator('input').fill('3'); + await page.waitForTimeout(300); + // Traverse ziehen (wie Linie) + const tStart = cx - 160; + const tStartY = cy + 120; + await page.mouse.move(tStart, tStartY); + await page.mouse.down(); + await page.mouse.move(tStart + 150, tStartY, { steps: 6 }); + await page.mouse.up(); + await page.waitForTimeout(800); + const trussEl = await page.evaluate(() => { + const w = window as unknown as { __cad14Truss?: { id: string; chordCount: number } | null }; + return w.__cad14Truss ?? null; + }); + // Alternativ: ueber die DB pruefen (Autosave-Poll) — rohe DB-Zeile hat + // properties_json (String), nicht properties. + let chordInDb: unknown = null; + for (let i = 0; i < 8; i++) { + await page.waitForTimeout(5000); + const drs = await (await fetch(`${BASE_URL}/api/projects/${proj.id}/drawings`, { headers: { Authorization: `Bearer ${token}` } })).json(); + let found = false; + for (const d of drs) { + const raw = await (await fetch(`${BASE_URL}/api/drawings/${d.id}/elements`, { headers: { Authorization: `Bearer ${token}` } })).json(); + for (const e of raw) { + const parsed = typeof e.properties_json === 'string' ? JSON.parse(e.properties_json) : (e.properties_json ?? {}); + if (String(e.type) === 'truss' && (parsed as any)?.chordCount === 3) { + chordInDb = parsed.chordCount; + found = true; + break; + } + } + if (found) break; + } + if (chordInDb !== null) break; + } + expect(String(chordInDb), 'chordCount=3 muss beim gezeichneten Element ankommen').toBe('3'); });