Add estimate row project device handoff

This commit is contained in:
Julian Appel 2026-09-27 18:36:48 +02:00
parent 8b1923e8ec
commit a6500e2b26
9 changed files with 434 additions and 21 deletions

View file

@ -1,7 +1,7 @@
"use client";
import type { ReactNode } from "react";
import { ArrowDown, ArrowUp, Pencil, Plus, Trash2 } from "lucide-react";
import { ArrowDown, ArrowUp, PackagePlus, Pencil, Plus, Trash2 } from "lucide-react";
import {
calculateEstimateBalance,
calculateEstimateRow,
@ -46,6 +46,7 @@ interface EstimateBalanceSheetProps {
onChange: EstimateBalanceUpdate;
onEditGroup: (group: EstimateGroupSnapshot) => void;
onOpenGzfPicker: (groupId: string, rowId: string) => void;
onTakeOverRow: (groupId: string, rowId: string) => void;
onValidityChange: (fieldKey: string, isValid: boolean) => void;
threePhaseVoltageV: number;
}
@ -178,6 +179,7 @@ function EstimateRowLine({
group,
onChange,
onOpenGzfPicker,
onTakeOverRow,
onValidityChange,
row,
rowIndex,
@ -186,6 +188,7 @@ function EstimateRowLine({
group: EstimateGroupSnapshot;
onChange: EstimateBalanceUpdate;
onOpenGzfPicker: (groupId: string, rowId: string) => void;
onTakeOverRow: (groupId: string, rowId: string) => void;
onValidityChange: (fieldKey: string, isValid: boolean) => void;
row: EstimateRowSnapshot;
rowIndex: number;
@ -348,14 +351,24 @@ function EstimateRowLine({
/>
</td>
<td className="estimate-actions-cell estimate-no-print">
<IconButton
disabled={disabled}
label={`Zeile löschen (${rowLabel})`}
onClick={() => onChange((balance) => removeEstimateRow(balance, group.id, row.id))}
variant="danger"
>
<Trash2 aria-hidden="true" size={13} />
</IconButton>
<div className="estimate-row-actions">
<IconButton
disabled={disabled}
label={`Als Projektgerät übernehmen (${rowLabel})`}
onClick={() => onTakeOverRow(group.id, row.id)}
variant="primary"
>
<PackagePlus aria-hidden="true" size={13} />
</IconButton>
<IconButton
disabled={disabled}
label={`Zeile löschen (${rowLabel})`}
onClick={() => onChange((balance) => removeEstimateRow(balance, group.id, row.id))}
variant="danger"
>
<Trash2 aria-hidden="true" size={13} />
</IconButton>
</div>
</td>
</tr>
);
@ -373,6 +386,7 @@ export function EstimateBalanceSheet({
onChange,
onEditGroup,
onOpenGzfPicker,
onTakeOverRow,
onValidityChange,
threePhaseVoltageV,
}: EstimateBalanceSheetProps) {
@ -546,6 +560,7 @@ export function EstimateBalanceSheet({
key={row.id}
onChange={onChange}
onOpenGzfPicker={onOpenGzfPicker}
onTakeOverRow={onTakeOverRow}
onValidityChange={onValidityChange}
row={row}
rowIndex={rowIndex}

View file

@ -0,0 +1,51 @@
import type { CreateProjectDeviceInput } from "../types";
import type {
EstimateGroupSnapshot,
EstimateRowSnapshot,
} from "../../shared/estimate-balance/estimate-balance";
export type EstimateProjectDeviceInitialValues = Omit<
Partial<CreateProjectDeviceInput>,
"quantity" | "powerPerUnit"
> & {
quantity?: number | null;
powerPerUnit?: number | null;
};
const connectionLabels = {
fixed: "Festanschluss",
socket: "Steckdose",
} as const;
/**
* Builds the editable ProjectDevice proposal for one estimate row. Area rows
* deliberately leave quantity and power blank because W/m² has no equivalent
* in the current ProjectDevice model.
*/
export function createEstimateProjectDeviceInitialValues(
group: Pick<EstimateGroupSnapshot, "costGroup">,
row: Pick<
EstimateRowSnapshot,
| "name"
| "unit"
| "connection"
| "powerPerUnitW"
| "simultaneityFactor"
| "cosPhi"
| "remark"
>
): EstimateProjectDeviceInitialValues {
const name = row.name.trim();
return {
name,
displayName: name,
category: "single_phase",
connectionKind: row.connection ? connectionLabels[row.connection] : undefined,
costGroup: group.costGroup || undefined,
quantity: row.unit === "area" ? null : 1,
powerPerUnit: row.unit === "area" ? null : row.powerPerUnitW / 1000,
simultaneityFactor: row.simultaneityFactor,
cosPhi: row.cosPhi,
remark: row.remark ?? undefined,
};
}