Add versioned project settings modal

This commit is contained in:
Julian Appel 2026-07-28 18:16:22 +02:00
parent e1fc81a083
commit d77cfbeb49
22 changed files with 2563 additions and 111 deletions

View file

@ -2,23 +2,42 @@ import type { SerializedProjectCommand } from "./project-command.model.js";
export const projectSettingsUpdateCommandType =
"project.update-settings" as const;
export const projectSettingsUpdateCommandSchemaVersion = 1 as const;
export const legacyProjectSettingsUpdateCommandSchemaVersion = 1 as const;
export const projectSettingsUpdateCommandSchemaVersion = 2 as const;
export interface ProjectSettingsValues {
export interface LegacyProjectSettingsValues {
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
}
export interface ProjectSettingsUpdateProjectCommand
export interface ProjectSettingsValues extends LegacyProjectSettingsValues {
name: string;
internalProjectNumber: string | null;
externalProjectNumber: string | null;
buildingOwner: string | null;
description: string | null;
}
export interface LegacyProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<LegacyProjectSettingsValues> {
schemaVersion: typeof legacyProjectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export interface CurrentProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<ProjectSettingsValues> {
schemaVersion: typeof projectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export type ProjectSettingsUpdateProjectCommand =
| LegacyProjectSettingsUpdateProjectCommand
| CurrentProjectSettingsUpdateProjectCommand;
export function createProjectSettingsUpdateProjectCommand(
values: ProjectSettingsValues
): ProjectSettingsUpdateProjectCommand {
const command: ProjectSettingsUpdateProjectCommand = {
): CurrentProjectSettingsUpdateProjectCommand {
const command: CurrentProjectSettingsUpdateProjectCommand = {
schemaVersion: projectSettingsUpdateCommandSchemaVersion,
type: projectSettingsUpdateCommandType,
payload: { ...values },
@ -31,16 +50,45 @@ export function assertProjectSettingsUpdateProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is ProjectSettingsUpdateProjectCommand {
if (
command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion ||
command.type !== projectSettingsUpdateCommandType
command.type !== projectSettingsUpdateCommandType ||
(command.schemaVersion !==
legacyProjectSettingsUpdateCommandSchemaVersion &&
command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion)
) {
throw new Error("Unsupported project settings update command.");
}
if (!isPlainObject(command.payload)) {
throw new Error(
"Project settings update command contains invalid values."
);
}
if (command.schemaVersion === legacyProjectSettingsUpdateCommandSchemaVersion) {
assertLegacyValues(command.payload);
return;
}
if (
!isPlainObject(command.payload) ||
!isTrimmedString(command.payload.name, 1, 200) ||
!isNullableTrimmedString(command.payload.internalProjectNumber, 100) ||
!isNullableTrimmedString(command.payload.externalProjectNumber, 100) ||
!isNullableTrimmedString(command.payload.buildingOwner, 200) ||
!isNullableTrimmedString(command.payload.description, 2000) ||
!isPositiveFiniteNumber(command.payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(command.payload.threePhaseVoltageV) ||
Object.keys(command.payload).length !== 2
Object.keys(command.payload).length !== 7
) {
throw new Error(
"Project settings update command contains invalid values."
);
}
}
function assertLegacyValues(
payload: Record<string, unknown>
): asserts payload is Record<string, unknown> & LegacyProjectSettingsValues {
if (
!isPositiveFiniteNumber(payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(payload.threePhaseVoltageV) ||
Object.keys(payload).length !== 2
) {
throw new Error(
"Project settings update command contains invalid voltages."
@ -59,3 +107,26 @@ function isPositiveFiniteNumber(value: unknown): value is number {
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function isTrimmedString(
value: unknown,
minimumLength: number,
maximumLength: number
): value is string {
return (
typeof value === "string" &&
value === value.trim() &&
value.length >= minimumLength &&
value.length <= maximumLength
);
}
function isNullableTrimmedString(
value: unknown,
maximumLength: number
): value is string | null {
return (
value === null ||
(isTrimmedString(value, 1, maximumLength))
);
}