leistungsbilanz-ts/src/frontend/types.ts
Grovy311 cd89c4a499 Add buildings above floors and a building structure page
Projects with several buildings could not be structured: floors and rooms
hung directly off the project. Floors can now belong to a building
(migration 0009: table buildings, nullable floors.building_id). Rooms and
distribution boards keep their floor; their building follows from it.
Existing floors stay without a building.

Buildings use the location-structure command store with
project-building.insert/.update/.delete, inverses and persistent Undo/Redo;
deleting a building with assigned floors is rejected. Floor commands carry
buildingId since command version 2. Version 1 floor commands from existing
history predate buildings and are upgraded with buildingId = null before
they are executed and persisted, so forward and inverse keep one version.

Project snapshot schema 7 stores buildings and floor.buildingId; versions
1-6 are upgraded without buildings. Restore and portable transfer include
buildings, and the transfer remaps building ids.

Buildings, floors and rooms move from the project overview to their own
page /projects/:id/struktur with three columns (Gebäude -> Etagen -> Räume)
that filter each other, and a sidebar entry. Floor selects in the board and
room dialogs are grouped by building. Unchanged dialogs close without a
command. Details in docs/building-structure.md; see issue #6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 12:52:34 +02:00

537 lines
13 KiB
TypeScript

import type { DistributionBoardSupplyType } from "../shared/constants/distribution-board";
import type { CircuitGroupCategory } from "../shared/constants/circuit-group";
import type {
DistributionBoardComponentPlacement,
DistributionBoardComponentRole,
} from "../shared/constants/distribution-board-component";
import type {
BreakerTripCharacteristic,
FuseUtilizationCategory,
ProtectionDeviceType,
RcdType,
} from "../shared/constants/protection-device";
import type {
ExternalCsvConfiguration,
ExternalCsvDialect,
} from "../external-model/csv/external-csv-contracts";
import type { createExternalInitialImportPlan } from "../external-model/application/external-initial-import-plan";
export interface ExternalCsvConfigurationSnapshotDto {
id: string;
projectId: string;
configurationVersion: number;
configuration: ExternalCsvConfiguration;
}
export interface ExternalCsvPreviewObjectDto {
rowNumber: number;
ifcGuid: string;
roomNumber: string;
roomName: string;
familyAndType: string;
selectionMarker: string;
circuitIdentifier: string;
power: string;
quantity: string | null;
additionalSourceValues: Record<string, string>;
}
export interface ExternalCsvPreviewDto {
fileName: string;
byteLength: number;
sha256: string;
dialect: ExternalCsvDialect;
headerRowNumber: number;
headerColumns: string[];
rowCount: number;
objectCount: number;
passthroughCount: number;
nonEmptyPassthroughCount: number;
suspectObjectCount: number;
objects: ExternalCsvPreviewObjectDto[];
suspectRowNumbers: number[];
}
export type ExternalInitialImportPlanDto = ReturnType<
typeof createExternalInitialImportPlan
>;
export interface ApplyExternalInitialImportInput {
expectedRevision: number;
expectedConfigurationVersion: number;
expectedSha256: string;
fileName: string;
contentBase64: string;
sourceName: string;
roomDecisions: Array<{
sourceRoomKey: string;
roomId: string | null;
defaultDistributionBoardId: string | null;
}>;
familyProjectDeviceDecisions: Array<{
familyAndType: string;
projectDeviceId: string | null;
}>;
}
export interface ExternalInitialImportResultDto extends ProjectCommandResultDto {
import: {
sourceId: string;
importBatchId: string;
objectCount: number;
};
}
export interface ProjectDto {
id: string;
name: string;
internalProjectNumber: string | null;
externalProjectNumber: string | null;
buildingOwner: string | null;
description: string | null;
isPublicBuilding: boolean;
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
enabledDistributionBoardSupplyTypes: DistributionBoardSupplyType[];
siteAddress: string | null;
workStage: string | null;
projectLead: string | null;
projectEditor: string | null;
currentRevision: number;
}
export interface OfficeProfileDto {
officeName: string | null;
officeAddress: string | null;
}
export interface ProjectHistoryStateDto {
projectId: string;
currentRevision: number;
undoDepth: number;
redoDepth: number;
undoChangeSetId: string | null;
redoChangeSetId: string | null;
}
export type ProjectRevisionSourceDto =
| "user"
| "undo"
| "redo"
| "restore"
| "migration";
export interface ProjectRevisionSummaryDto {
revisionId: string;
changeSetId: string;
revisionNumber: number;
createdAtIso: string;
actorId: string | null;
source: ProjectRevisionSourceDto;
description: string | null;
commandType: string;
payloadSchemaVersion: number;
}
export interface ProjectRevisionPageDto {
projectId: string;
currentRevision: number;
revisions: ProjectRevisionSummaryDto[];
nextBeforeRevision: number | null;
}
export interface ProjectSnapshotMetadataDto {
id: string;
projectId: string;
sourceRevision: number;
sourceRevisionMetadata: ProjectRevisionSummaryDto | null;
schemaVersion: number;
kind: "named" | "automatic";
name: string;
description: string | null;
payloadSha256: string;
createdAtIso: string;
createdByActorId: string | null;
}
export interface ProjectSnapshotRestoreResultDto
extends ProjectCommandResultDto {
snapshot: ProjectSnapshotMetadataDto;
}
export interface ProjectSettingsCommandResultDto
extends ProjectCommandResultDto {
project: ProjectDto;
}
export interface ProjectCommandDto {
schemaVersion: number;
type: string;
payload: unknown;
}
export interface ProjectCommandResultDto {
revision: {
revisionId: string;
changeSetId: string;
projectId: string;
revisionNumber: number;
createdAtIso: string;
};
history: ProjectHistoryStateDto;
}
export interface DistributionBoardInfoDto {
boardNumber: string | null;
location: string | null;
fedFrom: string | null;
remark: string | null;
}
export interface DistributionBoardDto extends DistributionBoardInfoDto {
id: string;
projectId: string;
name: string;
floorId: string | null;
supplyType: DistributionBoardSupplyType | null;
simultaneityFactor: number;
}
export interface DocumentLogoMetadataDto {
mimeType: string;
fileName: string | null;
sizeBytes: number;
updatedAtIso: string;
}
export interface DistributionBoardCommandResultDto
extends ProjectCommandResultDto {
distributionBoard: DistributionBoardDto;
}
export interface DistributionBoardCopyCommandResultDto
extends DistributionBoardCommandResultDto {
circuitList: CircuitListDto;
}
export interface DistributionBoardDeleteCommandResultDto
extends ProjectCommandResultDto {
distributionBoardId: string;
}
export interface CircuitListDto {
id: string;
projectId: string;
distributionBoardId: string;
name: string;
}
export interface BuildingDto {
id: string;
projectId: string;
name: string;
sortOrder: number;
}
export interface ProjectBuildingCommandResultDto
extends ProjectCommandResultDto {
building: BuildingDto;
}
export interface FloorDto {
id: string;
projectId: string;
name: string;
sortOrder: number;
buildingId: string | null;
}
export interface ProjectFloorCommandResultDto
extends ProjectCommandResultDto {
floor: FloorDto;
}
export interface RoomDto {
id: string;
projectId: string;
floorId: string | null;
roomNumber: string;
roomName: string;
}
export interface GlobalDeviceDto {
id: string;
name: string;
displayName: string;
category: string | null;
quantity: number;
installedPowerPerUnitKw: number;
demandFactor: number;
voltageV: number | null;
phaseCount: 1 | 3 | null;
powerFactor: number | null;
note: string | null;
}
export interface ProjectDeviceDto {
id: string;
projectId: string;
name: string;
displayName: string;
phaseType: "single_phase" | "three_phase";
connectionKind: string | null;
costGroup: string | null;
category: CircuitGroupCategory;
quantity: number;
powerPerUnit: number;
simultaneityFactor: number;
totalPower: number;
cosPhi: number | null;
remark: string | null;
voltageV: number | null;
}
export interface ProjectRoomCommandResultDto
extends ProjectCommandResultDto {
room: RoomDto;
}
export interface ProjectDeviceCommandResultDto
extends ProjectCommandResultDto {
projectDevice: ProjectDeviceDto;
}
export interface CreateBuildingInput {
name: string;
}
export interface CreateFloorInput {
name: string;
/** Omitted = keep the current assignment on update; null = no building. */
buildingId?: string | null;
}
export interface CreateRoomInput {
floorId?: string;
roomNumber: string;
roomName: string;
}
export interface CreateGlobalDeviceInput {
name: string;
displayName: string;
category?: string;
quantity: number;
installedPowerPerUnitKw: number;
demandFactor: number;
phaseCount: 1 | 3;
powerFactor?: number;
note?: string;
}
export interface CreateProjectDeviceInput {
name: string;
displayName: string;
connectionKind?: string;
costGroup?: string;
category: CircuitGroupCategory;
quantity: number;
powerPerUnit: number;
simultaneityFactor: number;
cosPhi?: number;
remark?: string;
}
export interface ProjectDeviceSyncDifferenceDto {
field: ProjectDeviceSyncField;
currentValue: string | number | null;
sourceValue: string | number | null;
isOverridden: boolean;
}
export interface ProjectDeviceSyncRowDto {
rowId: string;
circuitId: string;
equipmentIdentifier: string;
circuitDisplayName: string | null;
circuitListId: string;
circuitListName: string;
distributionBoardId: string;
distributionBoardName: string;
rowDisplayName: string;
overriddenFields: ProjectDeviceSyncField[];
differences: ProjectDeviceSyncDifferenceDto[];
}
export interface ProjectDeviceSyncPreviewDto {
projectDevice: {
id: string;
name: string;
displayName: string;
};
rows: ProjectDeviceSyncRowDto[];
}
export interface ProjectDeviceSyncCommandResultDto
extends ProjectCommandResultDto {
preview: ProjectDeviceSyncPreviewDto;
}
export interface CircuitTreeDeviceRowDto {
id: string;
linkedProjectDeviceId?: string;
sortOrder: number;
name: string;
displayName: string;
phaseType?: string;
connectionKind?: string;
costGroup?: string;
category?: string;
level?: string;
roomId?: string;
roomNumberSnapshot?: string;
roomNameSnapshot?: string;
quantity: number;
manualQuantity: number;
powerPerUnit: number;
simultaneityFactor: number;
cosPhi?: number;
remark?: string;
overriddenFields?: string;
rowTotalPower: number;
}
export interface CircuitTreeProtectionDeviceDto {
type: ProtectionDeviceType;
ratedCurrentA: number;
fuseUtilizationCategory?: FuseUtilizationCategory;
tripCharacteristic?: BreakerTripCharacteristic;
rcdType?: RcdType;
ratedResidualCurrentMa?: number;
}
export interface CircuitTreeComponentDto {
id: string;
circuitListId: string;
sectionId?: string;
equipmentIdentifier: string;
name: string;
role: DistributionBoardComponentRole;
placement: DistributionBoardComponentPlacement;
sortOrder: number;
protectionDevice?: CircuitTreeProtectionDeviceDto;
}
export interface CircuitTreeCircuitDto {
id: string;
circuitListId: string;
sectionId: string;
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
rcdAssignment?: string;
terminalDesignation?: string;
voltage?: number;
controlRequirement?: string;
status?: string;
isReserve: boolean;
remark?: string;
circuitTotalPower: number;
deviceRows: CircuitTreeDeviceRowDto[];
protectionDevice?: CircuitTreeProtectionDeviceDto;
}
export interface CircuitTreeSectionDto {
id: string;
key: string;
displayName: string;
prefix: string;
sortOrder: number;
category?: CircuitGroupCategory;
groupNumber?: number;
sectionTotalPower: number;
components: CircuitTreeComponentDto[];
circuits: CircuitTreeCircuitDto[];
}
export interface CircuitTreeResponseDto {
circuitListId: string;
currentRevision: number;
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
distributionBoardSimultaneityFactor: number;
distributionBoardTotalPower: number;
distributionBoardTotalPowerWithSimultaneityFactor: number;
headerComponents: CircuitTreeComponentDto[];
sections: CircuitTreeSectionDto[];
footerComponents: CircuitTreeComponentDto[];
}
export interface ExternalCircuitListObjectDto {
id: string;
ifcGuid: string;
selectionMarker: string;
familyAndType: string;
sourceCircuitIdentifier: string;
displayName: string | null;
category: string | null;
connectionKind: string | null;
effectiveQuantity: number;
powerPerUnitW: number | null;
sourceRoomNumber: string;
sourceRoomName: string;
roomId: string | null;
linkedProjectDeviceId: string | null;
circuitDeviceRowId: string | null;
presenceStatus: "present" | "missing";
}
export interface ExternalCircuitListObjectsDto {
sourceName: string | null;
objectCount: number;
unassignedObjectCount: number;
objects: ExternalCircuitListObjectDto[];
}
export interface CreateCircuitInputDto {
sectionId: string;
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
rcdAssignment?: string;
terminalDesignation?: string;
voltage?: number;
controlRequirement?: string;
status?: string;
isReserve?: boolean;
remark?: string;
}
export interface CreateCircuitDeviceRowInputDto {
linkedProjectDeviceId?: string;
name: string;
displayName: string;
phaseType?: string;
connectionKind?: string;
costGroup?: string;
category?: string;
level?: string;
roomId?: string;
roomNumberSnapshot?: string;
roomNameSnapshot?: string;
quantity: number;
powerPerUnit: number;
simultaneityFactor: number;
cosPhi?: number;
remark?: string;
overriddenFields?: string;
sortOrder?: number;
}
import type { ProjectDeviceSyncField } from "../shared/constants/project-device-sync-fields";