fix: add conflict re-check guard before saving holiday hours and time blockers
Re-check conflicts immediately before finalizing holiday hours and time blocker saves to prevent race conditions. Add Refresh button to conflict banners. Fix prettier formatting in login page.
This commit is contained in:
@@ -86,6 +86,11 @@
|
||||
]
|
||||
});
|
||||
|
||||
// =============== Conflict Resolution State ===============
|
||||
let overlappingBookings = $state<OverlappingBooking[]>([]);
|
||||
let checkingOverlap = $state(false);
|
||||
let hasOverlap = $derived(overlappingBookings.length > 0);
|
||||
|
||||
const isFormValid = $derived(
|
||||
exceptionDraft.name.trim() !== '' &&
|
||||
exceptionDraft.weekStarts.length > 0 &&
|
||||
@@ -120,11 +125,6 @@
|
||||
|
||||
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
|
||||
// =============== Conflict Resolution State ===============
|
||||
let overlappingBookings = $state<OverlappingBooking[]>([]);
|
||||
let checkingOverlap = $state(false);
|
||||
let hasOverlap = $derived(overlappingBookings.length > 0);
|
||||
|
||||
async function checkConflictingBookings() {
|
||||
if (exceptionDraft.weekStarts.length === 0) {
|
||||
overlappingBookings = [];
|
||||
@@ -241,8 +241,8 @@
|
||||
}) => ({
|
||||
id: h.id,
|
||||
weekday: h.weekday,
|
||||
start_time: h.startTime,
|
||||
end_time: h.endTime,
|
||||
start_time: h.startTime,
|
||||
end_time: h.endTime,
|
||||
is_open: h.isOpen
|
||||
})
|
||||
) || []
|
||||
@@ -266,6 +266,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-check conflicts before finalizing (race condition guard)
|
||||
await checkConflictingBookings();
|
||||
if (overlappingBookings.length > 0) {
|
||||
toast.error('New conflicting bookings found. Please resolve them first.');
|
||||
return;
|
||||
}
|
||||
|
||||
savingHours = true;
|
||||
const loadingToast = toast.loading('Creating exception group...');
|
||||
|
||||
@@ -298,7 +305,9 @@
|
||||
await fetchExceptionGroups();
|
||||
} else {
|
||||
const text = await response.text();
|
||||
toast.error('Failed to create: ' + sanitizeText(extractErrorMessage(text)), { id: loadingToast });
|
||||
toast.error('Failed to create: ' + sanitizeText(extractErrorMessage(text)), {
|
||||
id: loadingToast
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error creating exception group:', err);
|
||||
@@ -329,7 +338,9 @@
|
||||
await fetchExceptionGroups();
|
||||
} else {
|
||||
const text = await response.text();
|
||||
toast.error('Failed to delete: ' + sanitizeText(extractErrorMessage(text)), { id: loadingToast });
|
||||
toast.error('Failed to delete: ' + sanitizeText(extractErrorMessage(text)), {
|
||||
id: loadingToast
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error deleting exception group:', err);
|
||||
@@ -697,7 +708,14 @@
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
/>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
@@ -729,6 +747,9 @@
|
||||
? ''
|
||||
: 's'} with the proposed hours
|
||||
</span>
|
||||
<Button variant="outline" size="sm" class="h-6 text-xs ml-auto" onclick={checkConflictingBookings}>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{#each overlappingBookings as booking (booking.id)}
|
||||
|
||||
@@ -395,6 +395,14 @@
|
||||
const end = new Date(endIso);
|
||||
const durationMinutes = Math.round((end.getTime() - start.getTime()) / 60000);
|
||||
|
||||
// Re-check conflicts before finalizing (race condition guard)
|
||||
await checkOverlappingBookings();
|
||||
if (overlappingBookings.length > 0) {
|
||||
toast.error('New conflicting bookings found. Please resolve them first.');
|
||||
creating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
creating = true;
|
||||
const loadingToast = toast.loading('Creating time blocker...');
|
||||
|
||||
@@ -864,6 +872,9 @@
|
||||
? ''
|
||||
: 's'} with this slot
|
||||
</span>
|
||||
<Button variant="outline" size="sm" class="h-6 text-xs ml-auto" onclick={checkOverlappingBookings}>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
{#each overlappingBookings as booking (booking.id)}
|
||||
|
||||
Reference in New Issue
Block a user