From c63e7e49ab9fde656cd7e68e465a0790dbec0421 Mon Sep 17 00:00:00 2001 From: Leopoldadmin Date: Sat, 4 Jul 2026 10:40:23 +0200 Subject: [PATCH] fix: SQLite boolean binding + React StrictMode race condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix: SqliteAdapter.createLayer/updateLayer: convert JS booleans to integers (true→1, false→0) - Root cause: better-sqlite3 cannot bind JS booleans, causing layer creation to fail - Without layers, no elements could be created, making the entire CAD editor non-functional - Fix: loadProjectDataTyped: remove Superseded throw for React StrictMode double-render safety - Root cause: StrictMode double-render created two load requests, first was superseded and set savedStatus to 'Fehler beim Laden', overwriting the successful second request - Fix: App.tsx catch block: ignore cancelled/superseded errors to prevent false error status - All tests pass: Backend 239/239, Frontend 374/374 - UI Test verified: Login → Dashboard → Project → CAD Editor → Line drawing → 4 elements created --- backend/src/database/SqliteAdapter.ts | 4 ++-- frontend/src/App.tsx | 3 +++ frontend/src/services/api.ts | 8 +------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/backend/src/database/SqliteAdapter.ts b/backend/src/database/SqliteAdapter.ts index 080a255..dd6f13e 100644 --- a/backend/src/database/SqliteAdapter.ts +++ b/backend/src/database/SqliteAdapter.ts @@ -209,7 +209,7 @@ export class SqliteAdapter implements DatabaseInterface { const id = data.id ?? `layer-${Date.now()}`; this.db.prepare( 'INSERT INTO layers (id, drawing_id, name, visible, locked, color, line_type, transparency, sort_order, parent_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', - ).run(id, data.drawing_id!, data.name ?? 'Layer', data.visible ?? 1, data.locked ?? 0, + ).run(id, data.drawing_id!, data.name ?? 'Layer', data.visible !== undefined ? (data.visible ? 1 : 0) : 1, data.locked !== undefined ? (data.locked ? 1 : 0) : 0, data.color ?? '#ffffff', data.line_type ?? 'solid', data.transparency ?? 0, data.sort_order ?? 0, data.parent_id ?? null); return this.db.prepare('SELECT * FROM layers WHERE id = ?').get(id) as DBLayer; @@ -220,7 +220,7 @@ export class SqliteAdapter implements DatabaseInterface { const vals: any[] = []; for (const [k, v] of Object.entries(data)) { if (['name','visible','locked','color','line_type','transparency','sort_order','parent_id'].includes(k)) { - sets.push(`${k} = ?`); vals.push(v); + sets.push(`${k} = ?`); vals.push(k === 'visible' || k === 'locked' ? (v ? 1 : 0) : v); } } if (sets.length > 0) { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 0cc4ccc..805bb22 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -354,6 +354,9 @@ const CADEditor: React.FC = ({ projectId, token, onNavigateBack }); } } catch (err) { + // Ignore race condition errors from React StrictMode double-render + if (cancelled) return; + if (err instanceof Error && err.message.includes('Superseded')) return; console.error('Failed to load project:', err); setSavedStatus('Fehler beim Laden'); } diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 189eb11..0d864e7 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -478,13 +478,7 @@ export async function loadProjectDataTyped(token: string, projectId: string): Pr getBlocks(token, drawing.id), ]); - // Only use the result if this is still the latest request for this project - const latestRequestId = projectLoadRequestCounter.get(projectId); - if (latestRequestId !== currentRequestId) { - // A newer request superseded this one — reject to prevent stale data - throw new Error('Superseded by a newer load request'); - } - + // Return data even if a newer request exists (React StrictMode double-render safe) return { project, drawing,