Files
tutortool/conductor/room-editor-refactor-core.md
s0wlz (Matthias Puchstein) 08cb668bab
All checks were successful
Release / release (push) Successful in 7m12s
fix: restore login page accessibility and wire silent token refresh
The admin layout guard rendered only a "Redirecting to login..." placeholder
for the /admin/login child route, trapping every unauthenticated visitor.
Exempt the login route from the auth gate so the form renders correctly.

Also wire the new POST /api/auth/refresh endpoint (from the dual-token
migration) into both auth.init() and the api request() 401 handler, so
sessions survive the 15-minute access-token lifetime without a hard logout.

Adds a Playwright regression test asserting the login form is visible
in a clean (no-cookie) browser context.
2026-05-04 04:19:42 +02:00

6.2 KiB
Raw Permalink Blame History

Implementation Plan: Room Editor Refactor (Core & Logic)

Objective: Fix the pixel vs. grid-unit mismatch in stored room data, and robustify the editor for professional room planning.

Background: The editor (RoomCanvas.svelte) already stores and renders in grid units (1 unit = 40 px). However, the demo seed (demo_seed.sql) was written with raw pixel values (e.g. width: 200), causing demo Room A to render broken (200 grid units = 8000 px). Any room created via the editor since launch is correct; any room predating the editor's grid-unit switch (or the demo room) is broken. A one-time data migration is the first priority.

Note on the type/kind field: backend/src/models.rs:81 already bridges this with #[serde(rename = "type")] pub kind: String. The wire format is type and frontend/src/lib/types.ts:33 already uses type. No rename is needed. If the Rust internal name is ever changed to type, a raw identifier (r#type) is required since type is reserved.

Note on backend validation: backend/src/routes/rooms.rs:1869 already implements validate_layout with empty-layout check, unique IDs, allowed types (seat, table, gap, door), unique seat labels, and non-negative geometry. Tests at lines 184322 cover all of it. Task 2 below replaces the previously planned duplicate work.

Note on SeatMap.svelte: This plan does not touch SeatMap.svelte. Its retirement and replacement with a dynamic renderer is handled by the sibling visualization plan. Any LayoutElement contract change made here must be cross-checked against backend/src/routes/checkin.rs:53,194 (which deserialises it) and frontend/src/lib/types.ts:86 (CheckinInfo.layout).


1. Data Migration & Seed Fix

Task 1: Pixel → Grid-Unit Migration

Files to Modify:

  • backend/migrations/003_normalize_room_layout_units.sql (create)
  • backend/demo/demo_seed.sql
  • backend/src/routes/rooms.rs (update tests that assert large numeric coordinates)

Changes:

  • Write 003_normalize_room_layout_units.sql. For each row in rooms, parse layout_json; if any element has x, y, width, or height > 50, divide all four by 40 and update the row. This heuristic is safe because grid-unit values are small integers/half-steps (max ~30), while pixel values are large (typically 80800).
  • Update demo_seed.sql:1641 to use grid units (e.g. width: 200width: 5). The 24 elements in demo Room A need to be re-measured in grid units.
  • Update any integration tests in rooms.rs that rely on large pixel-scale layout values.

Task 2: Backend Validation (Scope Reduction)

Files to Modify:

  • backend/src/routes/rooms.rs

Changes (additive only — do not duplicate existing logic):

  • Add upper-bound validation: x and y must be < a MAX_CANVAS constant (e.g. 100 grid units). Reject elements that fall off the canvas.
  • Add grid-step validation: x, y, width, height must be multiples of 0.5 (i.e. (value * 2) % 1 == 0). Apply post-migration so existing data has already been normalised.
  • Add a test for each new validator.

2. Editor Core Refactor

Task 3: RoomCanvas State & Behaviour

Files to Modify:

  • frontend/src/lib/RoomCanvas.svelte

Current state (188 lines):

  • Drag: draggingId / startX / startY only (lines 2628). No resize state or handles exist.
  • Snap: lines 4748 snap to 0.25 grid units (Math.round(.../10)*10/40). This is partially correct but the increment should be configurable (0.5 default).
  • Rendering: already multiplies by GRID_SIZE = 40 (line 85). Unit separation is mostly correct.
  • Bug: onmousemove / onmouseup are bound on the SVG only (lines 6971). Releasing the cursor outside the SVG strands the drag. Move these listeners to window for the duration of a drag.

Changes:

  • Build resize from scratch. Add resize handles (e.g. bottom-right corner hit area) per element. Track resizingId, resizeStartX, resizeStartY, resizeStartW, resizeStartH as drag state. Snap resize delta to 0.5 increments.
  • Fix drag escape. Bind mousemove/mouseup to window when dragging begins; remove them on drop.
  • Snap increment. Change snap to 0.5 grid units (from 0.25). Accept an optional snapStep prop (default 0.5) for the snap-toggle feature below.

Task 4: Editor UI Improvements

Files to Modify:

  • frontend/src/routes/admin/rooms/[roomId]/+page.svelte

What already exists (do not re-add):

  • Width/height inputs with step="0.5" (lines 9097)
  • Label input (line 87)
  • Add seat/table/door buttons (lines 6466)
  • Delete button (line 101)

What to add:

  • X/Y numeric inputs (with step="0.5") for precise coordinate editing of the selected element, bound to its x and y fields.
  • "+ Gap" button alongside the existing add buttons. gap is accepted by validate_layout but is currently unreachable from the UI.
  • "Snap to Grid" toggle. Bind to a boolean state; pass as snapStep={snapEnabled ? 0.5 : 0} to RoomCanvas.
  • "Duplicate element" button. Copies the selected element with a new UUID and offsets it by 1 grid unit.
  • Surface save errors. saveLayout (lines 2729) currently only console.error. Display an inline error message in the UI.

3. Verification

Automated Tests:

  • backend/src/routes/rooms.rs: Tests for the new upper-bound and grid-step validators.
  • backend/migrations/: Verify migration 003 runs cleanly on the test DB (use sqlx migrate run).
  • frontend/tests/rooms.spec.ts (new): Playwright test — create a room, add table and two seats via the UI, drag a seat (verify snap), save and reload, assert coordinates are preserved.

Manual Verification:

  1. make seed-demo — reseed with the fixed demo_seed.sql.
  2. Open Admin → Rooms → Room A in the editor. All elements must appear at sensible grid positions (not far off-screen).
  3. Drag an element: verify it snaps to 0.5-unit increments.
  4. Resize an element: verify handles appear and snap correctly.
  5. Add a Gap element and verify it can be placed and saved.
  6. Inspect the SQLite DB directly: SELECT layout_json FROM rooms LIMIT 1. All element coordinates must be small numbers (≤ 30), not pixel values (≥ 80).
  7. Save and reload: verify coordinates are exactly preserved (no rounding drift).