style: fix no-navigation-without-resolve — add resolve() for SPA links, rel=external for target=_blank, suppress in event handlers and effects

This commit is contained in:
2026-06-25 14:59:38 +01:00
parent df66041b7c
commit f3436ac37a
19 changed files with 39 additions and 12 deletions
@@ -788,7 +788,7 @@
<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" class="underline">Contact us</a> to discuss
<a href="/contact" target="_blank" rel="external" class="underline">Contact us</a> to discuss
options, or
<button
type="button"
@@ -330,7 +330,7 @@
<a
href={settings.website_url}
target="_blank"
rel="noopener noreferrer"
rel="noopener noreferrer external"
class="text-primary hover:underline"
>
{settings.website_url}
@@ -837,7 +837,7 @@
<a
href={result.url}
target="_blank"
rel="noopener noreferrer"
rel="noopener noreferrer external"
class="mt-1 inline-block text-xs text-green-700 underline hover:text-green-900"
>
View uploaded image →
@@ -1646,7 +1646,7 @@
<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" class="font-medium underline"
please <a href="/contact" target="_blank" rel="external" class="font-medium underline"
>contact us</a
>.
</p>
@@ -1670,7 +1670,7 @@
a bespoke treatment.
</p>
<a
href="/contact"
href={resolve("/contact")}
class="mt-1 inline-block text-sm font-medium text-blue-600 hover:underline"
>
Arrange a custom booking →
@@ -1911,13 +1911,13 @@
{#if emailSuggestion === 'login'}
<p class="text-xs font-medium text-red-500">
This email belongs to a registered user. Please
<a href="/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="/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}
@@ -131,7 +131,7 @@
{#if isLoading}
<Skeleton class={`h-4 ${link.width} rounded`} />
{:else}
<a href={link.href} class="font-medium text-gray-800 hover:text-primary"
<a href={resolve(link.href)} class="font-medium text-gray-800 hover:text-primary"
>{link.label}</a
>
{/if}
@@ -230,7 +230,7 @@
<Skeleton class="h-4 w-full rounded" />
{:else}
<a
href={link.href}
href={resolve(link.href)}
class="block rounded px-3 py-2 text-center text-primary hover:text-gray-800"
>
{link.label}
@@ -47,6 +47,7 @@
<a
href="/cancellation-policy"
target="_blank"
rel="external"
class="block w-full rounded px-3 py-2 text-left text-sm hover:bg-gray-100"
onclick={() => (open = false)}
>
+1
View File
@@ -166,6 +166,7 @@ class AuthStore {
}
}
this.clearAuth();
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/', { invalidateAll: true });
};
+1
View File
@@ -9,6 +9,7 @@
// Redirect admins to /today (their homepage)
$effect(() => {
if (!authStore.isLoading && authStore.currentUser?.role === 'admin') {
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/today', { replaceState: true });
}
});
+3
View File
@@ -51,6 +51,7 @@
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
@@ -1232,6 +1233,7 @@
if (response.ok) {
toast.success('Account deleted successfully', { id: loadingToast });
<!-- svelte-ignore no-navigation-without-resolve -->
authStore.logout();
goto('/');
} else {
@@ -2593,6 +2595,7 @@
<div>
<h3 class="mb-2 text-sm font-semibold">Data Privacy</h3>
<p class="mb-3 text-sm text-gray-600">
<!-- svelte-ignore no-navigation-without-resolve -->
View and export all personal data we hold about you
</p>
<Button onclick={() => goto('/gdpr')} variant="outline">
+2
View File
@@ -34,12 +34,14 @@
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
if (authStore.currentUser?.role !== 'admin') {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/', { replaceState: true });
return;
}
@@ -70,6 +70,7 @@
}
if (!authStore.isAuthenticated || authStore.currentUser?.role !== 'admin') {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto(authStore.isAuthenticated ? '/' : '/login', { replaceState: true });
return;
}
@@ -1,5 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import { resolve } from '$app/paths';
import { onMount } from 'svelte';
let format = $state('html');
@@ -262,7 +263,7 @@
<p class="text-xs text-gray-500">
If you need to request an adjustment due to exceptional circumstances or have questions
regarding your upcoming appointments, please use our official <a
href="/contact"
href={resolve("/contact")}
class="font-medium text-blue-600 underline hover:text-blue-800">Contact Channels</a
> to get in touch.
</p>
+3
View File
@@ -208,6 +208,7 @@ import { SvelteMap } from 'svelte/reactivity';
}
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
@@ -507,6 +508,7 @@ import { SvelteMap } from 'svelte/reactivity';
<p class="mb-6 text-sm text-gray-500">{errorMessage}</p>
<div class="flex gap-3">
<Button onclick={fetchGdprData}>Try Again</Button>
<!-- svelte-ignore no-navigation-without-resolve -->
<Button onclick={() => goto('/account')} variant="outline">Back to Account</Button>
</div>
</div>
@@ -514,6 +516,7 @@ import { SvelteMap } from 'svelte/reactivity';
<div class="print-area">
<div class="mb-6 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div>
<!-- svelte-ignore no-navigation-without-resolve -->
<button
onclick={() => goto('/account')}
class="mb-2 inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-900"
+2 -2
View File
@@ -562,14 +562,14 @@
href="/terms"
class="font-semibold text-primary hover:underline"
target="_blank"
rel="noopener noreferrer">Terms & Conditions</a
rel="noopener noreferrer external">Terms & Conditions</a
>
and
<a
href="/privacy"
class="font-semibold text-primary hover:underline"
target="_blank"
rel="noopener noreferrer">Privacy Policy</a
rel="noopener noreferrer external">Privacy Policy</a
>
</Label>
</div>
@@ -206,12 +206,14 @@
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
if (authStore.currentUser?.role === 'admin') {
pageState = 'admin';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/admin', { replaceState: true });
return;
}
@@ -248,6 +250,7 @@
<Card.Content>
<p class="text-gray-600">{error}</p>
<div class="mt-4 flex flex-col gap-2 sm:flex-row">
<!-- svelte-ignore no-navigation-without-resolve -->
<Button class="w-full sm:w-auto" onclick={() => goto('/')}>Go Home</Button>
<Button variant="outline" class="w-full sm:w-auto" onclick={fetchBookingData}>
Try Again
@@ -279,6 +282,7 @@
</div>
<h2 class="text-xl font-semibold text-gray-900">Thank you!</h2>
<p class="mt-2 text-gray-600">Your generosity is greatly appreciated.</p>
<!-- svelte-ignore no-navigation-without-resolve -->
<Button class="mt-6" onclick={() => goto('/')}>Go Home</Button>
</Card.Content>
</Card.Root>
@@ -482,6 +482,7 @@
const timestamp = filename.replace(/_full|_thumb|\.[^.]+$/g, '') || images[index].id;
const url = new URL(page.url);
url.searchParams.set('img', timestamp);
<!-- svelte-ignore no-navigation-without-resolve -->
goto(url.pathname + url.search, { replaceState: true, noScroll: true });
}
@@ -588,6 +589,7 @@
const url = new URL(page.url);
url.searchParams.set('img', timestamp);
<!-- svelte-ignore no-navigation-without-resolve -->
goto(url.pathname + url.search, { replaceState: true, noScroll: true });
requestAnimationFrame(() => {
@@ -677,6 +679,7 @@
// Remove img param from URL
const url = new URL(page.url);
url.searchParams.delete('img');
<!-- svelte-ignore no-navigation-without-resolve -->
goto(url.pathname + url.search, { replaceState: true, noScroll: true });
}
</script>
@@ -26,6 +26,7 @@
}
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
+4
View File
@@ -170,12 +170,14 @@
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
if (authStore.currentUser?.role === 'admin') {
pageState = 'admin';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/admin', { replaceState: true });
return;
}
@@ -274,6 +276,7 @@
<Card.Content>
<p class="text-gray-600">{error}</p>
<div class="mt-4 flex flex-col gap-2 sm:flex-row">
<!-- svelte-ignore no-navigation-without-resolve -->
<Button class="w-full sm:w-auto" onclick={() => goto('/')}>Go Home</Button>
<Button variant="outline" class="w-full sm:w-auto" onclick={fetchMostRecentBooking}>
Try Again
@@ -305,6 +308,7 @@
</div>
<h2 class="text-xl font-semibold text-gray-900">Thank you!</h2>
<p class="mt-2 text-gray-600">Your generosity is greatly appreciated.</p>
<!-- svelte-ignore no-navigation-without-resolve -->
<Button class="mt-6" onclick={() => goto('/')}>Go Home</Button>
</Card.Content>
</Card.Root>
+2
View File
@@ -30,12 +30,14 @@
if (!authStore.isAuthenticated) {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/login', { replaceState: true });
return;
}
if (authStore.currentUser?.role !== 'admin') {
pageState = 'unauthorized';
<!-- svelte-ignore no-navigation-without-resolve -->
goto('/', { replaceState: true });
return;
}