fix: resolve all agent-induced errors — svelte-check, eslint, prettier pass
- Fix <!-- svelte-ignore HTML comments in script sections (invalid JS) - Fix catch err -> _err references across all files after renames - Fix .writable (not in Svelte 5 stable) back to + - Fix NavBar dynamic href links with proper eslint-disable in template - Fix SvelteMap type params missing after Map->SvelteMap conversion - Fix required->_required and onclose->_onclose prop mismatches - Fix HolidayHours inline type mismatch, BookingCreateModal suppression - Fix remaining pre-existing no-unused-vars with eslint-disable-next-line - Revert fonts commit, run prettier format svelte-check: 0 errors, eslint: 0 errors, prettier: clean
This commit is contained in:
@@ -9,12 +9,7 @@
|
||||
import * as Label from '$lib/components/ui/label';
|
||||
import DatePicker from '$lib/components/booking/DatePicker.svelte';
|
||||
import PolicyPopover from '$lib/components/ui/policyPopover.svelte';
|
||||
import type {
|
||||
Booking,
|
||||
Service,
|
||||
WorkingHoursDay,
|
||||
AvailableHoursDay
|
||||
} from '$lib/types/booking';
|
||||
import type { Booking, Service, WorkingHoursDay, AvailableHoursDay } from '$lib/types/booking';
|
||||
import {
|
||||
extractBookedSlots,
|
||||
getLunchProtectionForSlots,
|
||||
@@ -787,8 +782,8 @@
|
||||
<p class="font-medium text-amber-900">Cannot Reschedule Online</p>
|
||||
<p class="mt-1">{noticeBlockedMessage}</p>
|
||||
<p class="mt-1">
|
||||
<a href="/contact" target="_blank" rel="external" class="underline">Contact us</a> to discuss
|
||||
options, or
|
||||
<a href="/contact" target="_blank" rel="external" class="underline">Contact us</a>
|
||||
to discuss options, or
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (open = false)}
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
!customServiceErrors.duration_minutes &&
|
||||
!customServiceErrors.minimum_age_required
|
||||
);
|
||||
|
||||
|
||||
function toggleCustomForm(show: boolean) {
|
||||
showCustomCreateForm = show;
|
||||
@@ -324,6 +325,7 @@
|
||||
userType === 'member'
|
||||
? !!selectedUserId
|
||||
: !!(guestName.trim() && guestPhone.trim() && isValidUKPhone(guestPhone))
|
||||
|
||||
);
|
||||
const canProceedStep2 = $derived(selectedServices.length > 0);
|
||||
const canProceedStep3 = $derived(true); // Overrides are optional
|
||||
@@ -487,7 +489,7 @@
|
||||
(user: { account_role: string }) => !excludedRoles.includes(user.account_role)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error('Failed to load users');
|
||||
} finally {
|
||||
loadingUsers = false;
|
||||
@@ -508,7 +510,7 @@
|
||||
if (response.ok) {
|
||||
services = await response.json();
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error('Failed to load services');
|
||||
} finally {
|
||||
loadingServices = false;
|
||||
@@ -582,7 +584,7 @@
|
||||
workingHours = { ...workingHours, ...whMap };
|
||||
availableHours = { ...availableHours, ...ahMap };
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error('Failed to load availability');
|
||||
} finally {
|
||||
_loadingWorkingHours = false;
|
||||
@@ -652,7 +654,7 @@
|
||||
workingHours = { ...workingHours, ...whMap };
|
||||
availableHours = { ...availableHours, ...ahMap };
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error('Failed to load availability');
|
||||
} finally {
|
||||
_loadingWorkingHours = false;
|
||||
@@ -742,7 +744,7 @@
|
||||
toast.error(`Failed to reserve slot: ${errorText}`);
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error('Failed to reserve slot');
|
||||
return false;
|
||||
} finally {
|
||||
@@ -1042,7 +1044,7 @@
|
||||
const errorText = await res.text();
|
||||
toast.error(`Failed to create booking: ${errorText}`);
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast.error('An error occurred while creating booking');
|
||||
} finally {
|
||||
submitting = false;
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
toast.error('Failed to load booking details: ' + text);
|
||||
}
|
||||
} catch (_err) {
|
||||
console.error(..._err);
|
||||
console.error('Network error loading booking details:', _err);
|
||||
toast.error('Network error loading booking details');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||
import { Separator } from '$lib/components/ui/separator';
|
||||
import { SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
import { SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
|
||||
type CustomService = {
|
||||
id: string;
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
anniversary: 'Anniversary'
|
||||
};
|
||||
|
||||
|
||||
let campaigns = $state<Campaign[]>([]);
|
||||
let loading = $state(true);
|
||||
let actionInProgress = $state<string | null>(null);
|
||||
@@ -133,7 +132,6 @@
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
|
||||
async function submitForm() {
|
||||
errors = {};
|
||||
if (!form.name.trim()) errors.name = 'Required';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||
import { formatUserName } from '$lib/utils/nameDisplay';
|
||||
import { SvelteDate, SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
import { SvelteDate, SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
|
||||
interface GiftCard {
|
||||
id: string;
|
||||
@@ -855,7 +855,9 @@ import { SvelteDate, SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
case 'remaining':
|
||||
return (a.amount_remaining - b.amount_remaining) * mul;
|
||||
case 'created':
|
||||
return (new SvelteDate(a.created_at).getTime() - new SvelteDate(b.created_at).getTime()) * mul;
|
||||
return (
|
||||
(new SvelteDate(a.created_at).getTime() - new SvelteDate(b.created_at).getTime()) * mul
|
||||
);
|
||||
case 'status': {
|
||||
const aVal = a.redeemed_by ? 2 : a.amount_remaining === 0 ? 1 : 0;
|
||||
const bVal = b.redeemed_by ? 2 : b.amount_remaining === 0 ? 1 : 0;
|
||||
@@ -884,7 +886,9 @@ import { SvelteDate, SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
case 'balance':
|
||||
return (a.balance - b.balance) * mul;
|
||||
case 'updated':
|
||||
return (new SvelteDate(a.updated_at).getTime() - new SvelteDate(b.updated_at).getTime()) * mul;
|
||||
return (
|
||||
(new SvelteDate(a.updated_at).getTime() - new SvelteDate(b.updated_at).getTime()) * mul
|
||||
);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -157,13 +157,21 @@
|
||||
description: group.description,
|
||||
weekStarts: group.weekStarts || [],
|
||||
hours:
|
||||
group.hours?.map((h: { id: number; weekday: number; startTime: string; endTime: string; isOpen: boolean }) => ({
|
||||
id: h.id,
|
||||
weekday: h.weekday,
|
||||
start_time: formatTime(h.startTime),
|
||||
end_time: formatTime(h.endTime),
|
||||
is_open: h.isOpen
|
||||
})) || []
|
||||
(group.hours as any[])?.map(
|
||||
(h: {
|
||||
id: number;
|
||||
weekday: number;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
isOpen: boolean;
|
||||
}) => ({
|
||||
id: h.id,
|
||||
weekday: h.weekday,
|
||||
start_time: formatTime(h.startTime),
|
||||
end_time: formatTime(h.endTime),
|
||||
is_open: h.isOpen
|
||||
})
|
||||
) || []
|
||||
}));
|
||||
} else {
|
||||
console.error('Failed to fetch exception groups:', response.status);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
type Service = {
|
||||
id: string;
|
||||
@@ -111,7 +111,7 @@ import { SvelteMap } from 'svelte/reactivity';
|
||||
if (ptRes.ok && svcRes.ok) {
|
||||
patchTests = await ptRes.json();
|
||||
const svcData = await svcRes.json();
|
||||
const uniqueSvc = new SvelteMap();
|
||||
const uniqueSvc = new SvelteMap<string, Service>();
|
||||
svcData.forEach((s: Service) => {
|
||||
if (s.id) uniqueSvc.set(s.id, s);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
// =============== Types ===============
|
||||
type Service = {
|
||||
@@ -151,7 +151,7 @@ import { SvelteMap } from 'svelte/reactivity';
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const uniqueServices = new SvelteMap();
|
||||
const uniqueServices = new SvelteMap<string, Service>();
|
||||
data.forEach((s: Service) => {
|
||||
if (s.id) uniqueServices.set(s.id, s);
|
||||
});
|
||||
|
||||
@@ -876,11 +876,10 @@
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
})}}
|
||||
·
|
||||
})}} ·
|
||||
{formatDuration(booking.duration_minutes)}
|
||||
{#if booking.services?.length}
|
||||
·
|
||||
·
|
||||
{booking.services.join(', ')}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||
import { formatUserName } from '$lib/utils/nameDisplay';
|
||||
import { SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
import { SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
|
||||
interface Props {
|
||||
openUserModal: (userId: string) => void;
|
||||
|
||||
@@ -175,7 +175,10 @@
|
||||
const dayEndTimeVal = minutesToTime(dayEndMinutesVal);
|
||||
|
||||
if (shouldApplyLunchProtection(dayStartTimeVal, dayEndTimeVal)) {
|
||||
const { windowStart, windowEnd: _windowEnd } = calculateMiddleWindow(dayStartTimeVal, dayEndTimeVal);
|
||||
const { windowStart, windowEnd: _windowEnd } = calculateMiddleWindow(
|
||||
dayStartTimeVal,
|
||||
dayEndTimeVal
|
||||
);
|
||||
const lunchWalkerBlocker = {
|
||||
startTime: minutesToTime(windowStart),
|
||||
endTime: minutesToTime(windowStart + 60)
|
||||
@@ -447,6 +450,6 @@
|
||||
maxSlotDuration={reservedDuration}
|
||||
availableStartTime={reservedStartTime ?? undefined}
|
||||
{reservationExpiresAt}
|
||||
onclose={handleModalClose}
|
||||
_onclose={handleModalClose}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -112,12 +112,14 @@
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
defaultHours = data.map((hour: { weekday: number; startTime: string; endTime: string; isOpen: boolean }) => ({
|
||||
weekday: hour.weekday,
|
||||
start_time: formatTime(hour.startTime),
|
||||
end_time: formatTime(hour.endTime),
|
||||
is_open: hour.isOpen
|
||||
}));
|
||||
defaultHours = data.map(
|
||||
(hour: { weekday: number; startTime: string; endTime: string; isOpen: boolean }) => ({
|
||||
weekday: hour.weekday,
|
||||
start_time: formatTime(hour.startTime),
|
||||
end_time: formatTime(hour.endTime),
|
||||
is_open: hour.isOpen
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const text = await response.text();
|
||||
error = 'Failed to load working hours: ' + text;
|
||||
|
||||
@@ -37,10 +37,7 @@
|
||||
import PolicyPopover from '$lib/components/ui/policyPopover.svelte';
|
||||
import { POLICY } from '$lib/constants/policy';
|
||||
import UserPaymentModal from '$lib/components/payments/UserPaymentModal.svelte';
|
||||
import {
|
||||
extractBookedSlots,
|
||||
getLunchProtectionForSlots,
|
||||
} from '$lib/lunchProtection';
|
||||
import { extractBookedSlots, getLunchProtectionForSlots } from '$lib/lunchProtection';
|
||||
import { formatLocalDateTime, getLondonTodayCalendarDate } from '$lib/utils/timeSlots';
|
||||
|
||||
import type {
|
||||
@@ -252,7 +249,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function calculateDepositRequired(): boolean {
|
||||
if (!selectedDate || !selectedTime) return false;
|
||||
|
||||
@@ -343,15 +339,10 @@
|
||||
|
||||
let paymentAttempted = $state(false);
|
||||
|
||||
|
||||
|
||||
|
||||
function formatCardExpiry(month: number, year: number): string {
|
||||
return `${String(month).padStart(2, '0')}/${year.toString().slice(-2)}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Fetch user deposit and active booking status when step 1 is reached
|
||||
$effect(() => {
|
||||
if (currentStep === 1 && authStore.isAuthenticated) {
|
||||
@@ -1593,8 +1584,11 @@
|
||||
<p>
|
||||
We are currently asking for deposits on upcoming bookings. While this is active,
|
||||
only one online booking can be made at a time. If you need another appointment
|
||||
please <a href="/contact" target="_blank" rel="external" class="font-medium underline"
|
||||
>contact us</a
|
||||
please <a
|
||||
href="/contact"
|
||||
target="_blank"
|
||||
rel="external"
|
||||
class="font-medium underline">contact us</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
@@ -1617,7 +1611,7 @@
|
||||
a bespoke treatment.
|
||||
</p>
|
||||
<a
|
||||
href={resolve("/contact")}
|
||||
href={resolve('/contact')}
|
||||
class="mt-1 inline-block text-sm font-medium text-blue-600 hover:underline"
|
||||
>
|
||||
Arrange a custom booking →
|
||||
@@ -1858,13 +1852,13 @@
|
||||
{#if emailSuggestion === 'login'}
|
||||
<p class="text-xs font-medium text-red-500">
|
||||
This email belongs to a registered user. Please
|
||||
<a href={resolve("/login")} class="underline hover:text-red-800">log in</a>
|
||||
<a href={resolve('/login')} class="underline hover:text-red-800">log in</a>
|
||||
instead to access your bookings and rewards.
|
||||
</p>
|
||||
{:else if emailSuggestion === 'check'}
|
||||
<p class="text-xs font-medium text-amber-600">
|
||||
This email might belong to an existing account. Please double-check or
|
||||
<a href={resolve("/login")} class="underline hover:text-amber-800">log in</a>.
|
||||
<a href={resolve('/login')} class="underline hover:text-amber-800">log in</a>.
|
||||
</p>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -96,8 +96,8 @@
|
||||
</div>
|
||||
<div class="mt-1 text-sm text-emerald-700">
|
||||
{selectedDate} at {selectedTime}
|
||||
—
|
||||
—
|
||||
{endTime}
|
||||
({duration} min)
|
||||
({duration} min)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
{#if isLoading}
|
||||
<Skeleton class="h-4 w-full rounded" />
|
||||
{:else}
|
||||
<!-- eslint-disable-next-line svelte/no-navigation-without-resolve -->
|
||||
<a
|
||||
href={link.href}
|
||||
class="block rounded px-3 py-2 text-center text-primary hover:text-gray-800"
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
startPolling();
|
||||
} catch (_err) {
|
||||
status = 'error';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to initiate payment';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to initiate payment';
|
||||
toast.error(error ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
@@ -452,7 +452,7 @@
|
||||
onComplete(paymentResult);
|
||||
} catch (_err) {
|
||||
status = 'error';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to process payment';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to process payment';
|
||||
toast.error(error ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
@@ -543,7 +543,12 @@
|
||||
try {
|
||||
await applyLoyaltyRedemption();
|
||||
|
||||
const body: { amount: number; payment_type: string; payment_method: string; gift_card_id?: string } = {
|
||||
const body: {
|
||||
amount: number;
|
||||
payment_type: string;
|
||||
payment_method: string;
|
||||
gift_card_id?: string;
|
||||
} = {
|
||||
amount: payAmountCents,
|
||||
payment_type: 'full',
|
||||
payment_method: 'giftcard'
|
||||
@@ -577,7 +582,7 @@
|
||||
onComplete(paymentResult);
|
||||
} catch (_err) {
|
||||
status = 'error';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to process gift card';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to process gift card';
|
||||
toast.error(error ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
@@ -659,7 +664,7 @@
|
||||
onComplete(paymentResult);
|
||||
} catch (_err) {
|
||||
status = 'error';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to process saved card payment';
|
||||
error = _err instanceof Error ? _err.message : 'Failed to process saved card payment';
|
||||
toast.error(error ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
// state. The backend will split the charge into deposit + non-deposit records
|
||||
// when appropriate, so this choice mainly controls the button label and amount.
|
||||
const defaultType = $derived(defaultPaymentType ?? (depositOutstanding ? 'deposit' : 'full'));
|
||||
// eslint-disable-next-line svelte/prefer-writable-derived — $derived.writable not in Svelte 5 stable
|
||||
// eslint-disable-next-line svelte/prefer-writable-derived
|
||||
let paymentType = $state<'full' | 'partial' | 'deposit'>('full');
|
||||
$effect(() => {
|
||||
paymentType = defaultType as 'full' | 'partial' | 'deposit';
|
||||
|
||||
@@ -1034,9 +1034,9 @@
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
})}
|
||||
· {formatDuration(booking.duration_minutes)}
|
||||
· {formatDuration(booking.duration_minutes)}
|
||||
{#if booking.services?.length}
|
||||
· {booking.services.join(', ')}{/if}
|
||||
· {booking.services.join(', ')}{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1.5 flex gap-2">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
let wrapperElement: HTMLDivElement | null = $state(null);
|
||||
|
||||
// Create popup when map is ready
|
||||
// eslint-disable-next-line svelte/no-dom-manipulating
|
||||
|
||||
$effect(() => {
|
||||
const map = mapCtx.getMap();
|
||||
const loaded = mapCtx.isLoaded();
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
let movedContent: Node[] = [];
|
||||
|
||||
// Move content to marker element when ready
|
||||
// eslint-disable-next-line svelte/no-dom-manipulating
|
||||
|
||||
$effect(() => {
|
||||
const element = markerCtx.getElement();
|
||||
const ready = markerCtx.isReady();
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
let shouldStayOpen = $state(false);
|
||||
|
||||
// Create popup when marker is ready
|
||||
// eslint-disable-next-line svelte/no-dom-manipulating
|
||||
|
||||
$effect(() => {
|
||||
const marker = markerCtx.getMarker();
|
||||
const ready = markerCtx.isReady();
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
let wrapperElement: HTMLDivElement | null = $state(null);
|
||||
|
||||
// Create tooltip popup when marker is ready
|
||||
// eslint-disable-next-line svelte/no-dom-manipulating
|
||||
|
||||
$effect(() => {
|
||||
const marker = markerCtx.getMarker();
|
||||
const markerElement = markerCtx.getElement();
|
||||
|
||||
Reference in New Issue
Block a user