fix(ui): improve reservation lifecycle in BookingFlow

- Release reservation on service change, date change, and step-back navigation
- Add synchronous double-click payment guard (isProcessingPaymentSync)
- Immutable update for confirmedBooking to prevent race-condition overcharge
- Generate fresh idempotency key per submission attempt (was reused across
  component lifetime, causing stale-booking illusion on re-submit)
- Release reservation after successful booking submission
- Extract releaseReservation() helper for DRY reservation cleanup
- Race-condition guard in selectTimeWithValidation: pass clicked time
  explicitly so stale validation can't clobber a newer selection
This commit is contained in:
2026-07-06 17:58:14 +01:00
parent 98d561e8b6
commit e831953e5b
3 changed files with 162 additions and 65 deletions
@@ -502,8 +502,16 @@
const endStr = `${endYear}-${String(endMonth).padStart(2, '0')}-${String(daysInEndMonth).padStart(2, '0')}`;
const [whRes, ahRes] = await Promise.all([
fetch(`/api/scheduling/working-hours?start=${startStr}&end=${endStr}`),
fetch(`/api/scheduling/available-hours?start=${startStr}&end=${endStr}`)
fetch(`/api/scheduling/working-hours?start=${startStr}&end=${endStr}`, {
headers: authStore.currentToken
? { Authorization: `Bearer ${authStore.currentToken}` }
: {}
}),
fetch(`/api/scheduling/available-hours?start=${startStr}&end=${endStr}`, {
headers: authStore.currentToken
? { Authorization: `Bearer ${authStore.currentToken}` }
: {}
})
]);
if (whRes.ok && ahRes.ok) {
@@ -570,8 +578,16 @@
const endStr = endOfMonth.toString();
const [whRes, ahRes] = await Promise.all([
fetch(`/api/scheduling/working-hours?start=${startStr}&end=${endStr}`),
fetch(`/api/scheduling/available-hours?start=${startStr}&end=${endStr}`)
fetch(`/api/scheduling/working-hours?start=${startStr}&end=${endStr}`, {
headers: authStore.currentToken
? { Authorization: `Bearer ${authStore.currentToken}` }
: {}
}),
fetch(`/api/scheduling/available-hours?start=${startStr}&end=${endStr}`, {
headers: authStore.currentToken
? { Authorization: `Bearer ${authStore.currentToken}` }
: {}
})
]);
if (whRes.ok && ahRes.ok) {