From 2a5e6d4a229e167e549997c20e2b8a6319fc7fcb Mon Sep 17 00:00:00 2001 From: Simon Franken Date: Tue, 24 Feb 2026 21:44:54 +0100 Subject: [PATCH] fix: display correction amounts as h/m and replace decimal input with h:m fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Correction list now shows '13h 32m' instead of '13.65h', using formatDurationHoursMinutes (same formatter used everywhere else) - Sign shown as '−' (minus) for negative corrections instead of bare '-' - Correction input replaced with separate hours + minutes integer fields and a +/− toggle button, removing the awkward decimal entry --- frontend/src/pages/ClientsPage.tsx | 73 +++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/ClientsPage.tsx b/frontend/src/pages/ClientsPage.tsx index 6dae4a1..c30ac0d 100644 --- a/frontend/src/pages/ClientsPage.tsx +++ b/frontend/src/pages/ClientsPage.tsx @@ -60,7 +60,9 @@ function ClientTargetPanel({ // Correction form state const [showCorrectionForm, setShowCorrectionForm] = useState(false); const [corrDate, setCorrDate] = useState(''); - const [corrHours, setCorrHours] = useState(''); + const [corrHoursInt, setCorrHoursInt] = useState(''); + const [corrMins, setCorrMins] = useState(''); + const [corrNegative, setCorrNegative] = useState(false); const [corrDesc, setCorrDesc] = useState(''); const [corrError, setCorrError] = useState(null); const [corrSaving, setCorrSaving] = useState(false); @@ -152,9 +154,15 @@ function ClientTargetPanel({ e.preventDefault(); setCorrError(null); if (!target) return; - const hours = parseFloat(corrHours); - if (isNaN(hours) || hours < -1000 || hours > 1000) { - setCorrError('Hours must be between -1000 and 1000'); + const h = parseInt(corrHoursInt || '0', 10); + const m = parseInt(corrMins || '0', 10); + if (isNaN(h) || isNaN(m) || h < 0 || m < 0 || m > 59 || (h === 0 && m === 0)) { + setCorrError('Enter a valid duration (at least 1 minute)'); + return; + } + const totalHours = h + m / 60; + if (totalHours > 1000) { + setCorrError('Duration cannot exceed 1000 hours'); return; } if (!corrDate) { @@ -163,10 +171,15 @@ function ClientTargetPanel({ } setCorrSaving(true); try { + const h = parseInt(corrHoursInt || '0', 10); + const m = parseInt(corrMins || '0', 10); + const hours = (h + m / 60) * (corrNegative ? -1 : 1); const input: CreateCorrectionInput = { date: corrDate, hours, description: corrDesc || undefined }; await addCorrection.mutateAsync({ targetId: target.id, input }); setCorrDate(''); - setCorrHours(''); + setCorrHoursInt(''); + setCorrMins(''); + setCorrNegative(false); setCorrDesc(''); setShowCorrectionForm(false); } catch (err) { @@ -359,7 +372,7 @@ function ClientTargetPanel({
= 0 ? 'text-green-600' : 'text-red-600'}`}> - {c.hours >= 0 ? '+' : ''}{c.hours}h + {c.hours >= 0 ? '+' : '−'}{formatDurationHoursMinutes(Math.abs(c.hours) * 3600)}
-
- - setCorrHours(e.target.value)} - className="input text-xs py-1" - placeholder="+8 / -4" - step="0.5" - required - /> +
+ +
+ + setCorrHoursInt(e.target.value)} + className="input text-xs py-1 w-14 text-center" + placeholder="0h" + min="0" + max="999" + step="1" + /> + : + setCorrMins(e.target.value)} + className="input text-xs py-1 w-14 text-center" + placeholder="00m" + min="0" + max="59" + step="1" + /> +