forked from jappel/leistungsbilanz-ts
Add optional room areas
This commit is contained in:
parent
a6500e2b26
commit
dce31ee143
26 changed files with 3669 additions and 78 deletions
|
|
@ -10,7 +10,7 @@ export const projectFloorUpdateCommandType = "project-floor.update" as const;
|
|||
export const projectRoomInsertCommandType = "project-room.insert" as const;
|
||||
export const projectRoomDeleteCommandType = "project-room.delete" as const;
|
||||
export const projectRoomUpdateCommandType = "project-room.update" as const;
|
||||
/** Room commands, building commands and legacy floor commands. */
|
||||
/** Building commands and legacy floor/room commands. */
|
||||
export const projectLocationStructureCommandSchemaVersion = 1 as const;
|
||||
/**
|
||||
* Floor commands carry `buildingId` since version 2. Version 1 floor
|
||||
|
|
@ -19,6 +19,9 @@ export const projectLocationStructureCommandSchemaVersion = 1 as const;
|
|||
*/
|
||||
export const projectFloorCommandSchemaVersion = 2 as const;
|
||||
const legacyProjectFloorCommandSchemaVersion = 1 as const;
|
||||
/** Room commands carry `areaM2` since version 2. */
|
||||
export const projectRoomCommandSchemaVersion = 2 as const;
|
||||
const legacyProjectRoomCommandSchemaVersion = 1 as const;
|
||||
|
||||
export interface ProjectBuildingSnapshot {
|
||||
id: string;
|
||||
|
|
@ -43,8 +46,11 @@ export interface ProjectRoomSnapshot {
|
|||
floorId: string | null;
|
||||
roomNumber: string;
|
||||
roomName: string;
|
||||
areaM2: number | null;
|
||||
}
|
||||
|
||||
export type LegacyProjectRoomSnapshot = Omit<ProjectRoomSnapshot, "areaM2">;
|
||||
|
||||
interface ProjectBuildingStructureCommandPayload {
|
||||
building: ProjectBuildingSnapshot;
|
||||
}
|
||||
|
|
@ -61,6 +67,10 @@ interface ProjectRoomStructureCommandPayload {
|
|||
room: ProjectRoomSnapshot;
|
||||
}
|
||||
|
||||
interface LegacyProjectRoomStructureCommandPayload {
|
||||
room: LegacyProjectRoomSnapshot;
|
||||
}
|
||||
|
||||
interface ProjectBuildingUpdateCommandPayload {
|
||||
expected: ProjectBuildingSnapshot;
|
||||
target: ProjectBuildingSnapshot;
|
||||
|
|
@ -81,6 +91,11 @@ interface ProjectRoomUpdateCommandPayload {
|
|||
target: ProjectRoomSnapshot;
|
||||
}
|
||||
|
||||
interface LegacyProjectRoomUpdateCommandPayload {
|
||||
expected: LegacyProjectRoomSnapshot;
|
||||
target: LegacyProjectRoomSnapshot;
|
||||
}
|
||||
|
||||
export interface ProjectBuildingInsertProjectCommand
|
||||
extends SerializedProjectCommand<ProjectBuildingStructureCommandPayload> {
|
||||
schemaVersion: typeof projectLocationStructureCommandSchemaVersion;
|
||||
|
|
@ -137,19 +152,37 @@ export interface LegacyProjectFloorUpdateProjectCommand
|
|||
|
||||
export interface ProjectRoomInsertProjectCommand
|
||||
extends SerializedProjectCommand<ProjectRoomStructureCommandPayload> {
|
||||
schemaVersion: typeof projectLocationStructureCommandSchemaVersion;
|
||||
schemaVersion: typeof projectRoomCommandSchemaVersion;
|
||||
type: typeof projectRoomInsertCommandType;
|
||||
}
|
||||
|
||||
export interface ProjectRoomDeleteProjectCommand
|
||||
extends SerializedProjectCommand<ProjectRoomStructureCommandPayload> {
|
||||
schemaVersion: typeof projectLocationStructureCommandSchemaVersion;
|
||||
schemaVersion: typeof projectRoomCommandSchemaVersion;
|
||||
type: typeof projectRoomDeleteCommandType;
|
||||
}
|
||||
|
||||
export interface ProjectRoomUpdateProjectCommand
|
||||
extends SerializedProjectCommand<ProjectRoomUpdateCommandPayload> {
|
||||
schemaVersion: typeof projectLocationStructureCommandSchemaVersion;
|
||||
schemaVersion: typeof projectRoomCommandSchemaVersion;
|
||||
type: typeof projectRoomUpdateCommandType;
|
||||
}
|
||||
|
||||
export interface LegacyProjectRoomInsertProjectCommand
|
||||
extends SerializedProjectCommand<LegacyProjectRoomStructureCommandPayload> {
|
||||
schemaVersion: typeof legacyProjectRoomCommandSchemaVersion;
|
||||
type: typeof projectRoomInsertCommandType;
|
||||
}
|
||||
|
||||
export interface LegacyProjectRoomDeleteProjectCommand
|
||||
extends SerializedProjectCommand<LegacyProjectRoomStructureCommandPayload> {
|
||||
schemaVersion: typeof legacyProjectRoomCommandSchemaVersion;
|
||||
type: typeof projectRoomDeleteCommandType;
|
||||
}
|
||||
|
||||
export interface LegacyProjectRoomUpdateProjectCommand
|
||||
extends SerializedProjectCommand<LegacyProjectRoomUpdateCommandPayload> {
|
||||
schemaVersion: typeof legacyProjectRoomCommandSchemaVersion;
|
||||
type: typeof projectRoomUpdateCommandType;
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +198,10 @@ export type ProjectLocationStructureProjectCommand =
|
|||
| LegacyProjectFloorUpdateProjectCommand
|
||||
| ProjectRoomInsertProjectCommand
|
||||
| ProjectRoomDeleteProjectCommand
|
||||
| ProjectRoomUpdateProjectCommand;
|
||||
| ProjectRoomUpdateProjectCommand
|
||||
| LegacyProjectRoomInsertProjectCommand
|
||||
| LegacyProjectRoomDeleteProjectCommand
|
||||
| LegacyProjectRoomUpdateProjectCommand;
|
||||
|
||||
export function createProjectBuildingSnapshot(
|
||||
projectId: string,
|
||||
|
|
@ -205,6 +241,7 @@ export function createProjectRoomSnapshot(
|
|||
floorId?: string;
|
||||
roomNumber: string;
|
||||
roomName: string;
|
||||
areaM2?: number | null;
|
||||
}
|
||||
): ProjectRoomSnapshot {
|
||||
const room: ProjectRoomSnapshot = {
|
||||
|
|
@ -213,6 +250,7 @@ export function createProjectRoomSnapshot(
|
|||
floorId: input.floorId?.trim() || null,
|
||||
roomNumber: input.roomNumber.trim(),
|
||||
roomName: input.roomName.trim(),
|
||||
areaM2: input.areaM2 ?? null,
|
||||
};
|
||||
assertProjectRoomSnapshot(room);
|
||||
return room;
|
||||
|
|
@ -279,7 +317,7 @@ export function createProjectRoomInsertProjectCommand(
|
|||
): ProjectRoomInsertProjectCommand {
|
||||
assertProjectRoomSnapshot(room);
|
||||
return {
|
||||
schemaVersion: projectLocationStructureCommandSchemaVersion,
|
||||
schemaVersion: projectRoomCommandSchemaVersion,
|
||||
type: projectRoomInsertCommandType,
|
||||
payload: { room },
|
||||
};
|
||||
|
|
@ -290,7 +328,7 @@ export function createProjectRoomDeleteProjectCommand(
|
|||
): ProjectRoomDeleteProjectCommand {
|
||||
assertProjectRoomSnapshot(room);
|
||||
return {
|
||||
schemaVersion: projectLocationStructureCommandSchemaVersion,
|
||||
schemaVersion: projectRoomCommandSchemaVersion,
|
||||
type: projectRoomDeleteCommandType,
|
||||
payload: { room },
|
||||
};
|
||||
|
|
@ -314,7 +352,7 @@ export function createProjectRoomUpdateProjectCommand(
|
|||
): ProjectRoomUpdateProjectCommand {
|
||||
assertMatchingRoomUpdate(expected, target);
|
||||
return {
|
||||
schemaVersion: projectLocationStructureCommandSchemaVersion,
|
||||
schemaVersion: projectRoomCommandSchemaVersion,
|
||||
type: projectRoomUpdateCommandType,
|
||||
payload: { expected, target },
|
||||
};
|
||||
|
|
@ -376,7 +414,9 @@ export function assertProjectFloorDeleteProjectCommand(
|
|||
|
||||
export function assertProjectRoomInsertProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is ProjectRoomInsertProjectCommand {
|
||||
): asserts command is
|
||||
| ProjectRoomInsertProjectCommand
|
||||
| LegacyProjectRoomInsertProjectCommand {
|
||||
assertProjectRoomStructureProjectCommand(
|
||||
command,
|
||||
projectRoomInsertCommandType
|
||||
|
|
@ -385,7 +425,9 @@ export function assertProjectRoomInsertProjectCommand(
|
|||
|
||||
export function assertProjectRoomDeleteProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is ProjectRoomDeleteProjectCommand {
|
||||
): asserts command is
|
||||
| ProjectRoomDeleteProjectCommand
|
||||
| LegacyProjectRoomDeleteProjectCommand {
|
||||
assertProjectRoomStructureProjectCommand(
|
||||
command,
|
||||
projectRoomDeleteCommandType
|
||||
|
|
@ -419,15 +461,26 @@ export function assertProjectFloorUpdateProjectCommand(
|
|||
|
||||
export function assertProjectRoomUpdateProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>
|
||||
): asserts command is ProjectRoomUpdateProjectCommand {
|
||||
): asserts command is
|
||||
| ProjectRoomUpdateProjectCommand
|
||||
| LegacyProjectRoomUpdateProjectCommand {
|
||||
if (
|
||||
command.schemaVersion !== projectLocationStructureCommandSchemaVersion ||
|
||||
command.type !== projectRoomUpdateCommandType ||
|
||||
!isPlainObject(command.payload) ||
|
||||
Object.keys(command.payload).length !== 2
|
||||
) {
|
||||
throw new Error("Unsupported project-room update command.");
|
||||
}
|
||||
if (command.schemaVersion === legacyProjectRoomCommandSchemaVersion) {
|
||||
assertMatchingRoomUpdate(
|
||||
upgradeLegacyRoomSnapshot(command.payload.expected),
|
||||
upgradeLegacyRoomSnapshot(command.payload.target)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (command.schemaVersion !== projectRoomCommandSchemaVersion) {
|
||||
throw new Error("Unsupported project-room update command.");
|
||||
}
|
||||
assertMatchingRoomUpdate(command.payload.expected, command.payload.target);
|
||||
}
|
||||
|
||||
|
|
@ -480,6 +533,51 @@ export function upgradeProjectFloorCommand(
|
|||
}
|
||||
}
|
||||
|
||||
/** Upgrades persisted version-1 room commands with an empty area. */
|
||||
export function upgradeProjectRoomCommand(
|
||||
command:
|
||||
| ProjectRoomInsertProjectCommand
|
||||
| LegacyProjectRoomInsertProjectCommand
|
||||
): ProjectRoomInsertProjectCommand;
|
||||
export function upgradeProjectRoomCommand(
|
||||
command:
|
||||
| ProjectRoomDeleteProjectCommand
|
||||
| LegacyProjectRoomDeleteProjectCommand
|
||||
): ProjectRoomDeleteProjectCommand;
|
||||
export function upgradeProjectRoomCommand(
|
||||
command:
|
||||
| ProjectRoomUpdateProjectCommand
|
||||
| LegacyProjectRoomUpdateProjectCommand
|
||||
): ProjectRoomUpdateProjectCommand;
|
||||
export function upgradeProjectRoomCommand(
|
||||
command:
|
||||
| ProjectRoomInsertProjectCommand
|
||||
| ProjectRoomDeleteProjectCommand
|
||||
| ProjectRoomUpdateProjectCommand
|
||||
| LegacyProjectRoomInsertProjectCommand
|
||||
| LegacyProjectRoomDeleteProjectCommand
|
||||
| LegacyProjectRoomUpdateProjectCommand
|
||||
) {
|
||||
if (command.schemaVersion === projectRoomCommandSchemaVersion) {
|
||||
return command;
|
||||
}
|
||||
switch (command.type) {
|
||||
case projectRoomInsertCommandType:
|
||||
return createProjectRoomInsertProjectCommand(
|
||||
upgradeLegacyRoomSnapshot(command.payload.room)
|
||||
);
|
||||
case projectRoomDeleteCommandType:
|
||||
return createProjectRoomDeleteProjectCommand(
|
||||
upgradeLegacyRoomSnapshot(command.payload.room)
|
||||
);
|
||||
case projectRoomUpdateCommandType:
|
||||
return createProjectRoomUpdateProjectCommand(
|
||||
upgradeLegacyRoomSnapshot(command.payload.expected),
|
||||
upgradeLegacyRoomSnapshot(command.payload.target)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function assertMatchingBuildingUpdate(expected: unknown, target: unknown) {
|
||||
assertProjectBuildingSnapshot(expected);
|
||||
assertProjectBuildingSnapshot(target);
|
||||
|
|
@ -568,6 +666,29 @@ function upgradeLegacyFloorSnapshot(floor: unknown): ProjectFloorSnapshot {
|
|||
export function assertProjectRoomSnapshot(
|
||||
room: unknown
|
||||
): asserts room is ProjectRoomSnapshot {
|
||||
if (!isPlainObject(room) || Object.keys(room).length !== 6) {
|
||||
throw new Error("Project-room snapshot is invalid.");
|
||||
}
|
||||
assertNormalizedNonEmptyString(room.id, "room.id");
|
||||
assertNormalizedNonEmptyString(room.projectId, "room.projectId");
|
||||
if (room.floorId !== null) {
|
||||
assertNormalizedNonEmptyString(room.floorId, "room.floorId");
|
||||
}
|
||||
assertNormalizedNonEmptyString(room.roomNumber, "room.roomNumber");
|
||||
assertNormalizedNonEmptyString(room.roomName, "room.roomName");
|
||||
if (
|
||||
room.areaM2 !== null &&
|
||||
(typeof room.areaM2 !== "number" ||
|
||||
!Number.isFinite(room.areaM2) ||
|
||||
room.areaM2 < 0)
|
||||
) {
|
||||
throw new Error("room.areaM2 must be null or a non-negative finite number.");
|
||||
}
|
||||
}
|
||||
|
||||
function assertLegacyProjectRoomSnapshot(
|
||||
room: unknown
|
||||
): asserts room is LegacyProjectRoomSnapshot {
|
||||
if (!isPlainObject(room) || Object.keys(room).length !== 5) {
|
||||
throw new Error("Project-room snapshot is invalid.");
|
||||
}
|
||||
|
|
@ -580,6 +701,11 @@ export function assertProjectRoomSnapshot(
|
|||
assertNormalizedNonEmptyString(room.roomName, "room.roomName");
|
||||
}
|
||||
|
||||
function upgradeLegacyRoomSnapshot(room: unknown): ProjectRoomSnapshot {
|
||||
assertLegacyProjectRoomSnapshot(room);
|
||||
return { ...room, areaM2: null };
|
||||
}
|
||||
|
||||
function assertProjectBuildingStructureProjectCommand(
|
||||
command: SerializedProjectCommand<unknown>,
|
||||
expectedType:
|
||||
|
|
@ -627,13 +753,19 @@ function assertProjectRoomStructureProjectCommand(
|
|||
| typeof projectRoomDeleteCommandType
|
||||
) {
|
||||
if (
|
||||
command.schemaVersion !== projectLocationStructureCommandSchemaVersion ||
|
||||
command.type !== expectedType ||
|
||||
!isPlainObject(command.payload) ||
|
||||
Object.keys(command.payload).length !== 1
|
||||
) {
|
||||
throw new Error("Unsupported project-room structure command.");
|
||||
}
|
||||
if (command.schemaVersion === legacyProjectRoomCommandSchemaVersion) {
|
||||
assertLegacyProjectRoomSnapshot(command.payload.room);
|
||||
return;
|
||||
}
|
||||
if (command.schemaVersion !== projectRoomCommandSchemaVersion) {
|
||||
throw new Error("Unsupported project-room structure command.");
|
||||
}
|
||||
assertProjectRoomSnapshot(command.payload.room);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue