Files
admin 9ee78d2dcf Start-/Endzeit optional: ohne Zeiten Arbeitszeit Kunde & Mehrarbeit leer
- Start- und Endzeit nicht mehr vorbelegt (leeres Feld, Platzhalter HH:MM)
- TimeInput erlaubt Leeren eines Feldes
- Validierung: beide Zeiten zusammen angeben oder beide leer lassen
- Ohne Zeiten zeigen Vorschau und Liste "—" für Arbeitszeit Kunde und Mehrarbeit
  (Eintrag mit nur Reisezeit/Vertriebsunterstützung möglich)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 10:05:50 +02:00

95 lines
2.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect, useState } from 'react';
interface Props {
value: string; // "HH:MM"
onChange: (value: string) => void;
className?: string;
clearOnFocus?: boolean;
autoFocus?: boolean;
}
function isValid(t: string): boolean {
if (!/^\d{1,2}:\d{2}$/.test(t)) return false;
const [h, m] = t.split(':').map(Number);
return h >= 0 && h <= 23 && m >= 0 && m <= 59;
}
function normalize(t: string): string {
const [h, m] = t.split(':').map(Number);
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}
export default function TimeInput({ value, onChange, className = '', clearOnFocus = false, autoFocus = false }: Props) {
const [local, setLocal] = useState(value);
const [error, setError] = useState(false);
// Lokalen Bearbeitungstext zurücksetzen, wenn sich der Wert von außen ändert.
// Bewusster Prop→State-Sync (Hybrid aus controlled/uncontrolled Eingabe).
useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setLocal(value);
setError(false);
}, [value]);
function handleChange(raw: string) {
setError(false);
// Auto-insert colon after the 2nd digit (only when adding, not deleting)
if (/^\d{2}$/.test(raw) && /^\d$/.test(local)) {
setLocal(raw + ':');
return;
}
setLocal(raw);
}
function handleFocus() {
if (clearOnFocus) {
setLocal('');
setError(false);
}
}
function handleBlur() {
if (local === '') {
// Leeres Feld ist erlaubt (keine Uhrzeit) — leeren Wert übernehmen.
setError(false);
if (value !== '') onChange('');
return;
}
const expanded = /^\d{1,2}:?$/.test(local) ? local.replace(/:$/, '') + ':00' : local;
if (isValid(expanded)) {
const norm = normalize(expanded);
setLocal(norm);
setError(false);
onChange(norm);
} else {
setError(true);
}
}
return (
<div className={`relative ${className}`}>
<input
type="text"
inputMode="numeric"
value={local}
onChange={(e) => handleChange(e.target.value)}
onFocus={handleFocus}
onBlur={handleBlur}
autoFocus={autoFocus}
placeholder="HH:MM"
maxLength={5}
className={`w-full px-2 py-1 border-2 rounded-lg bg-white text-sm text-gray-900 font-mono text-center focus:outline-none ${
error ? 'border-red-500 focus:border-red-500' : 'border-gray-400 focus:border-green-600'
}`}
/>
{error && (
<p className="absolute left-0 top-full mt-0.5 text-xs text-red-600 whitespace-nowrap z-10">
Ungültig (00:00 23:59)
</p>
)}
</div>
);
}