Persist circuit cell edits

This commit is contained in:
2026-07-25 20:26:45 +02:00
parent 3977a6e6e1
commit 76d8d59391
24 changed files with 348 additions and 301 deletions
+12 -9
View File
@@ -5,9 +5,9 @@
The circuit-first editor uses tree, circuit, row and project-device endpoints.
All paths below are mounted below `/api`. There is no Consumer application API.
Project responses from `GET /projects` and `GET /projects/:projectId` include
`currentRevision`. Versioned project commands use this value for optimistic
concurrency checks.
Project responses from `GET /projects`, `GET /projects/:projectId` and the
circuit-tree endpoint include `currentRevision`. Versioned project commands use
this value for optimistic concurrency checks.
### Project Commands and History
@@ -35,8 +35,9 @@ with stable ids; delete commands include the expected parent identity. Circuit
snapshots contain zero, one or multiple complete device rows. Move commands
contain each row's expected and target circuit plus its exact expected and
target sort order. This makes deletion and moves undoable without generating
replacement identities or renumbering circuits. The editor does not consume
these endpoints yet.
replacement identities or renumbering circuits. Existing Circuit and
CircuitDeviceRow cell edits in the editor consume these endpoints; the
remaining structural editor writes are still being migrated.
`circuit-device-row.move` targets existing circuits in the same circuit list.
`circuit-device-row.move-with-new-circuit` atomically creates exactly one
@@ -137,6 +138,7 @@ Response sketch:
```json
{
"circuitListId": "cl_1",
"currentRevision": 12,
"sections": [
{
"id": "sec_1",
@@ -167,8 +169,6 @@ Response sketch:
- create circuit in list/section
- `POST /projects/:projectId/circuit-lists/:circuitListId/circuits-with-device-rows`
- atomically create one circuit and one or more initial device rows
- `PATCH /circuits/:circuitId`
- update circuit-level fields (BMK, protection, cable, reserve flag, etc.)
- `DELETE /circuits/:circuitId`
- delete circuit (and related rows via DB relations)
- `GET /circuit-sections/:sectionId/next-identifier`
@@ -189,11 +189,14 @@ Request sketch (`POST .../circuits`):
- `POST /circuits/:circuitId/device-rows`
- create row inside a circuit
- `PATCH /circuit-device-rows/:rowId`
- update row values
- `DELETE /circuit-device-rows/:rowId`
- delete row
Circuit and device-row field updates are available only as versioned
`circuit.update` and `circuit-device-row.update` commands through
`POST /projects/:projectId/commands`. There are no direct field-update PATCH
routes.
### Move Device Row
- `PATCH /circuit-device-rows/:rowId/move`
+12 -4
View File
@@ -11,7 +11,10 @@ Inline cells are static text by default. A cell enters edit mode by:
`Enter` confirms changes. `Escape` cancels current edit draft. `Tab` and `Shift+Tab` confirm and move to next/previous editable cell.
Committed cell edits are persisted as partial database updates. Fields that were not part of the edit are not written back from an older read and therefore cannot overwrite a concurrent change to another field.
Committed cells on existing circuits and device rows are persisted as typed
project commands with an optimistic project revision. Only the edited fields
are included, so an older tree response cannot write unrelated fields back.
The server records the inverse in the same transaction.
## `selectedCell` vs `editingCell`
@@ -119,7 +122,11 @@ Cross-section device moves show confirmation-required feedback before drop. The
## Undo/Redo
Undo/redo wraps editor operations as command objects with async `redo`/`undo` and reloads tree after each command.
Undo/redo wraps editor operations as command objects and reloads the tree after
each command. Existing Circuit and device-row cell edits execute persistent
project commands; their toolbar undo/redo calls the project-wide server
history. Other covered operations still use their existing direct API writes
and session-local inverse callbacks during the cutover.
Covered operations include:
@@ -132,5 +139,6 @@ Covered operations include:
Current limitations:
- session-local only
- no persisted history across browser reload
- the visible editor stack is still session-local and is empty after reload
- structural and destructive editor actions are not all connected to the
persistent command boundary yet
@@ -1,9 +1,12 @@
# Circuit List Editor Known Limitations
- The circuit-list editor still exposes only its session-local undo/redo UI.
- Existing Circuit and CircuitDeviceRow cell edits use persistent project
commands and project-wide toolbar undo/redo. The editor's visible stack is
still session-local, is empty after reload and still contains direct
structural/destructive operations during the cutover.
- Server-side project revisions and persistent undo/redo eligibility exist for
the command types documented in `circuit-list-editor-api.md`; command coverage
and the editor UI cutover are not complete.
the command types documented in `circuit-list-editor-api.md`; complete editor
command coverage and history reconstruction are not complete.
- Named logical project snapshots and restore are not implemented yet.
- No Revit/CSV/IFCGUID import and export workflow is implemented yet.
- Persistence currently targets local SQLite; PostgreSQL is an architectural option, not an implemented runtime.
+12 -5
View File
@@ -41,7 +41,7 @@ liegt über einen Host-Mount außerhalb des Containers.
- `src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx`
unterstützte Editorroute
- `src/frontend/components/circuit-tree-editor.tsx` Editorzustand,
Befehlsausführung, Drag-and-drop und sitzungslokales Undo/Redo
Befehlsausführung, Drag-and-drop und der schrittweise Historien-Cutover
- `src/frontend/components/circuit-grid-*.ts` reine Grid-Projektion,
Zellbesitz, Einfügen und Sicherheitsregeln
- `src/frontend/utils/api.ts` typisierte Frontend-API-Aufrufe
@@ -62,7 +62,8 @@ liegt über einen Host-Mount außerhalb des Containers.
6. Das Frontend lädt den Circuit-Tree neu und stellt Auswahl beziehungsweise
Viewport soweit möglich wieder her.
Die React-Historie ist derzeit sitzungslokal. Das Datenmodell besitzt bereits
Der sichtbare React-Historiestapel ist während des schrittweisen Cutovers noch
sitzungslokal. Das Datenmodell besitzt
einen projektbezogenen Revisionszähler sowie getrennte Revision-/Change-Set-
Tabellen. Ein getestetes Repository kann diese Historienmetadaten optimistisch
und atomar fortschreiben. Vorwärts- und Rückwärtskommandos besitzen einen
@@ -85,8 +86,13 @@ inversen Kommando gesichert, sodass Undo dieselbe UUID und alle Fachwerte
wiederherstellt. `circuit.insert` und `circuit.delete` behandeln einen
Stromkreis mit null, einer oder mehreren Gerätezeilen als vollständigen Block.
Undo bewahrt dabei sämtliche Circuit-/Row-UUIDs und ändert keine
Betriebsmittelkennzeichen. Das Grid verwendet diese Endpunkte noch nicht; seine
sichtbare Historie bleibt daher sitzungslokal.
Betriebsmittelkennzeichen. Bestehende Circuit- und Gerätezeilen-Zelländerungen
verwendet das Grid bereits über die öffentliche Command-Grenze. Der Tree liefert
dazu `currentRevision`; Undo/Redo für diese Zellaktionen läuft über die
projektweite Serverhistorie. Die direkten PATCH-Endpunkte für diese Felder sind
entfernt. Andere Editoraktionen verbleiben vorerst im sichtbaren
sitzungslokalen Stack; nach einem Reload wird dieser noch nicht aus der
Serverhistorie rekonstruiert.
`circuit-device-row.move` verschiebt oder sortiert eine oder mehrere Zeilen
zwischen vorhandenen Stromkreisen derselben Liste. Erwartete und neue
Stromkreis-/Sortierpositionen machen Forward und Inverse deterministisch;
@@ -159,7 +165,8 @@ bei einem späteren Wechsel trotzdem einen eigenen PostgreSQL-Adapter.
## Noch nicht unterstützt
- persistentes Undo/Redo und Projektversionen
- vollständiges persistentes Undo/Redo, sichtbare Historie nach Reload und
benannte Projektstände
- Mehrbenutzerbetrieb und Konfliktauflösung
- Revit-/CSV-/IFCGUID-Round-trip
- vollständige elektrische Dimensionierung
@@ -173,6 +173,11 @@ Completed foundation:
to SQLite
- public command, undo and redo endpoints require an expected revision; undo
and redo reconstruct the eligible persisted inverse or forward command
- the circuit tree returns the project's current revision; existing Circuit
and CircuitDeviceRow cell edits use the public command endpoint, while their
toolbar undo/redo actions use project-wide persistent history
- obsolete direct Circuit and CircuitDeviceRow field-update PATCH routes are
removed so these editor writes cannot bypass revision history
- `circuit-device-row.insert` and `circuit-device-row.delete` preserve stable
row ids, complete deleted-row snapshots and circuit reserve state through
atomic insert/delete, revision and stack transitions
@@ -210,10 +215,10 @@ Remaining constraints before completing persistent history:
is complete
- route the remaining structural and destructive domain
writes through the shared command, revision and stack transaction boundary
- replace the session-local frontend command stack only after server-side
command coverage is complete for structural and destructive circuit-editor
operations; the ProjectDevice synchronization UI already uses its persistent
command and project-wide undo endpoint
- finish routing structural and destructive circuit-editor operations through
persistent commands, then reconstruct the visible frontend history state
after reload; Circuit/CircuitDeviceRow cell edits and ProjectDevice
synchronization already use persistent commands and project-wide undo
- do not model IFCGUID as an overloaded circuit equipment identifier
- keep database backup/restore checks separate from project history tests
+8 -4
View File
@@ -390,6 +390,9 @@ Implemented foundation:
- a central application dispatcher reconstructs persisted commands and exposes
optimistic command, undo and redo endpoints for Circuit and CircuitDeviceRow
field updates
- the circuit tree exposes `currentRevision`; existing Circuit and
CircuitDeviceRow cell edits use those optimistic commands and project-wide
undo/redo instead of direct field-update PATCH routes
- typed CircuitDeviceRow insert/delete commands preserve complete row snapshots
and stable ids; row mutation, circuit reserve state, revision and history
stack transition commit or roll back together
@@ -424,10 +427,11 @@ Implemented foundation:
- integration coverage verifies sequential revisions, stale-command rejection
and rollback after a forced late persistence failure
ProjectDevice CRUD, synchronization and disconnect on the project page are
connected to this history boundary. The circuit-list editor and the remaining
domain writes are not fully connected; its visible undo/redo therefore remains
session-local until the remaining tasks are implemented.
ProjectDevice CRUD, synchronization and disconnect on the project page and
existing Circuit/CircuitDeviceRow cell edits in the circuit-list editor are
connected to this history boundary. Remaining structural editor writes are not
fully connected; the editor's visible undo/redo stack therefore remains
session-local and is not reconstructed after reload.
Acceptance criteria: