chore: apply project-wide formatting and UI library updates

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-05-29 20:23:14 +01:00
co-authored by Sisyphus
parent ef650e013a
commit 6e901690fd
131 changed files with 2437 additions and 1571 deletions
@@ -33,7 +33,11 @@
let saving = $state(false);
let placeholder = $state<CalendarDate>(
new CalendarDate(new SvelteDate().getFullYear(), new SvelteDate().getMonth() + 1, new SvelteDate().getDate())
new CalendarDate(
new SvelteDate().getFullYear(),
new SvelteDate().getMonth() + 1,
new SvelteDate().getDate()
)
);
let selectedDate = $state<CalendarDate | undefined>(undefined);
let selectedTime = $state<string | null>(null);
@@ -55,21 +59,42 @@
const minDate = new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate());
const maxDate = new SvelteDate();
maxDate.setMonth(today.getMonth() + 6);
const maxCalendarDate = new CalendarDate(maxDate.getFullYear(), maxDate.getMonth() + 1, maxDate.getDate());
const maxCalendarDate = new CalendarDate(
maxDate.getFullYear(),
maxDate.getMonth() + 1,
maxDate.getDate()
);
let bookingDuration = $derived(
booking.services?.reduce((sum, s) => sum + (s.override_duration_minutes ?? s.duration_minutes ?? 0), 0) ?? booking.duration_minutes ?? 0
booking.services?.reduce(
(sum, s) => sum + (s.override_duration_minutes ?? s.duration_minutes ?? 0),
0
) ??
booking.duration_minutes ??
0
);
const lunchProtection = $derived(
selectedDate ? buildLunchProtection(selectedDate, workingHours, availableHours, bookingDuration, true) : new Map()
selectedDate
? buildLunchProtection(selectedDate, workingHours, availableHours, bookingDuration, true)
: new Map()
);
const groupedTimeSlots = $derived(
selectedDate ? generateGroupedTimeSlots(selectedDate, workingHours, availableHours, bookingDuration, lunchProtection) : []
selectedDate
? generateGroupedTimeSlots(
selectedDate,
workingHours,
availableHours,
bookingDuration,
lunchProtection
)
: []
);
const formattedSelectedDate = $derived(selectedDate ? getDayWithOrdinal(selectedDate) : undefined);
const formattedSelectedDate = $derived(
selectedDate ? getDayWithOrdinal(selectedDate) : undefined
);
function isDateUnavailable(date: DateValue): boolean {
if (!(date instanceof CalendarDate)) return true;
@@ -84,8 +109,20 @@
// If booking duration is 0 (data missing), no valid slots can exist — mark unavailable
if (bookingDuration <= 0) return true;
// Build lunch protection specifically for the date being checked (not the selected date)
const dayProtection = buildLunchProtection(date as CalendarDate, workingHours, availableHours, bookingDuration, true);
const slots = generateAvailableTimeSlots(date as CalendarDate, workingHours, availableHours, bookingDuration, dayProtection);
const dayProtection = buildLunchProtection(
date as CalendarDate,
workingHours,
availableHours,
bookingDuration,
true
);
const slots = generateAvailableTimeSlots(
date as CalendarDate,
workingHours,
availableHours,
bookingDuration,
dayProtection
);
if (slots.length === 0) return true;
return false;
}
@@ -130,7 +167,9 @@
const whMap: Record<string, DayHours> = {};
const ahMap: Record<string, DayAvailability> = {};
whData.forEach((d) => (whMap[d.date] = { isOpen: d.isOpen, startTime: d.startTime, endTime: d.endTime }));
whData.forEach(
(d) => (whMap[d.date] = { isOpen: d.isOpen, startTime: d.startTime, endTime: d.endTime })
);
ahData.forEach((d) => (ahMap[d.date] = { isOpen: d.isOpen, slots: d.slots }));
// Cache by month key
@@ -191,7 +230,11 @@
try {
const startOfMonth = new CalendarDate(date.year, date.month, 1);
const endOfMonth = new CalendarDate(date.year, date.month, date.calendar.getDaysInMonth(date));
const endOfMonth = new CalendarDate(
date.year,
date.month,
date.calendar.getDaysInMonth(date)
);
const [whRes, ahRes] = await Promise.all([
fetch(`/api/scheduling/working-hours?start=${startOfMonth}&end=${endOfMonth}`, {
headers: { Authorization: `Bearer ${authStore.currentToken}` }
@@ -206,7 +249,9 @@
if (gen !== hoursMonthGeneration) return;
const whMap: Record<string, DayHours> = {};
const ahMap: Record<string, DayAvailability> = {};
whData.forEach((d) => (whMap[d.date] = { isOpen: d.isOpen, startTime: d.startTime, endTime: d.endTime }));
whData.forEach(
(d) => (whMap[d.date] = { isOpen: d.isOpen, startTime: d.startTime, endTime: d.endTime })
);
ahData.forEach((d) => (ahMap[d.date] = { isOpen: d.isOpen, slots: d.slots }));
workingHoursCache[monthKey] = whMap;
@@ -220,7 +265,7 @@
toast.error('Failed to load availability');
} finally {
delete loadingMonths[monthKey];
loadingMonthKeys = new Set([...loadingMonthKeys].filter(k => k !== monthKey));
loadingMonthKeys = new Set([...loadingMonthKeys].filter((k) => k !== monthKey));
loadingHours = false;
}
}
@@ -237,7 +282,11 @@
loadingMonths = {};
loadingMonthKeys = new Set();
rescheduleAutoSelectDone = false;
placeholder = new CalendarDate(new SvelteDate().getFullYear(), new SvelteDate().getMonth() + 1, new SvelteDate().getDate());
placeholder = new CalendarDate(
new SvelteDate().getFullYear(),
new SvelteDate().getMonth() + 1,
new SvelteDate().getDate()
);
}
async function reschedule() {
@@ -318,7 +367,14 @@
// Auto-select first available date once data loads (timing-safe, data-driven)
$effect(() => {
if (open && workingHours && availableHours && !selectedDate && !userNavigatedCalendar && !rescheduleAutoSelectDone) {
if (
open &&
workingHours &&
availableHours &&
!selectedDate &&
!userNavigatedCalendar &&
!rescheduleAutoSelectDone
) {
rescheduleAutoSelectDone = true;
const now = new SvelteDate();
const maxDateJs = new SvelteDate(
@@ -334,7 +390,11 @@
const checkDate = new SvelteDate(now);
checkDate.setDate(now.getDate() + i);
const dateStr = checkDate.toISOString().split('T')[0];
const calDate = new CalendarDate(checkDate.getFullYear(), checkDate.getMonth() + 1, checkDate.getDate());
const calDate = new CalendarDate(
checkDate.getFullYear(),
checkDate.getMonth() + 1,
checkDate.getDate()
);
if (workingHours[dateStr]?.isOpen && !isDateUnavailable(calDate)) {
selectedDate = calDate;
if (!userNavigatedCalendar) {
@@ -360,10 +420,21 @@
<Card.Root class="mb-4">
<Card.Content class="pt-4">
<div class="text-sm font-medium">
Current: {new SvelteDate(booking.start_time).toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' })} at {new SvelteDate(booking.start_time).toLocaleTimeString('en-GB', { hour: 'numeric', minute: '2-digit', hour12: true })}
Current: {new SvelteDate(booking.start_time).toLocaleDateString('en-GB', {
weekday: 'short',
day: 'numeric',
month: 'short',
year: 'numeric'
})} at {new SvelteDate(booking.start_time).toLocaleTimeString('en-GB', {
hour: 'numeric',
minute: '2-digit',
hour12: true
})}
</div>
<div class="text-sm text-gray-500 mt-1">
{booking.user?.full_name || 'Unknown'} · {booking.services?.map((s) => s.service_name).join(', ') || 'No services'} · {bookingDuration} min
<div class="mt-1 text-sm text-gray-500">
{booking.user?.full_name || 'Unknown'} · {booking.services
?.map((s) => s.service_name)
.join(', ') || 'No services'} · {bookingDuration} min
</div>
</Card.Content>
</Card.Root>
@@ -381,10 +452,10 @@
selectedDate = newDate;
selectedTime = null;
}}
onPlaceholderChange={(newPlaceholder) => {
placeholder = newPlaceholder;
userNavigatedCalendar = true;
}}
onPlaceholderChange={(newPlaceholder) => {
placeholder = newPlaceholder;
userNavigatedCalendar = true;
}}
/>
</div>
@@ -393,14 +464,18 @@
<div class="border-t">
<div class="max-h-64 overflow-y-auto p-6">
{#if formattedSelectedDate}
<div class="mb-3 grid justify-center gap-2 text-sm font-medium">{formattedSelectedDate}</div>
<div class="mb-3 grid justify-center gap-2 text-sm font-medium">
{formattedSelectedDate}
</div>
{/if}
<TimeSlotList
slots={groupedTimeSlots}
{selectedTime}
duration={bookingDuration}
protection={lunchProtection}
onSelect={(time) => { selectedTime = time; }}
onSelect={(time) => {
selectedTime = time;
}}
/>
</div>
</div>
@@ -423,7 +498,14 @@
</div>
<Modal.Footer class="flex items-center justify-end gap-2">
<Button variant="outline" onclick={() => { open = false; resetForm(); }} disabled={saving}>
<Button
variant="outline"
onclick={() => {
open = false;
resetForm();
}}
disabled={saving}
>
Cancel
</Button>
<Button onclick={reschedule} disabled={saving || !selectedDate || !selectedTime}>