From 4af2b8dfb47bbd14eb42af89d21d7be6b2caa341 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Thu, 25 Jun 2026 13:48:03 +0100 Subject: [PATCH] style: fix prefer-const and prettier formatting issues --- .../account/EditRequestModal.svelte | 32 ++++++------ .../account/UserBookingModal.svelte | 40 +++++++-------- .../admin/BookingCreateModal.svelte | 2 +- .../lib/components/admin/BookingModal.svelte | 14 +++--- .../lib/components/admin/BookingsCard.svelte | 2 +- .../admin/CustomServicesManagement.svelte | 4 +- .../admin/DiscountsManagement.svelte | 2 +- .../components/admin/EditBookingModal.svelte | 8 +-- .../components/admin/EditRequestModal.svelte | 2 +- .../admin/GiftCardsManagement.svelte | 49 ++++++++++--------- .../lib/components/admin/HolidayHours.svelte | 4 +- .../lib/components/admin/ImageUpload.svelte | 2 +- .../admin/PatchTestsManagement.svelte | 2 +- .../components/admin/RescheduleModal.svelte | 10 ++-- .../admin/ServicesManagement.svelte | 6 +-- .../lib/components/admin/TillPurchases.svelte | 4 +- .../lib/components/admin/TimeBlockers.svelte | 20 ++++---- .../src/lib/components/admin/UsersCard.svelte | 2 +- .../lib/components/admin/WalkInBooking.svelte | 4 +- .../components/admin/WalkInCreateModal.svelte | 2 +- .../lib/components/booking/BookingFlow.svelte | 26 +++++----- .../components/booking/BookingSummary.svelte | 2 +- .../lib/components/booking/DatePicker.svelte | 2 +- .../booking/SelectedTimeSummary.svelte | 2 +- .../lib/components/booking/ServiceCard.svelte | 2 +- .../components/booking/ServiceSelector.svelte | 2 +- .../components/booking/TimeSlotList.svelte | 2 +- .../components/booking/TimeSlotPicker.svelte | 2 +- .../src/lib/components/layout/NavBar.svelte | 4 +- .../components/payments/PaymentModal.svelte | 32 ++++++------ .../payments/TillPaymentModal.svelte | 12 ++--- .../payments/UserPaymentModal.svelte | 48 +++++++++--------- .../today/CurrentAppointment.svelte | 4 +- .../components/today/PendingApprovals.svelte | 6 +-- .../lib/components/today/TodayCalendar.svelte | 12 ++--- .../src/lib/components/ui/CharCounter.svelte | 2 +- .../src/lib/components/ui/ImageVariant.svelte | 2 +- .../src/lib/components/ui/StatusBadge.svelte | 2 +- .../lib/components/ui/form/form-button.svelte | 2 +- .../image-cropper-cropper.svelte | 2 +- .../image-cropper/image-cropper-dialog.svelte | 2 +- .../image-cropper-preview.svelte | 2 +- .../src/lib/components/ui/map/MapArc.svelte | 4 +- .../components/ui/map/MapClusterLayer.svelte | 2 +- .../lib/components/ui/map/MapControls.svelte | 2 +- .../lib/components/ui/map/MapMarker.svelte | 2 +- .../src/lib/components/ui/map/MapPopup.svelte | 2 +- .../src/lib/components/ui/map/MapRoute.svelte | 2 +- .../components/ui/map/MarkerContent.svelte | 2 +- .../lib/components/ui/map/MarkerLabel.svelte | 2 +- .../lib/components/ui/map/MarkerPopup.svelte | 2 +- .../components/ui/map/MarkerTooltip.svelte | 2 +- .../lib/components/ui/policyPopover.svelte | 2 +- .../lib/components/ui/sonner/sonner.svelte | 2 +- frontend/src/lib/utils/timeSlots.ts | 4 +- frontend/src/routes/+layout.svelte | 2 +- frontend/src/routes/account/+page.svelte | 32 ++++++------ .../routes/admin/notifications/+page.svelte | 2 +- frontend/src/routes/api/[...path]/+server.ts | 2 +- frontend/src/routes/gdpr/+page.svelte | 2 +- frontend/src/routes/login/+page.svelte | 4 +- frontend/src/routes/pay-tip/[id]/+page.svelte | 2 +- frontend/src/routes/schedule/+page.svelte | 2 +- frontend/src/routes/tip/+page.svelte | 2 +- 64 files changed, 234 insertions(+), 229 deletions(-) diff --git a/frontend/src/lib/components/account/EditRequestModal.svelte b/frontend/src/lib/components/account/EditRequestModal.svelte index 48a07c0..1e8940c 100644 --- a/frontend/src/lib/components/account/EditRequestModal.svelte +++ b/frontend/src/lib/components/account/EditRequestModal.svelte @@ -89,57 +89,57 @@ ); let placeholderDate = $state(minDate); - let hoursUntilAppointment = $derived( + const hoursUntilAppointment = $derived( (new SvelteDate(booking.start_time).getTime() - new SvelteDate().getTime()) / (1000 * 60 * 60) ); - let hasPayments = $derived((booking.amount_paid ?? 0) > 0); - let noticePeriodBlocked = $derived( + const hasPayments = $derived((booking.amount_paid ?? 0) > 0); + const noticePeriodBlocked = $derived( hasPayments ? hoursUntilAppointment < 72 : hoursUntilAppointment < 24 ); - let noticePeriodWarning = $derived( + const noticePeriodWarning = $derived( !hasPayments && hoursUntilAppointment < 24 && hoursUntilAppointment >= 0 ); - let noticeBlockedMessage = $derived( + const noticeBlockedMessage = $derived( hasPayments ? 'This booking has payments and is too close to the original appointment to reschedule online.' : 'This booking is too close to the original appointment time to reschedule online.' ); - let discountTotal = $derived( + const discountTotal = $derived( booking.discounts?.reduce((sum, d) => sum + d.discount_amount, 0) ?? 0 ); - let bookingTotalDuration = $derived( + const bookingTotalDuration = $derived( booking.services?.reduce( (sum, s) => sum + (s.override_duration_minutes ?? s.duration_minutes ?? 0), 0 ) || 0 ); - let selectedServicesDuration = $derived( + const selectedServicesDuration = $derived( selectedServices.reduce((sum, s) => sum + (s.duration_minutes ?? 0), 0) ); - let hasOverrides = $derived( + const hasOverrides = $derived( booking?.services?.some( (s) => s.override_price != null || s.override_duration_minutes != null ) ?? false ); - let slotDuration = $derived( + const slotDuration = $derived( editMode === 'time' ? bookingTotalDuration : selectedServicesDuration ); - let canSubmit = $derived( + const canSubmit = $derived( submitting === false && ((editMode === 'time' && !!newDate && newTime.length >= 4) || (editMode === 'services' && selectedServices.length > 0) || (editMode === 'both-time' && !!newDate && newTime.length >= 4)) ); - let notesChanged = $derived(notes.trim() !== originalNotes.trim()); + const notesChanged = $derived(notes.trim() !== originalNotes.trim()); - let modalTitle = $derived(() => { + const modalTitle = $derived(() => { switch (editMode) { case 'select': return 'Edit/Reschedule'; @@ -193,7 +193,7 @@ function calculateEndTime(startTime: string, durationMinutes: number): string { const [hours, minutes] = startTime.split(':').map(Number); - let total = hours * 60 + minutes + durationMinutes; + const total = hours * 60 + minutes + durationMinutes; const h = Math.floor(total / 60); const m = total % 60; return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`; @@ -201,7 +201,7 @@ function calculatePreviousTime(time: string): string { const [h, m] = time.split(':').map(Number); - let total = h * 60 + m - 15; + const total = h * 60 + m - 15; return `${String(Math.floor(total / 60)).padStart(2, '0')}:${String(total % 60).padStart(2, '0')}`; } @@ -654,7 +654,7 @@ return Math.max(0, Math.min(nextBookingStart, workingEndMinutes) - bookingEndMinutes); } - let availableAdditionalServices = $derived(() => { + const availableAdditionalServices = $derived(() => { if (editMode !== 'services') return []; const remaining = calculateRemainingTime(); if (remaining <= 0) return []; diff --git a/frontend/src/lib/components/account/UserBookingModal.svelte b/frontend/src/lib/components/account/UserBookingModal.svelte index 2c47a89..3cc853b 100644 --- a/frontend/src/lib/components/account/UserBookingModal.svelte +++ b/frontend/src/lib/components/account/UserBookingModal.svelte @@ -21,7 +21,7 @@ let { open = $bindable(), bookingId }: Props = $props(); let selectedBooking = $state(null); - let businessSettings = $derived(getBusinessInfo()); + const businessSettings = $derived(getBusinessInfo()); let loading = $state(false); let hasPendingEditRequest = $state(false); let pendingEditRequest = $state<{ @@ -44,80 +44,80 @@ // IMPORTANT: Use override_duration_minutes when present — services may have been // customised at booking time. Showing base values misleads users about what was booked. - let totalDuration = $derived( + const totalDuration = $derived( selectedBooking?.services?.reduce( (sum, service) => sum + (service.override_duration_minutes ?? service.duration_minutes ?? 0), 0 ) || 0 ); - let isFutureBooking = $derived( + const isFutureBooking = $derived( selectedBooking ? new SvelteDate(selectedBooking.start_time) > new SvelteDate() : false ); - let hasPayments = $derived( + const hasPayments = $derived( selectedBooking && selectedBooking.payments && selectedBooking.payments.length > 0 ); - let isCancellable = $derived( + const isCancellable = $derived( selectedBooking && isFutureBooking && ['pending', 'confirmed'].includes(selectedBooking.status) ); - let canEditBooking = $derived(isCancellable); + const canEditBooking = $derived(isCancellable); - let totalPaid = $derived( + const totalPaid = $derived( selectedBooking?.payments ?.filter((p) => p.status === 'completed') .reduce((sum, p) => sum + p.amount, 0) || 0 ); - let totalRefunds = $derived( + const totalRefunds = $derived( (selectedBooking?.refunds ?? []) .filter((r) => r.status === 'completed') .reduce((sum, r) => sum + r.amount, 0) ); - let balanceDue = $derived(selectedBooking ? computeBalanceDue(selectedBooking) : 0); + const balanceDue = $derived(selectedBooking ? computeBalanceDue(selectedBooking) : 0); - let totalVAT = $derived( + const totalVAT = $derived( selectedBooking?.payments ?.filter((p) => p.status === 'completed' && p.vat_amount) .reduce((sum, p) => sum + (p.vat_amount ?? 0), 0) || 0 ); - let totalNet = $derived( + const totalNet = $derived( selectedBooking?.payments ?.filter((p) => p.status === 'completed' && p.net_amount) .reduce((sum, p) => sum + (p.net_amount ?? 0), 0) || 0 ); - let hasVAT = $derived(totalVAT > 0); + const hasVAT = $derived(totalVAT > 0); - let depositOutstanding = $derived( + const depositOutstanding = $derived( selectedBooking?.deposit_required && !selectedBooking?.deposit_paid ); - let canPayEarly = $derived( + const canPayEarly = $derived( selectedBooking && !depositOutstanding && totalPaid < selectedBooking.total_amount && ['confirmed', 'in_progress'].includes(selectedBooking.status) ); - let isCompleted = $derived(selectedBooking?.status === 'completed'); + const isCompleted = $derived(selectedBooking?.status === 'completed'); - let hoursUntilAppointment = $derived( + const hoursUntilAppointment = $derived( selectedBooking ? (new SvelteDate(selectedBooking.start_time).getTime() - new SvelteDate().getTime()) / (1000 * 60 * 60) : Infinity ); - let protectedDeposit = $derived( + const protectedDeposit = $derived( selectedBooking ? Math.min(totalPaid, selectedBooking.total_amount * POLICY.PROTECTED_DEPOSIT_MAX_PCT) : 0 ); - let estimatedRefund = $derived( + const estimatedRefund = $derived( hoursUntilAppointment > POLICY.FULL_REFUND_THRESHOLD_HOURS ? totalPaid : hoursUntilAppointment >= POLICY.PARTIAL_REFUND_THRESHOLD_HOURS @@ -133,11 +133,11 @@ let customTipInput = $state(''); let tipProcessing = $state(false); - let canSaveCards = $derived( + const canSaveCards = $derived( authStore.currentUser?.role === 'verified_email' || authStore.currentUser?.role === 'affiliate' ); - let tipPresets = $derived( + const tipPresets = $derived( selectedBooking ? [ { pct: 10, amount: Math.round(selectedBooking.total_amount * 0.1 * 100) / 100 }, diff --git a/frontend/src/lib/components/admin/BookingCreateModal.svelte b/frontend/src/lib/components/admin/BookingCreateModal.svelte index df5636b..c0fd071 100644 --- a/frontend/src/lib/components/admin/BookingCreateModal.svelte +++ b/frontend/src/lib/components/admin/BookingCreateModal.svelte @@ -138,7 +138,7 @@ minimum_age_required: validateCsMinimumAge(newCustomService.minimum_age_required) }; } - let isCustomFormValid = $derived( + const isCustomFormValid = $derived( (newCustomService.name ?? '').trim() !== '' && !customServiceErrors.name && !customServiceErrors.price && diff --git a/frontend/src/lib/components/admin/BookingModal.svelte b/frontend/src/lib/components/admin/BookingModal.svelte index 262f2c8..2e52bdc 100644 --- a/frontend/src/lib/components/admin/BookingModal.svelte +++ b/frontend/src/lib/components/admin/BookingModal.svelte @@ -32,29 +32,29 @@ let cancelling = $state(false); // Derived values for cancel confirmation - let totalPaid = $derived( + const totalPaid = $derived( (selectedBooking?.payments ?? []) .filter((p) => p.payment_method !== 'discount' && p.status === 'completed') .reduce((sum, p) => sum + p.amount, 0) ); - let totalRefunds = $derived( + const totalRefunds = $derived( (selectedBooking?.refunds ?? []) .filter((r) => r.status === 'completed') .reduce((sum, r) => sum + r.amount, 0) ); - let balanceDue = $derived(selectedBooking ? computeBalanceDue(selectedBooking) : 0); - let hoursUntilAppt = $derived( + const balanceDue = $derived(selectedBooking ? computeBalanceDue(selectedBooking) : 0); + const hoursUntilAppt = $derived( selectedBooking ? (new SvelteDate(selectedBooking.start_time).getTime() - new SvelteDate().getTime()) / (1000 * 60 * 60) : Infinity ); - let protectedDeposit = $derived( + const protectedDeposit = $derived( selectedBooking ? Math.min(totalPaid, selectedBooking.total_amount * POLICY.PROTECTED_DEPOSIT_MAX_PCT) : 0 ); - let estimatedRefund = $derived( + const estimatedRefund = $derived( forgiveFeesCancel ? totalPaid : hoursUntilAppt > POLICY.FULL_REFUND_THRESHOLD_HOURS @@ -97,7 +97,7 @@ // IMPORTANT: Use override_duration_minutes when present — services may have been // customised at booking time (discounts, extended sessions). Showing base values // misleads admins about what was actually booked. - let totalDuration = $derived( + const totalDuration = $derived( selectedBooking?.services?.reduce( (sum, service) => sum + (service.override_duration_minutes ?? service.duration_minutes ?? 0), 0 diff --git a/frontend/src/lib/components/admin/BookingsCard.svelte b/frontend/src/lib/components/admin/BookingsCard.svelte index 08bad37..1cfb7b7 100644 --- a/frontend/src/lib/components/admin/BookingsCard.svelte +++ b/frontend/src/lib/components/admin/BookingsCard.svelte @@ -12,7 +12,7 @@ import { formatUserName } from '$lib/utils/nameDisplay'; // Props - let { openBookingModal }: { openBookingModal: (bookingId: string) => void } = $props(); + const { openBookingModal }: { openBookingModal: (bookingId: string) => void } = $props(); // State let bookings = $state([]); diff --git a/frontend/src/lib/components/admin/CustomServicesManagement.svelte b/frontend/src/lib/components/admin/CustomServicesManagement.svelte index 481defd..792623a 100644 --- a/frontend/src/lib/components/admin/CustomServicesManagement.svelte +++ b/frontend/src/lib/components/admin/CustomServicesManagement.svelte @@ -25,7 +25,7 @@ let services = $state([]); let total = $state(0); let page = $state(1); - let perPage = $state(10); + const perPage = $state(10); let searchQuery = $state(''); let loading = $state(true); let creating = $state(false); @@ -95,7 +95,7 @@ serviceErrors.minimum_age_required = validateMinimumAge(newService.minimum_age_required); } - let isFormValid = $derived( + const isFormValid = $derived( (newService.name ?? '').trim() !== '' && !serviceErrors.name && !serviceErrors.price && diff --git a/frontend/src/lib/components/admin/DiscountsManagement.svelte b/frontend/src/lib/components/admin/DiscountsManagement.svelte index 16bb53c..a115fbd 100644 --- a/frontend/src/lib/components/admin/DiscountsManagement.svelte +++ b/frontend/src/lib/components/admin/DiscountsManagement.svelte @@ -92,7 +92,7 @@ } }); - let isFormValid = $derived.by(() => { + const isFormValid = $derived.by(() => { if (!form.name.trim()) return false; if (form.discount_percent <= 0 || form.discount_percent > 100) return false; if (form.campaign_type === 'time_based') { diff --git a/frontend/src/lib/components/admin/EditBookingModal.svelte b/frontend/src/lib/components/admin/EditBookingModal.svelte index 7f8779e..89b832b 100644 --- a/frontend/src/lib/components/admin/EditBookingModal.svelte +++ b/frontend/src/lib/components/admin/EditBookingModal.svelte @@ -149,11 +149,11 @@ } } - let totalDuration = $derived( + const totalDuration = $derived( services.reduce((sum, s) => sum + (s.override_duration_minutes ?? s.duration_minutes ?? 0), 0) ); - let totalPrice = $derived( + const totalPrice = $derived( services.reduce((sum, s) => sum + (s.override_price ?? s.price ?? 0), 0) ); @@ -280,7 +280,7 @@ return services.some((s) => s.service_id === serviceId); } - let maxAvailableDuration = $derived.by(() => { + const maxAvailableDuration = $derived.by(() => { if (!booking?.start_time || !nextAppointmentStart) return null; const bookingStart = new SvelteDate(booking.start_time).getTime(); @@ -292,7 +292,7 @@ return Math.max(0, Math.floor(availableMs / (60 * 1000))); }); - let filteredServices = $derived.by(() => { + const filteredServices = $derived.by(() => { return availableServices.filter((s) => { if (isServiceAdded(s.id)) return false; if (maxAvailableDuration !== null && s.duration_minutes > maxAvailableDuration) return false; diff --git a/frontend/src/lib/components/admin/EditRequestModal.svelte b/frontend/src/lib/components/admin/EditRequestModal.svelte index c456916..213c20b 100644 --- a/frontend/src/lib/components/admin/EditRequestModal.svelte +++ b/frontend/src/lib/components/admin/EditRequestModal.svelte @@ -101,7 +101,7 @@ return false; } - let serviceDiff = $derived.by(() => { + const serviceDiff = $derived.by(() => { const orig = editRequest.original.services; const prop = editRequest.proposed.services; const origIds = new Set(orig.map((s) => s.id)); diff --git a/frontend/src/lib/components/admin/GiftCardsManagement.svelte b/frontend/src/lib/components/admin/GiftCardsManagement.svelte index dd931e7..9576a1a 100644 --- a/frontend/src/lib/components/admin/GiftCardsManagement.svelte +++ b/frontend/src/lib/components/admin/GiftCardsManagement.svelte @@ -37,7 +37,7 @@ user_balances: UserBalance[]; } - let summary = $state({ + const summary = $state({ total_unclaimed: 0, total_user_balances: 0, gift_cards: [], @@ -196,32 +196,32 @@ let topUpMode = $state<'giveaway' | 'purchase'>('giveaway'); // Validation - let generateError = $derived( + const generateError = $derived( generateAmount && (isNaN(Number(generateAmount)) || Number(generateAmount) <= 0) ? 'Must be a valid positive number' : '' ); - let topUpError = $derived( + const topUpError = $derived( topUpAmount && (isNaN(Number(topUpAmount)) || Number(topUpAmount) <= 0) ? 'Must be a valid positive number' : '' ); - let transferAmountError = $derived( + const transferAmountError = $derived( transferAmount && (isNaN(Number(transferAmount)) || Number(transferAmount) <= 0) ? 'Must be a valid positive number' : '' ); - let transferCodeError = $derived( + const transferCodeError = $derived( transferToCode && transferToCode.replace(/[^a-zA-Z0-9]/g, '').length !== 12 ? 'Code must be exactly 12 characters' : '' ); - let isAmountValid = $derived( + const isAmountValid = $derived( generateType === 'stock' ? true : !!(generateAmount && !generateError) ); - let isEmailValid = $derived.by(() => { + const isEmailValid = $derived.by(() => { if (generateType !== 'code') return true; const trimmed = generateEmail.trim(); if (isGuestSelected) { @@ -233,20 +233,20 @@ return trimmed.includes('@') && trimmed.includes('.'); }); - let isGenerateValid = $derived(generateType === 'stock' ? true : isAmountValid && isEmailValid); + const isGenerateValid = $derived(generateType === 'stock' ? true : isAmountValid && isEmailValid); - let isTopUpValid = $derived(topUpAmount && !topUpError); - let isTransferValid = $derived( + const isTopUpValid = $derived(topUpAmount && !topUpError); + const isTransferValid = $derived( transferAmount && !transferAmountError && transferToCode && !transferCodeError ); // TillPayment state - let showTillPayment = $state(false); - let tillAmount = $state(0); - let tillAction = $state<'create' | 'topup'>('create'); - let tillGiftCardId = $state(undefined); - let tillUserId = $state(undefined); - let tillDelivery = $state<'account' | 'code'>('code'); + const showTillPayment = $state(false); + const tillAmount = $state(0); + const tillAction = $state<'create' | 'topup'>('create'); + const tillGiftCardId = $state(undefined); + const tillUserId = $state(undefined); + const tillDelivery = $state<'account' | 'code'>('code'); async function fetchGiftCards(page: number = 1, search: string = '') { loading = true; @@ -512,7 +512,7 @@ // =============== Embedded Payment Handlers =============== - let isEphemeralCardValid = $derived( + const isEphemeralCardValid = $derived( ephemeralCardNumber.replace(/\s/g, '').length >= 13 && ephemeralCardExpiry.includes('/') && ephemeralCardExpiry.length === 5 && @@ -811,8 +811,11 @@ | 'balance' | 'updated'; - let cardSort = $state<{ key: SortKey; dir: 'asc' | 'desc' }>({ key: 'created', dir: 'desc' }); - let balanceSort = $state<{ key: SortKey; dir: 'asc' | 'desc' }>({ key: 'updated', dir: 'desc' }); + const cardSort = $state<{ key: SortKey; dir: 'asc' | 'desc' }>({ key: 'created', dir: 'desc' }); + const balanceSort = $state<{ key: SortKey; dir: 'asc' | 'desc' }>({ + key: 'updated', + dir: 'desc' + }); function toggleCardSort(key: SortKey) { if (cardSort.key === key) { @@ -832,7 +835,7 @@ } } - let sortedCards = $derived.by(() => { + const sortedCards = $derived.by(() => { let filtered = [...cards]; if (activeSection === 'cards') { filtered = cards.filter((gc) => !isExpired(gc.last_used_at)); @@ -864,10 +867,10 @@ return sorted; }); - let activeCardsCount = $derived(cards.filter((gc) => !isExpired(gc.last_used_at)).length); - let expiredCardsCount = $derived(cards.filter((gc) => isExpired(gc.last_used_at)).length); + const activeCardsCount = $derived(cards.filter((gc) => !isExpired(gc.last_used_at)).length); + const expiredCardsCount = $derived(cards.filter((gc) => isExpired(gc.last_used_at)).length); - let sortedBalances = $derived.by(() => { + const sortedBalances = $derived.by(() => { const bals = [...balances]; const { key, dir } = balanceSort; const mul = dir === 'asc' ? 1 : -1; diff --git a/frontend/src/lib/components/admin/HolidayHours.svelte b/frontend/src/lib/components/admin/HolidayHours.svelte index 6893715..b6b05e2 100644 --- a/frontend/src/lib/components/admin/HolidayHours.svelte +++ b/frontend/src/lib/components/admin/HolidayHours.svelte @@ -37,7 +37,7 @@ let exceptionGroupsLoading = $state(true); let savingHours = $state(false); - let formErrors = $state({ + const formErrors = $state({ name: '', weeks: '' }); @@ -59,7 +59,7 @@ ] }); - let isFormValid = $derived( + const isFormValid = $derived( exceptionDraft.name.trim() !== '' && exceptionDraft.weekStarts.length > 0 ); diff --git a/frontend/src/lib/components/admin/ImageUpload.svelte b/frontend/src/lib/components/admin/ImageUpload.svelte index 7b008a5..fd00a0f 100644 --- a/frontend/src/lib/components/admin/ImageUpload.svelte +++ b/frontend/src/lib/components/admin/ImageUpload.svelte @@ -26,7 +26,7 @@ return file.size > MAX_FILE_SIZE; } - let hasOversizedFiles = $derived(uploadFiles.some(isFileTooBig)); + const hasOversizedFiles = $derived(uploadFiles.some(isFileTooBig)); function isHeicFile(file: File): boolean { const name = file.name.toLowerCase(); diff --git a/frontend/src/lib/components/admin/PatchTestsManagement.svelte b/frontend/src/lib/components/admin/PatchTestsManagement.svelte index 74bc94e..15f3d56 100644 --- a/frontend/src/lib/components/admin/PatchTestsManagement.svelte +++ b/frontend/src/lib/components/admin/PatchTestsManagement.svelte @@ -83,7 +83,7 @@ validateExpiryField(); } - let isFormValid = $derived( + const isFormValid = $derived( formData.name.trim() !== '' && !formErrors.name && !formErrors.notice_duration && diff --git a/frontend/src/lib/components/admin/RescheduleModal.svelte b/frontend/src/lib/components/admin/RescheduleModal.svelte index 2c30f14..446966c 100644 --- a/frontend/src/lib/components/admin/RescheduleModal.svelte +++ b/frontend/src/lib/components/admin/RescheduleModal.svelte @@ -65,20 +65,20 @@ maxDate.getDate() ); - let hoursUntilAppointment = $derived( + const hoursUntilAppointment = $derived( (new SvelteDate(booking.start_time).getTime() - new SvelteDate().getTime()) / (1000 * 60 * 60) ); - let hasPayments = $derived((booking.amount_paid ?? 0) > 0); - let showNoticeWarning = $derived( + const hasPayments = $derived((booking.amount_paid ?? 0) > 0); + const showNoticeWarning = $derived( hasPayments ? hoursUntilAppointment < 72 : hoursUntilAppointment < 24 ); - let showNoShowWarning = $derived( + const showNoShowWarning = $derived( !hasPayments && hoursUntilAppointment < 24 && hoursUntilAppointment >= 0 ); let forgiveFees = $state(false); let forgiveNoShow = $state(false); - let bookingDuration = $derived( + const bookingDuration = $derived( booking.services?.reduce( (sum, s) => sum + (s.override_duration_minutes ?? s.duration_minutes ?? 0), 0 diff --git a/frontend/src/lib/components/admin/ServicesManagement.svelte b/frontend/src/lib/components/admin/ServicesManagement.svelte index 9b191e3..b339239 100644 --- a/frontend/src/lib/components/admin/ServicesManagement.svelte +++ b/frontend/src/lib/components/admin/ServicesManagement.svelte @@ -36,9 +36,9 @@ is_active: true, minimum_age_required: 0 }); - let editingService = $state(null); + const editingService = $state(null); let servicesLoading = $state(true); - let servicesUpdating = $state>({}); + const servicesUpdating = $state>({}); let showServiceModal = $state(false); let creatingService = $state(false); let serviceErrors = $state>({}); @@ -129,7 +129,7 @@ ); } - let isFormValid = $derived( + const isFormValid = $derived( newService.name !== '' && !serviceErrors.name && !serviceErrors.price && diff --git a/frontend/src/lib/components/admin/TillPurchases.svelte b/frontend/src/lib/components/admin/TillPurchases.svelte index 59a93a7..0ef6b00 100644 --- a/frontend/src/lib/components/admin/TillPurchases.svelte +++ b/frontend/src/lib/components/admin/TillPurchases.svelte @@ -15,8 +15,8 @@ let giftCardAmount = $state('25'); let showGiftCardInput = $state(false); - let subtotal = $derived(cart.reduce((sum, item) => sum + item.price * item.qty, 0)); - let itemCount = $derived(cart.reduce((sum, item) => sum + item.qty, 0)); + const subtotal = $derived(cart.reduce((sum, item) => sum + item.price * item.qty, 0)); + const itemCount = $derived(cart.reduce((sum, item) => sum + item.qty, 0)); function formatCurrency(n: number): string { return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(n); diff --git a/frontend/src/lib/components/admin/TimeBlockers.svelte b/frontend/src/lib/components/admin/TimeBlockers.svelte index b1a9f34..9cb8c02 100644 --- a/frontend/src/lib/components/admin/TimeBlockers.svelte +++ b/frontend/src/lib/components/admin/TimeBlockers.svelte @@ -55,7 +55,7 @@ rescheduleVersion?: number; } - let { openUserModal, openBookingModal, rescheduleVersion = 0 }: Props = $props(); + const { openUserModal, openBookingModal, rescheduleVersion = 0 }: Props = $props(); const PAGE_SIZE = 5; @@ -84,7 +84,7 @@ let overlappingBookings = $state([]); let hasOverlap = $state(false); - let hasDayConflicts = $derived(overlappingBookings.length > 0); + const hasDayConflicts = $derived(overlappingBookings.length > 0); let showDeleteAlert = $state(false); let blockerToDelete = $state(null); @@ -128,9 +128,9 @@ return `${row.startTime}-${row.endTime}-${row.isOpen}`; } - let selectedWorkingHours = $derived.by(() => getWorkingHoursForDate(newStartDate)); + const selectedWorkingHours = $derived.by(() => getWorkingHoursForDate(newStartDate)); - let availableStartOptions = $derived.by(() => { + const availableStartOptions = $derived.by(() => { const wh = selectedWorkingHours; if (!wh || !wh.isOpen) return []; const startMin = timeToMinutes(wh.startTime); @@ -154,7 +154,7 @@ return options; }); - let availableEndOptions = $derived.by(() => { + const availableEndOptions = $derived.by(() => { const wh = selectedWorkingHours; if (!wh || !wh.isOpen) return []; const startMin = timeToMinutes(wh.startTime); @@ -197,14 +197,14 @@ return `in ${Math.floor(hr / 24)}d`; } - let sortedBlockers = $derived.by(() => { + const sortedBlockers = $derived.by(() => { return [...blockers].sort( (a, b) => new SvelteDate(a.start_time).getTime() - new SvelteDate(b.start_time).getTime() ); }); - let totalPages = $derived.by(() => Math.max(1, Math.ceil(sortedBlockers.length / PAGE_SIZE))); - let pagedBlockers = $derived.by(() => { + const totalPages = $derived.by(() => Math.max(1, Math.ceil(sortedBlockers.length / PAGE_SIZE))); + const pagedBlockers = $derived.by(() => { const start = (currentPage - 1) * PAGE_SIZE; return sortedBlockers.slice(start, start + PAGE_SIZE); }); @@ -275,13 +275,13 @@ return formatLocalDateTime(dt); } - let formComplete = $derived.by(() => { + const formComplete = $derived.by(() => { return ( newDescription.trim() !== '' && newStartDate !== '' && startHour !== '' && endHour !== '' ); }); - let canCreate = $derived.by(() => { + const canCreate = $derived.by(() => { return formComplete && !hasOverlap && !hasDayConflicts && !checkingOverlap; }); diff --git a/frontend/src/lib/components/admin/UsersCard.svelte b/frontend/src/lib/components/admin/UsersCard.svelte index 17f204c..bd60067 100644 --- a/frontend/src/lib/components/admin/UsersCard.svelte +++ b/frontend/src/lib/components/admin/UsersCard.svelte @@ -11,7 +11,7 @@ openUserModal: (userId: string) => void; } - let { openUserModal }: Props = $props(); + const { openUserModal }: Props = $props(); type UserListItem = { id: string; diff --git a/frontend/src/lib/components/admin/WalkInBooking.svelte b/frontend/src/lib/components/admin/WalkInBooking.svelte index 3b0ba60..eb55a85 100644 --- a/frontend/src/lib/components/admin/WalkInBooking.svelte +++ b/frontend/src/lib/components/admin/WalkInBooking.svelte @@ -322,13 +322,13 @@ window.__walkInCountdownInterval = setInterval(updateCountdown, 1000); } - let availableMinutes = $derived.by(() => { + const availableMinutes = $derived.by(() => { if (!slotInfo) return null; if (slotInfo.isAvailableNow) return getLiveRemainingMinutes(); return slotInfo.durationMinutes; }); - let tooShortForService = $derived.by(() => { + const tooShortForService = $derived.by(() => { if (shortestServiceMinutes === null || availableMinutes === null) return false; return availableMinutes < shortestServiceMinutes; }); diff --git a/frontend/src/lib/components/admin/WalkInCreateModal.svelte b/frontend/src/lib/components/admin/WalkInCreateModal.svelte index cfdeb53..d1526eb 100644 --- a/frontend/src/lib/components/admin/WalkInCreateModal.svelte +++ b/frontend/src/lib/components/admin/WalkInCreateModal.svelte @@ -124,7 +124,7 @@ minimum_age_required: validateCsMinimumAge(newCustomService.minimum_age_required) }; } - let isCustomFormValid = $derived( + const isCustomFormValid = $derived( (newCustomService.name ?? '').trim() !== '' && !customServiceErrors.name && !customServiceErrors.price && diff --git a/frontend/src/lib/components/booking/BookingFlow.svelte b/frontend/src/lib/components/booking/BookingFlow.svelte index 44b91b9..5268147 100644 --- a/frontend/src/lib/components/booking/BookingFlow.svelte +++ b/frontend/src/lib/components/booking/BookingFlow.svelte @@ -72,7 +72,7 @@ let selectedServices = $state([]); let selectedDate = $state(undefined); let selectedTime = $state(null); - let customerInfo = $state({ + const customerInfo = $state({ firstName: '', lastName: '', email: '', @@ -98,13 +98,13 @@ let newCardNumber = $state(''); let newCardExpiry = $state(''); let newCardCVC = $state(''); - let saveCardForFuture = $state(false); + const saveCardForFuture = $state(false); // Payment flow state let depositPaid = $state(false); let showPaymentForm = $state(false); - let depositCardFormValid = $derived( + const depositCardFormValid = $derived( selectedPaymentMethod !== null || (showNewCardForm && newCardNumber.replace(/\s/g, '').length >= 13 && @@ -113,17 +113,17 @@ ); // VAT registration status from public business info (via shared store) - let vatRegistered = $derived(getBusinessInfo()?.is_vat_registered ?? false); + const vatRegistered = $derived(getBusinessInfo()?.is_vat_registered ?? false); // Email existence check (guest flow only) let emailChecking = $state(false); let emailSuggestion = $state(null); let emailError = $state(''); - let emailFormatValid = $derived( + const emailFormatValid = $derived( !customerInfo.email || /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(customerInfo.email) ); - let allGuestFieldsValid = $derived( + const allGuestFieldsValid = $derived( customerInfo.firstName && customerInfo.lastName && customerInfo.email && @@ -571,12 +571,12 @@ let loadingWorkingHours = $state(false); let loadingAvailableHours = $state(false); - let workingHoursCache: Record< + const workingHoursCache: Record< string, Record > = {}; - let availableHoursCache: Record< + const availableHoursCache: Record< string, Record }> > = {}; @@ -602,7 +602,7 @@ }); // Track which months are currently being fetched (prevents duplicate requests) - let loadingMonths: Record = {}; + const loadingMonths: Record = {}; // Preload current + next month on first render; subsequent months fetched individually let initialLoadDone = $state(false); @@ -1304,13 +1304,13 @@ selectedDate ? getDayWithOrdinal(selectedDate) : undefined ); - let depositRequired = $derived(calculateDepositRequired()); - let totalSteps = $derived(authStore.isAuthenticated ? 4 : 5); + const depositRequired = $derived(calculateDepositRequired()); + const totalSteps = $derived(authStore.isAuthenticated ? 4 : 5); // StepIndicator uses displayNumber = startAt + index. currentStep aligns with displayNumber, // not the array index. For auth: startAt=1, totalSteps=4 → last displayNumber=4. // For guest: startAt=0, totalSteps=5 → last displayNumber=4. Always evaluates to 4. - let finalStep = $derived(totalSteps - 1 + (authStore.isAuthenticated ? 1 : 0)); - let stepLabels = $derived( + const finalStep = $derived(totalSteps - 1 + (authStore.isAuthenticated ? 1 : 0)); + const stepLabels = $derived( authStore.isAuthenticated ? ['Service', 'Date & Time', 'Details', 'Payment'] : userDepositsRequired > 0 diff --git a/frontend/src/lib/components/booking/BookingSummary.svelte b/frontend/src/lib/components/booking/BookingSummary.svelte index e21b180..b90c1eb 100644 --- a/frontend/src/lib/components/booking/BookingSummary.svelte +++ b/frontend/src/lib/components/booking/BookingSummary.svelte @@ -4,7 +4,7 @@ import { getLocalTimeZone } from '@internationalized/date'; import { Separator } from '$lib/components/ui/separator'; - let { + const { services = [], date = undefined, time = null, diff --git a/frontend/src/lib/components/booking/DatePicker.svelte b/frontend/src/lib/components/booking/DatePicker.svelte index a32025c..af533dd 100644 --- a/frontend/src/lib/components/booking/DatePicker.svelte +++ b/frontend/src/lib/components/booking/DatePicker.svelte @@ -2,7 +2,7 @@ import type { CalendarDate, DateValue } from '@internationalized/date'; import Calendar from '$lib/components/ui/calendar/calendar.svelte'; - let { + const { date, placeholder, minValue, diff --git a/frontend/src/lib/components/booking/SelectedTimeSummary.svelte b/frontend/src/lib/components/booking/SelectedTimeSummary.svelte index 427e7b5..3513ea7 100644 --- a/frontend/src/lib/components/booking/SelectedTimeSummary.svelte +++ b/frontend/src/lib/components/booking/SelectedTimeSummary.svelte @@ -11,7 +11,7 @@ outOfHours?: boolean; } - let { + const { selectedDate, selectedTime, endTime, diff --git a/frontend/src/lib/components/booking/ServiceCard.svelte b/frontend/src/lib/components/booking/ServiceCard.svelte index b3a9290..f5be073 100644 --- a/frontend/src/lib/components/booking/ServiceCard.svelte +++ b/frontend/src/lib/components/booking/ServiceCard.svelte @@ -2,7 +2,7 @@ import { resolve } from '$app/paths'; import type { Service } from '$lib/types/booking'; - let { + const { service, selected = false, onclick, diff --git a/frontend/src/lib/components/booking/ServiceSelector.svelte b/frontend/src/lib/components/booking/ServiceSelector.svelte index 56b6524..6d8ae1d 100644 --- a/frontend/src/lib/components/booking/ServiceSelector.svelte +++ b/frontend/src/lib/components/booking/ServiceSelector.svelte @@ -2,7 +2,7 @@ import ServiceCard from './ServiceCard.svelte'; import type { Service } from '$lib/types/booking'; - let { + const { services = [], selected = [], loading = true, diff --git a/frontend/src/lib/components/booking/TimeSlotList.svelte b/frontend/src/lib/components/booking/TimeSlotList.svelte index 98ca150..5b0e982 100644 --- a/frontend/src/lib/components/booking/TimeSlotList.svelte +++ b/frontend/src/lib/components/booking/TimeSlotList.svelte @@ -11,7 +11,7 @@ onSelect: (time: string) => void; } - let { slots, selectedTime, duration, protection, onSelect }: Props = $props(); + const { slots, selectedTime, duration, protection, onSelect }: Props = $props(); {#if slots.length > 0} diff --git a/frontend/src/lib/components/booking/TimeSlotPicker.svelte b/frontend/src/lib/components/booking/TimeSlotPicker.svelte index 754b586..04cbf08 100644 --- a/frontend/src/lib/components/booking/TimeSlotPicker.svelte +++ b/frontend/src/lib/components/booking/TimeSlotPicker.svelte @@ -2,7 +2,7 @@ import type { CalendarDate } from '@internationalized/date'; import { Button } from '$lib/components/ui/button'; - let { + const { date, groupedTimeSlots = [], selectedTime = null, diff --git a/frontend/src/lib/components/layout/NavBar.svelte b/frontend/src/lib/components/layout/NavBar.svelte index b7fd950..235ab30 100644 --- a/frontend/src/lib/components/layout/NavBar.svelte +++ b/frontend/src/lib/components/layout/NavBar.svelte @@ -52,8 +52,8 @@ }; // Derived values for auth state - ensures reactivity - let isAuthenticated = $derived(authStore.isAuthenticated); - let isLoading = $derived(authStore.isLoading); + const isAuthenticated = $derived(authStore.isAuthenticated); + const isLoading = $derived(authStore.isLoading); // Notification bell state let unreadCount = $state(0); diff --git a/frontend/src/lib/components/payments/PaymentModal.svelte b/frontend/src/lib/components/payments/PaymentModal.svelte index 60a1bbd..cb1a1ee 100644 --- a/frontend/src/lib/components/payments/PaymentModal.svelte +++ b/frontend/src/lib/components/payments/PaymentModal.svelte @@ -15,7 +15,7 @@ onComplete: (payment: PaymentResult) => void; } - let { booking, onClose, onComplete }: Props = $props(); + const { booking, onClose, onComplete }: Props = $props(); type PaymentStatus = | 'idle' @@ -47,17 +47,17 @@ let paymentResult = $state(null); let error = $state(null); - let stamps = $derived(booking.user?.loyalty_stamps ?? 0); + const stamps = $derived(booking.user?.loyalty_stamps ?? 0); let useLoyalty = $state(false); - let loyaltyEligible = $derived( + const loyaltyEligible = $derived( stamps >= 10 && !(booking.discounts ?? []).some((d: BookingDiscount) => d.discount_source === 'loyalty') && booking.total_amount > 0 && booking.amount_paid === 0 ); - let loyaltyDiscount = $derived( + const loyaltyDiscount = $derived( useLoyalty ? Math.round(booking.total_amount * 100 * LOYALTY_DISCOUNT_RATE) : 0 ); @@ -203,13 +203,15 @@ return service.override_price ?? service.price ?? 0; } - let subtotal = $derived((booking.services ?? []).reduce((sum, s) => sum + getServicePrice(s), 0)); - let discountSum = $derived( + const subtotal = $derived( + (booking.services ?? []).reduce((sum, s) => sum + getServicePrice(s), 0) + ); + const discountSum = $derived( (booking.discounts ?? []).reduce((sum, d) => sum + d.discount_amount, 0) ); - let netTotal = $derived(Math.max(0, subtotal - discountSum)); + const netTotal = $derived(Math.max(0, subtotal - discountSum)); - let tipPercentages = $derived.by(() => { + const tipPercentages = $derived.by(() => { if (netTotal <= 0) return []; return [ { pct: 10, amount: Math.round(netTotal * 0.1 * 100) / 100 }, @@ -218,7 +220,7 @@ ]; }); - let tipMultiplier = $derived( + const tipMultiplier = $derived( selectedTipPercent !== null ? 1 + selectedTipPercent / 100 : customTipAmount && parseFloat(customTipAmount) > 0 @@ -226,8 +228,8 @@ : 1 ); - let totalWithTip = $derived(tipEnabled ? netTotal * tipMultiplier : netTotal); - let tipDisplay = $derived( + const totalWithTip = $derived(tipEnabled ? netTotal * tipMultiplier : netTotal); + const tipDisplay = $derived( selectedTipPercent !== null ? `${selectedTipPercent}%` : customTipAmount && parseFloat(customTipAmount) > 0 @@ -235,7 +237,7 @@ : '' ); - let totalDue = $derived(tipEnabled ? totalWithTip : netTotal); + const totalDue = $derived(tipEnabled ? totalWithTip : netTotal); function formatCurrency(value: number): string { return new Intl.NumberFormat('en-GB', { @@ -378,8 +380,8 @@ }); let cashAmount = $state(''); - let cashAmountNum = $derived(cashAmount === '' ? 0 : parseFloat(cashAmount)); - let changeDue = $derived(cashAmountNum > totalDue ? cashAmountNum - totalDue : 0); + const cashAmountNum = $derived(cashAmount === '' ? 0 : parseFloat(cashAmount)); + const changeDue = $derived(cashAmountNum > totalDue ? cashAmountNum - totalDue : 0); let extraAsTip = $state(false); function handleCashInput(e: Event) { @@ -511,7 +513,7 @@ giftCardId = formatted; } - let giftCardValid = $derived(useAccountBalance || giftCardId.replace(/-/g, '').length === 12); + const giftCardValid = $derived(useAccountBalance || giftCardId.replace(/-/g, '').length === 12); async function handleGiftCardPayment() { if (!giftCardValid) { diff --git a/frontend/src/lib/components/payments/TillPaymentModal.svelte b/frontend/src/lib/components/payments/TillPaymentModal.svelte index 1b6e8aa..0d68509 100644 --- a/frontend/src/lib/components/payments/TillPaymentModal.svelte +++ b/frontend/src/lib/components/payments/TillPaymentModal.svelte @@ -21,7 +21,7 @@ onComplete: (result: TillSaleResult) => void; } - let { + const { amount, itemType, action, @@ -100,9 +100,9 @@ // Cash let cashAmount = $state(''); - let cashAmountNum = $derived(Number(cashAmount) || 0); - let changeDue = $derived(Math.max(0, cashAmountNum - amount)); - let cashEntryComplete = $derived(cashAmountNum >= amount); + const cashAmountNum = $derived(Number(cashAmount) || 0); + const changeDue = $derived(Math.max(0, cashAmountNum - amount)); + const cashEntryComplete = $derived(cashAmountNum >= amount); // Remember cash received for success screen display let cashTendered = $state(0); @@ -224,13 +224,13 @@ ephemeralCardCVC = input.value.replace(/\D/g, '').substring(0, 4); } - let isEphemeralCardValid = $derived( + const isEphemeralCardValid = $derived( isValidLuhn(ephemeralCardNumber) && /^\d{2}\/\d{2}$/.test(ephemeralCardExpiry) && ephemeralCardCVC.length >= 3 ); - let ephemeralCardError = $derived( + const ephemeralCardError = $derived( ephemeralCardNumber.length > 0 && !isValidLuhn(ephemeralCardNumber) ? 'Invalid card number' : /^\d{2}\/\d{2}$/.test(ephemeralCardExpiry) && diff --git a/frontend/src/lib/components/payments/UserPaymentModal.svelte b/frontend/src/lib/components/payments/UserPaymentModal.svelte index c02503f..06235b9 100644 --- a/frontend/src/lib/components/payments/UserPaymentModal.svelte +++ b/frontend/src/lib/components/payments/UserPaymentModal.svelte @@ -22,7 +22,7 @@ defaultPaymentType?: 'full' | 'partial' | 'deposit'; } - let { booking, onClose, onComplete, canSaveCards = true, defaultPaymentType }: Props = $props(); + const { booking, onClose, onComplete, canSaveCards = true, defaultPaymentType }: Props = $props(); type PaymentStatus = 'idle' | 'processing' | 'polling' | 'success' | 'error'; @@ -140,9 +140,9 @@ return { month, year }; } - let expiryParts = $derived(parseExpiryParts(newCardExpiry)); + const expiryParts = $derived(parseExpiryParts(newCardExpiry)); - let isExpiryInPast = $derived( + const isExpiryInPast = $derived( expiryParts !== null && (() => { const expiryDate = new SvelteDate(expiryParts.year, expiryParts.month); @@ -150,7 +150,7 @@ })() ); - let hasInvalidMonth = $derived(/^\d{2}\/\d{2}$/.test(newCardExpiry) && expiryParts === null); + const hasInvalidMonth = $derived(/^\d{2}\/\d{2}$/.test(newCardExpiry) && expiryParts === null); function isValidLuhn(cardNumber: string): boolean { const s = cardNumber.replace(/\D/g, ''); @@ -168,17 +168,17 @@ return sum % 10 === 0 && s.length >= 13 && s.length <= 19; } - let cardFormValid = $derived( + const cardFormValid = $derived( isValidLuhn(newCardNumber) && expiryParts !== null && newCardCVC.length >= 3 && !isExpiryInPast ); - let cardSelected = $derived( + const cardSelected = $derived( (selectedPaymentMethod !== null && paymentMethods.length > 0) || (showNewCardForm && cardFormValid) || (paymentMethods.length === 0 && cardFormValid) ); - let cardValidationError = $derived( + const cardValidationError = $derived( !cardSelected ? selectedPaymentMethod === null && paymentMethods.length > 0 && !showNewCardForm ? 'Please select a card' @@ -211,12 +211,12 @@ } | null>(null); // Derived values - let depositOutstanding = $derived(booking.deposit_required && !booking.deposit_paid); + const depositOutstanding = $derived(booking.deposit_required && !booking.deposit_paid); // Auto-select a sensible default payment type based on the booking's deposit // state. The backend will split the charge into deposit + non-deposit records // when appropriate, so this choice mainly controls the button label and amount. - let defaultType = $derived(defaultPaymentType ?? (depositOutstanding ? 'deposit' : 'full')); + const defaultType = $derived(defaultPaymentType ?? (depositOutstanding ? 'deposit' : 'full')); let paymentType = $state<'full' | 'partial' | 'deposit'>('full'); $effect(() => { paymentType = defaultType as 'full' | 'partial' | 'deposit'; @@ -230,35 +230,35 @@ let lockInterval: ReturnType | null = null; let countdownInterval: ReturnType | null = null; - let servicesSubtotal = $derived( + const servicesSubtotal = $derived( (booking.services ?? []).reduce((sum, s) => sum + (s.price || 0), 0) ); - let discountSum = $derived( + const discountSum = $derived( (booking.discounts ?? []).reduce((sum, d) => sum + d.discount_amount, 0) ); - let totalPaid = $derived( + const totalPaid = $derived( booking.payments ?.filter((p) => p.status === 'completed') .reduce((sum, p) => sum + p.amount, 0) || 0 ); - let amountRemaining = $derived(booking.total_amount - totalPaid); + const amountRemaining = $derived(booking.total_amount - totalPaid); - let loyaltyEligible = $derived( + const loyaltyEligible = $derived( stamps >= 10 && !(booking.discounts ?? []).some((d) => d.discount_source === 'loyalty') && booking.total_amount > 0 && booking.amount_paid === 0 ); - let loyaltyDiscount = $derived( + const loyaltyDiscount = $derived( useLoyalty ? Math.round(booking.total_amount * 100 * LOYALTY_DISCOUNT_RATE) : 0 ); // Deposit policy warning text — dynamic based on booking state - let expectedDepositPercent = $derived(booking.deposit_required ? 20 : 0); - let depositPolicyWarning = $derived( + const expectedDepositPercent = $derived(booking.deposit_required ? 20 : 0); + const depositPolicyWarning = $derived( { get text(): string | null { if (!booking.deposit_required && totalPaid === 0 && booking.amount_due <= 0) return null; @@ -273,12 +273,12 @@ }.text ); - let isScenarioA = $derived(depositOutstanding); + const isScenarioA = $derived(depositOutstanding); - let isScenarioB = $derived(!depositOutstanding && totalPaid < booking.total_amount); + const isScenarioB = $derived(!depositOutstanding && totalPaid < booking.total_amount); - let partialAmountNum = $derived(partialAmount === '' ? 0 : parseFloat(partialAmount)); - let partialAmountValid = $derived( + const partialAmountNum = $derived(partialAmount === '' ? 0 : parseFloat(partialAmount)); + const partialAmountValid = $derived( paymentType === 'partial' && partialAmount !== '' && !isNaN(partialAmountNum) && @@ -287,7 +287,7 @@ /^\d+(\.\d{0,2})?$/.test(partialAmount) ); - let partialValidationError = $derived( + const partialValidationError = $derived( paymentType === 'partial' && !partialAmountValid ? partialAmount === '' ? 'Enter an amount' @@ -301,14 +301,14 @@ : null ); - let payButtonDisabled = $derived( + const payButtonDisabled = $derived( status === 'processing' || !cardSelected || (paymentType === 'partial' && !partialAmountValid) || (booking.status === 'pending_release' && (lockTimer <= 0 || !lockAcquired)) ); - let payButtonError = $derived( + const payButtonError = $derived( status === 'processing' ? null : !cardSelected diff --git a/frontend/src/lib/components/today/CurrentAppointment.svelte b/frontend/src/lib/components/today/CurrentAppointment.svelte index f7ab501..f995d37 100644 --- a/frontend/src/lib/components/today/CurrentAppointment.svelte +++ b/frontend/src/lib/components/today/CurrentAppointment.svelte @@ -18,7 +18,7 @@ openUserModal: (userId: string) => void; } - let { _openBookingModal, openEditBookingModal, openUserModal }: Props = $props(); + const { _openBookingModal, openEditBookingModal, openUserModal }: Props = $props(); type Booking = { id: string; @@ -90,7 +90,7 @@ let weekSummary = $state(null); // VAT registration status from public business info (via shared store) - let vatRegistered = $derived(getBusinessInfo()?.is_vat_registered ?? false); + const vatRegistered = $derived(getBusinessInfo()?.is_vat_registered ?? false); // Data diffing — only update UI when payload actually changes let prevDataJson = $state(''); diff --git a/frontend/src/lib/components/today/PendingApprovals.svelte b/frontend/src/lib/components/today/PendingApprovals.svelte index c9c5642..6888d53 100644 --- a/frontend/src/lib/components/today/PendingApprovals.svelte +++ b/frontend/src/lib/components/today/PendingApprovals.svelte @@ -87,17 +87,17 @@ }; let pendingApprovals = $state([]); - let visibleApprovals = $derived(pendingApprovals.slice(0, 3)); + const visibleApprovals = $derived(pendingApprovals.slice(0, 3)); let loading = $state(true); let showApprovalModal = $state(false); let selectedBooking = $state(null); let pendingEditRequests = $state([]); - let visibleEditRequests = $derived(pendingEditRequests.slice(0, 3)); + const visibleEditRequests = $derived(pendingEditRequests.slice(0, 3)); let showEditRequestModal = $state(false); let selectedEditRequest = $state(null); - let _hasItems = $derived(pendingApprovals.length > 0 || pendingEditRequests.length > 0); + const _hasItems = $derived(pendingApprovals.length > 0 || pendingEditRequests.length > 0); $effect(() => { hasItems = _hasItems; }); diff --git a/frontend/src/lib/components/today/TodayCalendar.svelte b/frontend/src/lib/components/today/TodayCalendar.svelte index 5c2dbe6..9fd1424 100644 --- a/frontend/src/lib/components/today/TodayCalendar.svelte +++ b/frontend/src/lib/components/today/TodayCalendar.svelte @@ -28,7 +28,7 @@ openUserModal: (userId: string) => void; } - let { openBookingModal, openUserModal }: Props = $props(); + const { openBookingModal, openUserModal }: Props = $props(); type TodayAppointment = { id: string; @@ -155,7 +155,7 @@ return blocker.description?.startsWith('RESERVATION:') ?? false; } - let availableStartOptions = $derived.by(() => { + const availableStartOptions = $derived.by(() => { const wh = workingHours; if (!wh || !wh.isOpen) return []; const startMin = timeToMinutes(wh.startTime); @@ -174,7 +174,7 @@ return options; }); - let availableEndOptions = $derived.by(() => { + const availableEndOptions = $derived.by(() => { const wh = workingHours; if (!wh || !wh.isOpen) return []; const endMin = timeToMinutes(wh.endTime); @@ -197,7 +197,7 @@ return options; }); - let suggestedLunch = $derived.by(() => { + const suggestedLunch = $derived.by(() => { if (!workingHours || !availableHours || !workingHours.isOpen) return null; const wh = workingHours; const ah = availableHours; @@ -284,7 +284,7 @@ | LunchTimelineItem | ClosingTimeTimelineItem; - let timeline = $derived.by(() => { + const timeline = $derived.by(() => { const items: TimelineItem[] = []; for (const apt of appointments) { @@ -512,7 +512,7 @@ return formatLocalDateTime(dt); } - let canCreate = $derived.by(() => !hasOverlap && !checkingOverlap); + const canCreate = $derived.by(() => !hasOverlap && !checkingOverlap); async function createBlocker() { if (!canCreate) return; diff --git a/frontend/src/lib/components/ui/CharCounter.svelte b/frontend/src/lib/components/ui/CharCounter.svelte index b9068a5..5130610 100644 --- a/frontend/src/lib/components/ui/CharCounter.svelte +++ b/frontend/src/lib/components/ui/CharCounter.svelte @@ -4,7 +4,7 @@ maxChars?: number; threshold?: number; // when to start showing counter }; - let { text, maxChars = 1000000, threshold = 750000 }: Props = $props(); + const { text, maxChars = 1000000, threshold = 750000 }: Props = $props(); const graphemeCount = $derived.by(() => { const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); diff --git a/frontend/src/lib/components/ui/ImageVariant.svelte b/frontend/src/lib/components/ui/ImageVariant.svelte index 21c1ebf..ea31eba 100644 --- a/frontend/src/lib/components/ui/ImageVariant.svelte +++ b/frontend/src/lib/components/ui/ImageVariant.svelte @@ -12,7 +12,7 @@ jxl?: string; } - let { + const { urls, type = 'thumb', alt = '', diff --git a/frontend/src/lib/components/ui/StatusBadge.svelte b/frontend/src/lib/components/ui/StatusBadge.svelte index e37ac33..ca95db2 100644 --- a/frontend/src/lib/components/ui/StatusBadge.svelte +++ b/frontend/src/lib/components/ui/StatusBadge.svelte @@ -1,5 +1,5 @@ diff --git a/frontend/src/lib/components/ui/image-cropper/image-cropper-cropper.svelte b/frontend/src/lib/components/ui/image-cropper/image-cropper-cropper.svelte index 1092edc..f7d285f 100644 --- a/frontend/src/lib/components/ui/image-cropper/image-cropper-cropper.svelte +++ b/frontend/src/lib/components/ui/image-cropper/image-cropper-cropper.svelte @@ -3,7 +3,7 @@ import { useImageCropperCropper } from './image-cropper.svelte.js'; import type { ImageCropperCropperProps } from './types.js'; - let { + const { cropShape = 'round', aspect = 1, showGrid = false, diff --git a/frontend/src/lib/components/ui/image-cropper/image-cropper-dialog.svelte b/frontend/src/lib/components/ui/image-cropper/image-cropper-dialog.svelte index 80322bd..b0076ad 100644 --- a/frontend/src/lib/components/ui/image-cropper/image-cropper-dialog.svelte +++ b/frontend/src/lib/components/ui/image-cropper/image-cropper-dialog.svelte @@ -4,7 +4,7 @@ import { useImageCropperDialog } from './image-cropper.svelte.js'; import type { ImageCropperDialogProps } from './types'; - let { children, class: className, ...rest }: ImageCropperDialogProps = $props(); + const { children, class: className, ...rest }: ImageCropperDialogProps = $props(); const dialogState = useImageCropperDialog(); diff --git a/frontend/src/lib/components/ui/image-cropper/image-cropper-preview.svelte b/frontend/src/lib/components/ui/image-cropper/image-cropper-preview.svelte index e3e044b..18da825 100644 --- a/frontend/src/lib/components/ui/image-cropper/image-cropper-preview.svelte +++ b/frontend/src/lib/components/ui/image-cropper/image-cropper-preview.svelte @@ -5,7 +5,7 @@ import UploadIcon from '@lucide/svelte/icons/upload'; import { cn } from '$lib/utils.js'; - let { child, class: className }: ImageCropperPreviewProps = $props(); + const { child, class: className }: ImageCropperPreviewProps = $props(); const previewState = useImageCropperPreview(); diff --git a/frontend/src/lib/components/ui/map/MapArc.svelte b/frontend/src/lib/components/ui/map/MapArc.svelte index b4ff3e2..f43fd7d 100644 --- a/frontend/src/lib/components/ui/map/MapArc.svelte +++ b/frontend/src/lib/components/ui/map/MapArc.svelte @@ -52,7 +52,7 @@ ('top-center'); diff --git a/frontend/src/routes/account/+page.svelte b/frontend/src/routes/account/+page.svelte index c541a25..c45ce1a 100644 --- a/frontend/src/routes/account/+page.svelte +++ b/frontend/src/routes/account/+page.svelte @@ -61,7 +61,7 @@ // =============== Tab State =============== let activeTab = $state<'general' | 'history' | 'referral' | 'cards' | 'admin'>('general'); - let canSaveCards = $derived( + const canSaveCards = $derived( authStore.currentUser?.role === 'verified_email' || authStore.currentUser?.role === 'affiliate' ); @@ -122,9 +122,9 @@ return path + ' Z'; } - let notifPrefs = $state({ emailEnabled: true, smsEnabled: true, browserPushEnabled: true }); + const notifPrefs = $state({ emailEnabled: true, smsEnabled: true, browserPushEnabled: true }); - let loadingCards = $state(false); + const loadingCards = $state(false); let cardToDelete = $state(null); let showDeleteCardDialog = $state(false); @@ -192,19 +192,19 @@ } // Derived validations for Add Saved Card form - let newCardExpiryParts = $derived(parseExpiryParts(newCardExpiry)); - let isNewCardExpiryInPast = $derived( + const newCardExpiryParts = $derived(parseExpiryParts(newCardExpiry)); + const isNewCardExpiryInPast = $derived( newCardExpiryParts !== null && (() => { const expiryDate = new SvelteDate(newCardExpiryParts.year, newCardExpiryParts.month); return expiryDate < new SvelteDate(); })() ); - let isNewCardExpiryInvalidMonth = $derived( + const isNewCardExpiryInvalidMonth = $derived( /^\d{2}\/\d{2}$/.test(newCardExpiry) && newCardExpiryParts === null ); - let addCardError = $derived( + const addCardError = $derived( newCardNumber.length > 0 && !isValidLuhn(newCardNumber) ? 'Invalid card number' : isNewCardExpiryInvalidMonth @@ -216,24 +216,24 @@ : null ); - let isAddCardValid = $derived( + const isAddCardValid = $derived( isValidLuhn(newCardNumber) && /^\d{2}\/\d{2}$/.test(newCardExpiry) && newCardCVC.length >= 3 ); // Derived validations for Buy Gift Card form - let buyNewCardExpiryParts = $derived(parseExpiryParts(buyNewCardExpiry)); - let isBuyNewCardExpiryInPast = $derived( + const buyNewCardExpiryParts = $derived(parseExpiryParts(buyNewCardExpiry)); + const isBuyNewCardExpiryInPast = $derived( buyNewCardExpiryParts !== null && (() => { const expiryDate = new SvelteDate(buyNewCardExpiryParts.year, buyNewCardExpiryParts.month); return expiryDate < new SvelteDate(); })() ); - let isBuyNewCardExpiryInvalidMonth = $derived( + const isBuyNewCardExpiryInvalidMonth = $derived( /^\d{2}\/\d{2}$/.test(buyNewCardExpiry) && buyNewCardExpiryParts === null ); - let buyCardError = $derived( + const buyCardError = $derived( buyNewCardNumber.length > 0 && !isValidLuhn(buyNewCardNumber) ? 'Invalid card number' : isBuyNewCardExpiryInvalidMonth @@ -245,7 +245,7 @@ : null ); - let isBuyCardValid = $derived( + const isBuyCardValid = $derived( buySelectedCard !== '' || (isValidLuhn(buyNewCardNumber) && buyNewCardExpiryParts !== null && @@ -1142,11 +1142,11 @@ let changingPassword = $state(false); // Password strength using zxcvbn - let newPasswordStrength = $derived(passwordData.new ? zxcvbn(passwordData.new) : null); - let isPasswordStrongEnough = $derived( + const newPasswordStrength = $derived(passwordData.new ? zxcvbn(passwordData.new) : null); + const isPasswordStrongEnough = $derived( !passwordData.new || newPasswordStrength === null || newPasswordStrength.score >= 2 ); - let passwordsMatch = $derived( + const passwordsMatch = $derived( passwordData.confirm === '' || passwordData.new === passwordData.confirm ); diff --git a/frontend/src/routes/admin/notifications/+page.svelte b/frontend/src/routes/admin/notifications/+page.svelte index 17883e2..9be1cef 100644 --- a/frontend/src/routes/admin/notifications/+page.svelte +++ b/frontend/src/routes/admin/notifications/+page.svelte @@ -63,7 +63,7 @@ let loading = $state(true); let error = $state(false); let page = $state(1); - let perPage = $state(20); + const perPage = $state(20); let total = $state(0); let includeAcknowledged = $state(false); diff --git a/frontend/src/routes/api/[...path]/+server.ts b/frontend/src/routes/api/[...path]/+server.ts index fab3219..65857ed 100644 --- a/frontend/src/routes/api/[...path]/+server.ts +++ b/frontend/src/routes/api/[...path]/+server.ts @@ -16,7 +16,7 @@ async function proxyRequest(request: Request, path: string) { headers.delete('content-length'); let body: BodyInit | undefined; - let fetchOptions: RequestInit = {}; + const fetchOptions: RequestInit = {}; if (!['GET', 'HEAD'].includes(request.method)) { const contentType = request.headers.get('content-type') || ''; if (contentType.includes('multipart/form-data')) { diff --git a/frontend/src/routes/gdpr/+page.svelte b/frontend/src/routes/gdpr/+page.svelte index 52b14f2..2a834aa 100644 --- a/frontend/src/routes/gdpr/+page.svelte +++ b/frontend/src/routes/gdpr/+page.svelte @@ -295,7 +295,7 @@ return !!(r?.referred_by || (r?.referred_users && r.referred_users.length > 0)); } - let hasVat = $derived( + const hasVat = $derived( gdprData?.payments?.some((p) => p.vat_amount && Number(p.vat_amount) > 0) ?? false ); diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index 4cf65c3..d5d8a8d 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -224,7 +224,7 @@ } // when password changes, re-compute strength - let passwordStrength = $derived( + const passwordStrength = $derived( formData.password ? (() => { const result = zxcvbn(formData.password); @@ -245,7 +245,7 @@ ); // form completion check - let isFormComplete = $derived( + const isFormComplete = $derived( isLogin || (formData.firstName.trim() && formData.lastName.trim() && diff --git a/frontend/src/routes/pay-tip/[id]/+page.svelte b/frontend/src/routes/pay-tip/[id]/+page.svelte index ef25799..fa2bc76 100644 --- a/frontend/src/routes/pay-tip/[id]/+page.svelte +++ b/frontend/src/routes/pay-tip/[id]/+page.svelte @@ -41,7 +41,7 @@ // Tip selection state let selectedTip = $state(null); let customTip = $state(''); - let tipAmount = $derived( + const tipAmount = $derived( selectedTip !== null ? selectedTip : customTip ? parseFloat(customTip) || 0 : 0 ); diff --git a/frontend/src/routes/schedule/+page.svelte b/frontend/src/routes/schedule/+page.svelte index ff12025..9de47cf 100644 --- a/frontend/src/routes/schedule/+page.svelte +++ b/frontend/src/routes/schedule/+page.svelte @@ -16,7 +16,7 @@ let loading = $state(false); let selectedBookingId = $state(null); let showBookingModal = $state(false); - let businessSettings = $derived(getBusinessInfo()); + const businessSettings = $derived(getBusinessInfo()); $effect(() => { if (!browser) return; diff --git a/frontend/src/routes/tip/+page.svelte b/frontend/src/routes/tip/+page.svelte index 4821c24..1efa58d 100644 --- a/frontend/src/routes/tip/+page.svelte +++ b/frontend/src/routes/tip/+page.svelte @@ -47,7 +47,7 @@ let selectedTip = $state(null); let customTip = $state(''); - let tipAmount = $derived( + const tipAmount = $derived( selectedTip !== null ? selectedTip : customTip ? parseFloat(customTip) || 0 : 0 );