forked from jappel/leistungsbilanz-ts
Add versioned project settings modal
This commit is contained in:
parent
e1fc81a083
commit
d77cfbeb49
22 changed files with 2563 additions and 111 deletions
225
src/frontend/components/project-settings-modal.tsx
Normal file
225
src/frontend/components/project-settings-modal.tsx
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
"use client";
|
||||
|
||||
import { FormEvent, useState } from "react";
|
||||
import type { ProjectDto } from "../types";
|
||||
|
||||
export interface ProjectSettingsInput {
|
||||
name: string;
|
||||
internalProjectNumber: string | null;
|
||||
externalProjectNumber: string | null;
|
||||
buildingOwner: string | null;
|
||||
description: string | null;
|
||||
singlePhaseVoltageV: number;
|
||||
threePhaseVoltageV: number;
|
||||
}
|
||||
|
||||
interface ProjectSettingsModalProps {
|
||||
isSaving: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (input: ProjectSettingsInput) => Promise<void>;
|
||||
project: ProjectDto;
|
||||
}
|
||||
|
||||
export function ProjectSettingsModal({
|
||||
isSaving,
|
||||
onClose,
|
||||
onSave,
|
||||
project,
|
||||
}: ProjectSettingsModalProps) {
|
||||
const [name, setName] = useState(project.name);
|
||||
const [internalProjectNumber, setInternalProjectNumber] = useState(
|
||||
project.internalProjectNumber ?? ""
|
||||
);
|
||||
const [externalProjectNumber, setExternalProjectNumber] = useState(
|
||||
project.externalProjectNumber ?? ""
|
||||
);
|
||||
const [buildingOwner, setBuildingOwner] = useState(
|
||||
project.buildingOwner ?? ""
|
||||
);
|
||||
const [description, setDescription] = useState(
|
||||
project.description ?? ""
|
||||
);
|
||||
const [singlePhaseVoltageV, setSinglePhaseVoltageV] = useState(
|
||||
String(project.singlePhaseVoltageV)
|
||||
);
|
||||
const [threePhaseVoltageV, setThreePhaseVoltageV] = useState(
|
||||
String(project.threePhaseVoltageV)
|
||||
);
|
||||
|
||||
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
await onSave({
|
||||
name: name.trim(),
|
||||
internalProjectNumber: toNullableString(internalProjectNumber),
|
||||
externalProjectNumber: toNullableString(externalProjectNumber),
|
||||
buildingOwner: toNullableString(buildingOwner),
|
||||
description: toNullableString(description),
|
||||
singlePhaseVoltageV: Number(singlePhaseVoltageV),
|
||||
threePhaseVoltageV: Number(threePhaseVoltageV),
|
||||
});
|
||||
}
|
||||
|
||||
const isValid =
|
||||
name.trim().length > 0 &&
|
||||
Number(singlePhaseVoltageV) > 0 &&
|
||||
Number(threePhaseVoltageV) > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
aria-labelledby="project-settings-title"
|
||||
aria-modal="true"
|
||||
className="modal fade show d-block"
|
||||
role="dialog"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div className="modal-dialog modal-lg modal-dialog-centered">
|
||||
<form className="modal-content" onSubmit={handleSubmit}>
|
||||
<div className="modal-header">
|
||||
<div>
|
||||
<h2 className="modal-title fs-5" id="project-settings-title">
|
||||
Projekteinstellungen
|
||||
</h2>
|
||||
<p className="text-secondary small mb-0">
|
||||
Stammdaten und elektrische Standardwerte des Projekts
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Schließen"
|
||||
className="btn-close"
|
||||
disabled={isSaving}
|
||||
onClick={onClose}
|
||||
type="button"
|
||||
/>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="row g-3">
|
||||
<div className="col-12">
|
||||
<label className="form-label" htmlFor="project-name">
|
||||
Projektname
|
||||
</label>
|
||||
<input
|
||||
autoFocus
|
||||
className="form-control"
|
||||
id="project-name"
|
||||
maxLength={200}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
required
|
||||
value={name}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label" htmlFor="internal-project-number">
|
||||
Projektnummer intern
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="internal-project-number"
|
||||
maxLength={100}
|
||||
onChange={(event) =>
|
||||
setInternalProjectNumber(event.target.value)
|
||||
}
|
||||
value={internalProjectNumber}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label" htmlFor="external-project-number">
|
||||
Projektnummer extern
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="external-project-number"
|
||||
maxLength={100}
|
||||
onChange={(event) =>
|
||||
setExternalProjectNumber(event.target.value)
|
||||
}
|
||||
value={externalProjectNumber}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<label className="form-label" htmlFor="building-owner">
|
||||
Bauherr
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="building-owner"
|
||||
maxLength={200}
|
||||
onChange={(event) => setBuildingOwner(event.target.value)}
|
||||
value={buildingOwner}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<label className="form-label" htmlFor="project-description">
|
||||
Beschreibung
|
||||
</label>
|
||||
<textarea
|
||||
className="form-control"
|
||||
id="project-description"
|
||||
maxLength={2000}
|
||||
onChange={(event) => setDescription(event.target.value)}
|
||||
rows={4}
|
||||
value={description}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label" htmlFor="single-phase-voltage">
|
||||
Standardspannung 1-phasig [V]
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="single-phase-voltage"
|
||||
min="1"
|
||||
onChange={(event) =>
|
||||
setSinglePhaseVoltageV(event.target.value)
|
||||
}
|
||||
required
|
||||
type="number"
|
||||
value={singlePhaseVoltageV}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label" htmlFor="three-phase-voltage">
|
||||
Standardspannung 3-phasig [V]
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="three-phase-voltage"
|
||||
min="1"
|
||||
onChange={(event) =>
|
||||
setThreePhaseVoltageV(event.target.value)
|
||||
}
|
||||
required
|
||||
type="number"
|
||||
value={threePhaseVoltageV}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
disabled={isSaving}
|
||||
onClick={onClose}
|
||||
type="button"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
disabled={isSaving || !isValid}
|
||||
type="submit"
|
||||
>
|
||||
{isSaving ? "Wird gespeichert …" : "Einstellungen speichern"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-backdrop fade show" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function toNullableString(value: string) {
|
||||
return value.trim() || null;
|
||||
}
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
export interface ProjectDto {
|
||||
id: string;
|
||||
name: string;
|
||||
internalProjectNumber: string | null;
|
||||
externalProjectNumber: string | null;
|
||||
buildingOwner: string | null;
|
||||
description: string | null;
|
||||
singlePhaseVoltageV: number;
|
||||
threePhaseVoltageV: number;
|
||||
currentRevision: number;
|
||||
|
|
|
|||
|
|
@ -210,7 +210,15 @@ export function createProject(name: string) {
|
|||
export function updateProjectSettings(
|
||||
projectId: string,
|
||||
expectedRevision: number,
|
||||
input: { singlePhaseVoltageV: number; threePhaseVoltageV: number }
|
||||
input: {
|
||||
name: string;
|
||||
internalProjectNumber: string | null;
|
||||
externalProjectNumber: string | null;
|
||||
buildingOwner: string | null;
|
||||
description: string | null;
|
||||
singlePhaseVoltageV: number;
|
||||
threePhaseVoltageV: number;
|
||||
}
|
||||
) {
|
||||
return request<ProjectSettingsCommandResultDto>(
|
||||
`/api/projects/${projectId}`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue