Add editable project floors and rooms

This commit is contained in:
2026-07-31 14:46:06 +02:00
parent 1e3c721cfa
commit 1eed19ef6b
15 changed files with 743 additions and 80 deletions
@@ -18,10 +18,19 @@ import { ProjectRevisionConflictError } from "../src/domain/errors/project-revis
import {
createProjectFloorInsertProjectCommand,
createProjectFloorSnapshot,
createProjectFloorUpdateProjectCommand,
createProjectRoomInsertProjectCommand,
createProjectRoomSnapshot,
createProjectRoomUpdateProjectCommand,
} from "../src/domain/models/project-location-structure-project-command.model.js";
import { createFloor, createRoom } from "../src/frontend/utils/api.js";
import {
createFloor,
createRoom,
deleteFloor,
deleteRoom,
updateFloor,
updateRoom,
} from "../src/frontend/utils/api.js";
import {
createFloorSchema,
createRoomSchema,
@@ -83,11 +92,12 @@ describe("project-location structure project command", () => {
false
);
const requests: Array<{ url: string; body: unknown }> = [];
const requests: Array<{ url: string; method: string; body: unknown }> = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = async (input, init) => {
requests.push({
url: String(input),
method: init?.method ?? "GET",
body: JSON.parse(String(init?.body)),
});
return new Response(JSON.stringify({}), {
@@ -106,16 +116,27 @@ describe("project-location structure project command", () => {
},
8
);
await updateFloor("project-1", "floor-1", { name: "1. OG" }, 9);
await updateRoom(
"project-1",
"room-1",
{ roomNumber: "101", roomName: "Büro" },
10
);
await deleteFloor("project-1", "floor-1", 11);
await deleteRoom("project-1", "room-1", 12);
} finally {
globalThis.fetch = originalFetch;
}
assert.deepEqual(requests, [
{
url: "/api/projects/project-1/floors",
method: "POST",
body: { name: "EG", expectedRevision: 7 },
},
{
url: "/api/projects/project-1/rooms",
method: "POST",
body: {
floorId: "floor-1",
roomNumber: "001",
@@ -123,9 +144,119 @@ describe("project-location structure project command", () => {
expectedRevision: 8,
},
},
{
url: "/api/projects/project-1/floors/floor-1",
method: "PUT",
body: { name: "1. OG", expectedRevision: 9 },
},
{
url: "/api/projects/project-1/rooms/room-1",
method: "PUT",
body: {
roomNumber: "101",
roomName: "Büro",
floorId: null,
expectedRevision: 10,
},
},
{
url: "/api/projects/project-1/floors/floor-1",
method: "DELETE",
body: { expectedRevision: 11 },
},
{
url: "/api/projects/project-1/rooms/room-1",
method: "DELETE",
body: { expectedRevision: 12 },
},
]);
});
it("updates floors and rooms with exact persisted undo and redo", () => {
const context = createTestDatabase();
try {
const repository = new ProjectLocationStructureProjectCommandRepository(context.db);
const history = new ProjectHistoryRepository(context.db);
const floor = createProjectFloorSnapshot("project-1", "EG", 0);
const room = createProjectRoomSnapshot("project-1", {
floorId: floor.id,
roomNumber: "001",
roomName: "Technik",
});
context.db.insert(floors).values(floor).run();
context.db.insert(rooms).values(room).run();
const updatedFloor = { ...floor, name: "Erdgeschoss" };
const floorCommand = createProjectFloorUpdateProjectCommand(floor, updatedFloor);
const floorResult = repository.execute({
projectId: "project-1",
expectedRevision: 0,
source: "user",
command: floorCommand,
});
const updatedRoom = {
...room,
floorId: null,
roomNumber: "002",
roomName: "Lager",
};
const roomCommand = createProjectRoomUpdateProjectCommand(room, updatedRoom);
const roomResult = repository.execute({
projectId: "project-1",
expectedRevision: 1,
source: "user",
command: roomCommand,
});
assert.equal(context.db.select().from(floors).where(eq(floors.id, floor.id)).get()?.name, "Erdgeschoss");
assert.deepEqual(context.db.select().from(rooms).where(eq(rooms.id, room.id)).get(), updatedRoom);
const undoRoom = history.getNextCommand("project-1", "undo");
assert.ok(undoRoom);
repository.execute({
projectId: "project-1",
expectedRevision: 2,
source: "undo",
historyTargetChangeSetId: undoRoom.changeSetId,
command: roomResult.inverse,
});
assert.deepEqual(context.db.select().from(rooms).where(eq(rooms.id, room.id)).get(), room);
const undoFloor = history.getNextCommand("project-1", "undo");
assert.ok(undoFloor);
repository.execute({
projectId: "project-1",
expectedRevision: 3,
source: "undo",
historyTargetChangeSetId: undoFloor.changeSetId,
command: floorResult.inverse,
});
assert.deepEqual(context.db.select().from(floors).where(eq(floors.id, floor.id)).get(), floor);
const redoFloor = history.getNextCommand("project-1", "redo");
assert.ok(redoFloor);
repository.execute({
projectId: "project-1",
expectedRevision: 4,
source: "redo",
historyTargetChangeSetId: redoFloor.changeSetId,
command: floorCommand,
});
const redoRoom = history.getNextCommand("project-1", "redo");
assert.ok(redoRoom);
repository.execute({
projectId: "project-1",
expectedRevision: 5,
source: "redo",
historyTargetChangeSetId: redoRoom.changeSetId,
command: roomCommand,
});
assert.deepEqual(context.db.select().from(floors).where(eq(floors.id, floor.id)).get(), updatedFloor);
assert.deepEqual(context.db.select().from(rooms).where(eq(rooms.id, room.id)).get(), updatedRoom);
} finally {
context.close();
}
});
it("creates a stable floor and supports persisted undo and redo", () => {
const context = createTestDatabase();
try {
@@ -218,7 +349,7 @@ describe("project-location structure project command", () => {
historyTargetChangeSetId: undoStep.changeSetId,
command: inserted.inverse,
}),
/assigned to a distribution board/
/Verteilung zugeordnet/
);
assert.equal(
context.db.select().from(projectRevisions).all().length,
@@ -416,7 +547,7 @@ describe("project-location structure project command", () => {
historyTargetChangeSetId: undoStep.changeSetId,
command: inserted.inverse,
}),
/assigned rooms/
/Räume zugeordnet/
);
} finally {
floorContext.close();