refactor(frontend): migrate email fields to EmailInput component

Replace raw <Input type="email"> with <EmailInput> in login (register + login), account (gift card recipient), BookingFlow (guest email), and GiftCardsManagement (admin gift card email).

Removes 30-line validateEmail() function from login page (now handled by EmailInput internally).

Removes validateEmailFormat() from BookingFlow (EmailInput handles format validation; debounced backend email check preserved via onvaluechange/onblur).

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-06-12 10:51:07 +01:00
co-authored by Sisyphus
parent 1cadbbe968
commit 18eaa00479
4 changed files with 48 additions and 100 deletions
+12 -12
View File
@@ -28,6 +28,7 @@
import { Checkbox } from '$lib/components/ui/checkbox';
import * as Card from '$lib/components/ui/card';
import { Input } from '$lib/components/ui/input';
import { EmailInput } from '$lib/components/ui/email-input/index.js';
import { PhoneInput } from '$lib/components/ui/phone-input/index.js';
import { Separator } from '$lib/components/ui/separator';
import * as AlertDialog from '$lib/components/ui/alert-dialog';
@@ -2095,18 +2096,17 @@
</div>
{#if buyRecipientType === 'friend'}
<div class="space-y-2">
<label for="recipient-email" class="text-sm font-medium text-gray-700"
>Friend's Email (Optional)</label
>
<Input
id="recipient-email"
type="email"
placeholder="friend@example.com (blank to send to yourself)"
bind:value={buyRecipientEmail}
class="mt-1"
/>
</div>
<div class="space-y-2">
<label for="recipient-email" class="text-sm font-medium text-gray-700"
>Friend's Email (Optional)</label
>
<EmailInput
id="recipient-email"
bind:value={buyRecipientEmail}
placeholder="friend@example.com (blank to send to yourself)"
class="mt-1"
/>
</div>
{/if}
<div class="space-y-3 border-t pt-2">
+26 -66
View File
@@ -2,6 +2,7 @@
import { resolve } from '$app/paths';
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { EmailInput } from '$lib/components/ui/email-input/index.js';
import { PhoneInput } from '$lib/components/ui/phone-input/index.js';
import { Label } from '$lib/components/ui/label/index.js';
import { Separator } from '$lib/components/ui/separator/index.js';
@@ -67,37 +68,6 @@
};
}
// Email validation
function validateEmail(email: string): boolean {
// ASCII-only email regex (safe with most providers)
const asciiRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// Unicode-friendly regex (valid per RFC 6531)
const unicodeRegex = /^[\p{L}\p{M}0-9._%+-]+@[\p{L}\p{M}0-9.-]+\.[\p{L}\p{M}]{2,}$/u;
if (!email) {
validationErrors.email = '';
return true;
}
if (asciiRegex.test(email)) {
// OK: standard ASCII address
validationErrors.email = '';
return true;
} else if (unicodeRegex.test(email)) {
// Looks like a valid internationalised address, but unsupported
validationErrors.email =
'This looks like a valid international email address, ' +
'but our providers only supports standard (A-Z, 0-9) email addresses. ' +
'We are sorry. Please use an ASCII-compatible email.';
return false;
} else {
// Not even valid under the Unicode spec
validationErrors.email = 'Invalid email format';
return false;
}
}
function validatePhone(phone: string): boolean {
const valid = isValidUKPhone(phone);
validationErrors.phone = valid ? '' : 'Please enter a valid UK phone number';
@@ -217,7 +187,7 @@
}
// Validate before submitting
const isEmailValid = validateEmail(formData.email);
const isEmailValid = !validationErrors.email && formData.email.trim().length > 0;
const isPhoneValid = validatePhone(formData.phone);
const isAgeValid = validateAge(formData.dateOfBirth);
@@ -380,23 +350,18 @@
{#if !isLogin}
<!-- SECTION 1: LOGIN DETAILS -->
<div class="space-y-4">
<h3 class="text-lg font-semibold">What we need to log you in</h3>
<h3 class="text-lg font-semibold">What we need to log you in</h3>
<div class="space-y-2">
<RequiredLabel forId="email" text="Email" />
<Input
id="email"
type="email"
placeholder="john@example.com"
maxlength={255}
bind:value={formData.email}
onblur={() => validateEmail(formData.email)}
required
/>
{#if validationErrors.email}
<p class="text-sm text-red-500">{validationErrors.email}</p>
{/if}
</div>
<div class="space-y-2">
<RequiredLabel forId="email" text="Email" />
<EmailInput
id="email"
bind:value={formData.email}
bind:error={validationErrors.email}
placeholder="john@example.com"
required
/>
</div>
<div class="space-y-2">
<RequiredLabel forId="password" text="Password" />
@@ -540,25 +505,20 @@
</div>
{:else}
<!-- Login Fields (Simple inline layout) -->
<div class="space-y-4">
<div class="space-y-2">
<RequiredLabel forId="email" text="Email" />
<Input
id="email"
type="email"
placeholder="john@example.com"
maxlength={255}
bind:value={formData.email}
onblur={() => validateEmail(formData.email)}
required
/>
{#if validationErrors.email}
<p class="text-sm text-red-500">{validationErrors.email}</p>
{/if}
</div>
<div class="space-y-4">
<div class="space-y-2">
<RequiredLabel forId="email" text="Email" />
<EmailInput
id="email"
bind:value={formData.email}
bind:error={validationErrors.email}
placeholder="john@example.com"
required
/>
</div>
<div class="space-y-2">
<RequiredLabel forId="password" text="Password" />
<div class="space-y-2">
<RequiredLabel forId="password" text="Password" />
<Input
id="password"
type="password"