Extract circuit grid model
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
import type { CircuitTreeCircuitDto, CircuitTreeDeviceRowDto } from "../types.js";
|
||||
|
||||
export type CellKey =
|
||||
| "equipmentIdentifier"
|
||||
| "displayName"
|
||||
| "quantity"
|
||||
| "powerPerUnit"
|
||||
| "simultaneityFactor"
|
||||
| "rowTotalPower"
|
||||
| "circuitTotalPower"
|
||||
| "protectionSummary"
|
||||
| "cableSummary"
|
||||
| "roomSummary"
|
||||
| "remark"
|
||||
| "technicalName"
|
||||
| "connectionKind"
|
||||
| "phaseType"
|
||||
| "costGroup"
|
||||
| "category"
|
||||
| "level"
|
||||
| "roomNumberSnapshot"
|
||||
| "roomNameSnapshot"
|
||||
| "cosPhi"
|
||||
| "protectionType"
|
||||
| "protectionRatedCurrent"
|
||||
| "protectionCharacteristic"
|
||||
| "cableType"
|
||||
| "cableCrossSection"
|
||||
| "cableLength"
|
||||
| "rcdAssignment"
|
||||
| "terminalDesignation"
|
||||
| "status"
|
||||
| "isReserve";
|
||||
|
||||
export type RowType =
|
||||
| "section"
|
||||
| "circuitCompact"
|
||||
| "circuitSummary"
|
||||
| "deviceRow"
|
||||
| "reserveCircuit"
|
||||
| "placeholder";
|
||||
export type CellKind = "circuitField" | "deviceField" | "computed" | "readonly";
|
||||
export type GridValue = string | number | boolean | undefined;
|
||||
|
||||
export interface ColumnDef {
|
||||
key: CellKey;
|
||||
label: string;
|
||||
numeric?: boolean;
|
||||
defaultVisible?: boolean;
|
||||
locked?: boolean;
|
||||
}
|
||||
|
||||
// BMK stays locked as the first column because circuit identity and circuit drag
|
||||
// handles depend on an always-visible equipment identifier.
|
||||
export const allColumns: ColumnDef[] = [
|
||||
{ key: "equipmentIdentifier", label: "Equipment identifier", defaultVisible: true, locked: true },
|
||||
{ key: "displayName", label: "Display name", defaultVisible: true },
|
||||
{ key: "quantity", label: "Quantity", numeric: true, defaultVisible: true },
|
||||
{ key: "powerPerUnit", label: "Power / unit", numeric: true, defaultVisible: true },
|
||||
{ key: "simultaneityFactor", label: "Simultaneity", numeric: true, defaultVisible: true },
|
||||
{ key: "rowTotalPower", label: "Row total", numeric: true, defaultVisible: true },
|
||||
{ key: "circuitTotalPower", label: "Circuit total", numeric: true, defaultVisible: true },
|
||||
{ key: "protectionSummary", label: "Protection summary", defaultVisible: true },
|
||||
{ key: "cableSummary", label: "Cable summary", defaultVisible: true },
|
||||
{ key: "roomSummary", label: "Room", defaultVisible: true },
|
||||
{ key: "remark", label: "Remark", defaultVisible: true },
|
||||
{ key: "technicalName", label: "Technical name" },
|
||||
{ key: "connectionKind", label: "Connection kind" },
|
||||
{ key: "phaseType", label: "Phase type" },
|
||||
{ key: "costGroup", label: "Cost group" },
|
||||
{ key: "category", label: "Category" },
|
||||
{ key: "level", label: "Level" },
|
||||
{ key: "roomNumberSnapshot", label: "Room number" },
|
||||
{ key: "roomNameSnapshot", label: "Room name" },
|
||||
{ key: "cosPhi", label: "cosPhi", numeric: true },
|
||||
{ key: "protectionType", label: "Protection type" },
|
||||
{ key: "protectionRatedCurrent", label: "Protection rated current", numeric: true },
|
||||
{ key: "protectionCharacteristic", label: "Protection characteristic" },
|
||||
{ key: "cableType", label: "Cable type" },
|
||||
{ key: "cableCrossSection", label: "Cable cross-section" },
|
||||
{ key: "cableLength", label: "Cable length", numeric: true },
|
||||
{ key: "rcdAssignment", label: "RCD assignment" },
|
||||
{ key: "terminalDesignation", label: "Terminal designation" },
|
||||
{ key: "status", label: "Status" },
|
||||
{ key: "isReserve", label: "Reserve" },
|
||||
];
|
||||
|
||||
export const defaultVisibleColumnKeys = allColumns
|
||||
.filter((column) => column.defaultVisible)
|
||||
.map((column) => column.key);
|
||||
|
||||
const deviceOnlyColumns = new Set<CellKey>([
|
||||
"quantity",
|
||||
"powerPerUnit",
|
||||
"simultaneityFactor",
|
||||
"rowTotalPower",
|
||||
"roomSummary",
|
||||
"technicalName",
|
||||
"connectionKind",
|
||||
"phaseType",
|
||||
"costGroup",
|
||||
"category",
|
||||
"level",
|
||||
"roomNumberSnapshot",
|
||||
"roomNameSnapshot",
|
||||
"cosPhi",
|
||||
]);
|
||||
|
||||
export const circuitOnlyColumns = new Set<CellKey>([
|
||||
"equipmentIdentifier",
|
||||
"protectionSummary",
|
||||
"cableSummary",
|
||||
"circuitTotalPower",
|
||||
"protectionType",
|
||||
"protectionRatedCurrent",
|
||||
"protectionCharacteristic",
|
||||
"cableType",
|
||||
"cableCrossSection",
|
||||
"cableLength",
|
||||
"rcdAssignment",
|
||||
"terminalDesignation",
|
||||
"status",
|
||||
"isReserve",
|
||||
]);
|
||||
|
||||
export const deviceFieldKeys = new Set<CellKey>([
|
||||
"displayName",
|
||||
"technicalName",
|
||||
"connectionKind",
|
||||
"phaseType",
|
||||
"costGroup",
|
||||
"category",
|
||||
"level",
|
||||
"roomSummary",
|
||||
"roomNumberSnapshot",
|
||||
"roomNameSnapshot",
|
||||
"quantity",
|
||||
"powerPerUnit",
|
||||
"simultaneityFactor",
|
||||
"cosPhi",
|
||||
"remark",
|
||||
]);
|
||||
|
||||
const circuitFieldKeys = new Set<CellKey>([
|
||||
"equipmentIdentifier",
|
||||
"displayName",
|
||||
"protectionType",
|
||||
"protectionRatedCurrent",
|
||||
"protectionCharacteristic",
|
||||
"protectionSummary",
|
||||
"cableType",
|
||||
"cableCrossSection",
|
||||
"cableLength",
|
||||
"cableSummary",
|
||||
"rcdAssignment",
|
||||
"terminalDesignation",
|
||||
"status",
|
||||
"isReserve",
|
||||
"remark",
|
||||
]);
|
||||
|
||||
export function formatValue(value: GridValue): string {
|
||||
if (value === undefined || value === null || value === "") {
|
||||
return "-";
|
||||
}
|
||||
if (typeof value === "boolean") {
|
||||
return value ? "Yes" : "No";
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export function parseNumeric(cellKey: CellKey, draft: string): number | undefined {
|
||||
const trimmed = draft.trim();
|
||||
if (trimmed === "") {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(trimmed);
|
||||
if (Number.isNaN(parsed)) {
|
||||
throw new Error(`Invalid number in ${cellKey}`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function getDeviceValue(device: CircuitTreeDeviceRowDto, key: CellKey): GridValue {
|
||||
switch (key) {
|
||||
case "technicalName": return device.name;
|
||||
case "displayName": return device.displayName || device.name;
|
||||
case "phaseType": return device.phaseType;
|
||||
case "connectionKind": return device.connectionKind;
|
||||
case "costGroup": return device.costGroup;
|
||||
case "category": return device.category;
|
||||
case "level": return device.level;
|
||||
case "roomSummary": return [device.roomNumberSnapshot, device.roomNameSnapshot].filter(Boolean).join(" ").trim() || undefined;
|
||||
case "roomNumberSnapshot": return device.roomNumberSnapshot;
|
||||
case "roomNameSnapshot": return device.roomNameSnapshot;
|
||||
case "quantity": return device.quantity;
|
||||
case "powerPerUnit": return device.powerPerUnit;
|
||||
case "simultaneityFactor": return device.simultaneityFactor;
|
||||
case "cosPhi": return device.cosPhi;
|
||||
case "rowTotalPower": return device.rowTotalPower;
|
||||
case "remark": return device.remark;
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function getCircuitValue(circuit: CircuitTreeCircuitDto, key: CellKey): GridValue {
|
||||
switch (key) {
|
||||
case "equipmentIdentifier": return circuit.equipmentIdentifier;
|
||||
case "displayName": return circuit.displayName;
|
||||
case "circuitTotalPower": return circuit.circuitTotalPower;
|
||||
case "protectionType": return circuit.protectionType;
|
||||
case "protectionRatedCurrent": return circuit.protectionRatedCurrent;
|
||||
case "protectionCharacteristic": return circuit.protectionCharacteristic;
|
||||
case "protectionSummary": {
|
||||
const current = circuit.protectionRatedCurrent !== undefined && circuit.protectionRatedCurrent !== null
|
||||
? `${circuit.protectionRatedCurrent}A`
|
||||
: "";
|
||||
return [circuit.protectionType, current, circuit.protectionCharacteristic].filter(Boolean).join(" ").trim() || undefined;
|
||||
}
|
||||
case "cableSummary": {
|
||||
const length = circuit.cableLength !== undefined && circuit.cableLength !== null ? `${circuit.cableLength} m` : "";
|
||||
return [circuit.cableType, circuit.cableCrossSection, length].filter(Boolean).join(", ").trim() || undefined;
|
||||
}
|
||||
case "cableType": return circuit.cableType;
|
||||
case "cableCrossSection": return circuit.cableCrossSection;
|
||||
case "cableLength": return circuit.cableLength;
|
||||
case "rcdAssignment": return circuit.rcdAssignment;
|
||||
case "terminalDesignation": return circuit.terminalDesignation;
|
||||
case "status": return circuit.status;
|
||||
case "isReserve": return circuit.isReserve;
|
||||
case "remark": return circuit.remark;
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeFilterValue(value: GridValue): string {
|
||||
return formatValue(value);
|
||||
}
|
||||
|
||||
export function getBlockSortValue(circuit: CircuitTreeCircuitDto, key: CellKey): GridValue {
|
||||
const firstRow = circuit.deviceRows[0];
|
||||
if (circuitOnlyColumns.has(key)) {
|
||||
return getCircuitValue(circuit, key);
|
||||
}
|
||||
if (deviceOnlyColumns.has(key)) {
|
||||
return firstRow ? getDeviceValue(firstRow, key) : undefined;
|
||||
}
|
||||
if (key === "displayName") {
|
||||
if (circuit.displayName) return circuit.displayName;
|
||||
return firstRow ? getDeviceValue(firstRow, key) : undefined;
|
||||
}
|
||||
if (key === "remark") {
|
||||
if (circuit.remark) return circuit.remark;
|
||||
return firstRow ? getDeviceValue(firstRow, key) : undefined;
|
||||
}
|
||||
return getCircuitValue(circuit, key);
|
||||
}
|
||||
|
||||
export function compareSortValues(left: GridValue, right: GridValue): number {
|
||||
const leftValue = left === undefined || left === null ? "" : left;
|
||||
const rightValue = right === undefined || right === null ? "" : right;
|
||||
if (typeof leftValue === "number" && typeof rightValue === "number") {
|
||||
return leftValue - rightValue;
|
||||
}
|
||||
return String(leftValue).localeCompare(String(rightValue), undefined, { sensitivity: "base", numeric: true });
|
||||
}
|
||||
|
||||
export function getCellKind(rowType: RowType, key: CellKey): CellKind {
|
||||
if (key === "rowTotalPower" || key === "circuitTotalPower") return "computed";
|
||||
if (rowType === "section") return "readonly";
|
||||
if (rowType === "placeholder") {
|
||||
if (deviceFieldKeys.has(key)) return "deviceField";
|
||||
if (circuitFieldKeys.has(key)) return "circuitField";
|
||||
return "readonly";
|
||||
}
|
||||
if (rowType === "deviceRow") return deviceFieldKeys.has(key) ? "deviceField" : "readonly";
|
||||
if (rowType === "circuitSummary") return circuitFieldKeys.has(key) ? "circuitField" : "readonly";
|
||||
if (rowType === "reserveCircuit" || rowType === "circuitCompact") {
|
||||
if (deviceFieldKeys.has(key)) return "deviceField";
|
||||
if (circuitFieldKeys.has(key)) return "circuitField";
|
||||
}
|
||||
return "readonly";
|
||||
}
|
||||
Reference in New Issue
Block a user