Files
omsorg/omsorgapp/src/modules/employees/ContractForm.jsx
T
Felix KemmlerandClaude Haiku 4.5 52fbec3214 Add German comma decimal input for currency/decimal fields
- New utility module `germanDecimal.js` with parseGermanDecimal(),
  sanitizeDecimalInput(), and formatGermanDecimal()
- New DecimalInput component replacing type="number" inputs
- Enables users to enter comma as decimal separator (12,50)
- Applied to: hourlyWage, weeklyHours (ContractForm)
  and billingRate, travelCostRate, travelCostPerKm,
  mealAllowanceRate, minimumHours, and percent surcharges (FacilityForm)
- Display formatting updated in FacilityDetailPanel to show comma decimals

Both comma and dot are accepted as decimal separators. Backend
decimal types remain unchanged (already decimal?).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-10 22:16:20 +02:00

166 lines
5.3 KiB
React

import OmsorgButton from "../../components/ui/OmsorgButton";
import DecimalInput from "../../components/ui/DecimalInput";
import { useValueListItems } from "../../app/useValueListItems";
import { parseGermanDecimal } from "../../utils/germanDecimal";
export const emptyContractForm = {
contractType: "",
startDate: "",
endDate: "",
status: "",
weeklyHours: "",
hourlyWage: "",
allowancesDescription: "",
overtimeRules: "",
vacationDaysPerYear: "",
probationPeriodMonths: "",
};
export function contractToFormValues(contract) {
return {
contractType: contract.contractType ?? "",
startDate: contract.startDate ?? "",
endDate: contract.endDate ?? "",
status: contract.status ?? "",
weeklyHours: contract.weeklyHours ?? "",
hourlyWage: contract.hourlyWage ?? "",
allowancesDescription: contract.allowancesDescription ?? "",
overtimeRules: contract.overtimeRules ?? "",
vacationDaysPerYear: contract.vacationDaysPerYear ?? "",
probationPeriodMonths: contract.probationPeriodMonths ?? "",
};
}
export function contractFormToPayload(form, { employeeId, includeStatus = false } = {}) {
const payload = {
contractType: form.contractType,
employeeId,
facilityId: null,
startDate: form.startDate,
endDate: form.endDate || null,
weeklyHours: parseGermanDecimal(form.weeklyHours),
hourlyWage: parseGermanDecimal(form.hourlyWage),
allowancesDescription: form.allowancesDescription.trim() || null,
overtimeRules: form.overtimeRules.trim() || null,
vacationDaysPerYear: form.vacationDaysPerYear === "" ? null : Number(form.vacationDaysPerYear),
probationPeriodMonths: form.probationPeriodMonths === "" ? null : Number(form.probationPeriodMonths),
};
if (includeStatus) {
payload.status = form.status;
}
return payload;
}
export default function ContractForm({ form, onChange, includeStatus = false }) {
const { items: contractTypes } = useValueListItems("ContractType");
const { items: contractStatuses } = useValueListItems("ContractStatus");
function updateField(field) {
return (event) => onChange({ ...form, [field]: event.target.value });
}
function updateDecimalField(field) {
return (rawValue) => onChange({ ...form, [field]: rawValue });
}
return (
<div className="form-grid">
<label className="form-field">
<span>Vertragstyp *</span>
<select value={form.contractType} onChange={updateField("contractType")} required>
<option value=""> auswählen </option>
{contractTypes.map((option) => (
<option key={option.id} value={option.value}>
{option.value}
</option>
))}
</select>
</label>
{includeStatus && (
<label className="form-field">
<span>Status</span>
<select value={form.status} onChange={updateField("status")}>
{contractStatuses.map((option) => (
<option key={option.id} value={option.value}>
{option.value}
</option>
))}
</select>
</label>
)}
<div className="form-row">
<label className="form-field">
<span>Beginn *</span>
<input type="date" value={form.startDate} onChange={updateField("startDate")} required />
</label>
<label className="form-field">
<span>Ende</span>
<input type="date" value={form.endDate} onChange={updateField("endDate")} />
</label>
</div>
<div className="form-row">
<label className="form-field">
<span>Arbeitszeit (Std./Woche)</span>
<DecimalInput value={form.weeklyHours} onChange={updateDecimalField("weeklyHours")} />
</label>
<label className="form-field">
<span>Stundenlohn ()</span>
<DecimalInput value={form.hourlyWage} onChange={updateDecimalField("hourlyWage")} />
</label>
</div>
<label className="form-field">
<span>Zuschläge</span>
<input value={form.allowancesDescription} onChange={updateField("allowancesDescription")} />
</label>
<label className="form-field">
<span>Überstundenregelung</span>
<input value={form.overtimeRules} onChange={updateField("overtimeRules")} />
</label>
<div className="form-row">
<label className="form-field">
<span>Urlaubsanspruch (Tage/Jahr)</span>
<input
type="number"
min="0"
value={form.vacationDaysPerYear}
onChange={updateField("vacationDaysPerYear")}
/>
</label>
<label className="form-field">
<span>Probezeit (Monate)</span>
<input
type="number"
min="0"
value={form.probationPeriodMonths}
onChange={updateField("probationPeriodMonths")}
/>
</label>
</div>
</div>
);
}
export function FormActions({ onCancel, isSaving, submitLabel }) {
return (
<div className="form-actions">
<OmsorgButton variant="secondary" type="button" onClick={onCancel} disabled={isSaving}>
Abbrechen
</OmsorgButton>
<OmsorgButton type="submit" disabled={isSaving}>
{isSaving ? "Speichert..." : submitLabel}
</OmsorgButton>
</div>
);
}