257 lines
6.7 KiB
Svelte
257 lines
6.7 KiB
Svelte
|
|
<script lang="ts">
|
||
|
|
import { enhance } from '$app/forms';
|
||
|
|
import { Button } from '$lib/components/ui/button';
|
||
|
|
import { Input } from '$lib/components/ui/input';
|
||
|
|
import { Label } from '$lib/components/ui/label';
|
||
|
|
import { DatePicker, NationalitySelect } from '$lib/components/ui';
|
||
|
|
import { FormMessage, LoadingSpinner } from '$lib/components/auth';
|
||
|
|
import { CalendarDate, today, getLocalTimeZone } from '@internationalized/date';
|
||
|
|
|
||
|
|
let { form } = $props();
|
||
|
|
|
||
|
|
let loading = $state(false);
|
||
|
|
let selectedNationalities = $state<string[]>([]);
|
||
|
|
let dateOfBirth = $state<CalendarDate | undefined>(undefined);
|
||
|
|
|
||
|
|
// Calculate max date for 18+ years old
|
||
|
|
const todayDate = today(getLocalTimeZone());
|
||
|
|
const maxDateOfBirth = new CalendarDate(
|
||
|
|
todayDate.year - 18,
|
||
|
|
todayDate.month,
|
||
|
|
todayDate.day
|
||
|
|
);
|
||
|
|
|
||
|
|
// Parse form data on error to restore values
|
||
|
|
$effect(() => {
|
||
|
|
if (form?.nationality) {
|
||
|
|
selectedNationalities = form.nationality.split(',').filter(Boolean);
|
||
|
|
}
|
||
|
|
if (form?.date_of_birth) {
|
||
|
|
const [year, month, day] = form.date_of_birth.split('-').map(Number);
|
||
|
|
dateOfBirth = new CalendarDate(year, month, day);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<svelte:head>
|
||
|
|
<title>Sign Up | Monaco USA</title>
|
||
|
|
</svelte:head>
|
||
|
|
|
||
|
|
<div class="space-y-6">
|
||
|
|
<div class="text-center">
|
||
|
|
<h2 class="text-xl font-semibold text-slate-900">Create your account</h2>
|
||
|
|
<p class="mt-1 text-sm text-slate-500">Join the Monaco USA community</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{#if form?.error}
|
||
|
|
<FormMessage type="error" message={form.error} />
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
{#if form?.success}
|
||
|
|
<FormMessage type="success" message={form.success} />
|
||
|
|
{:else}
|
||
|
|
<form
|
||
|
|
method="POST"
|
||
|
|
use:enhance={() => {
|
||
|
|
loading = true;
|
||
|
|
return async ({ update }) => {
|
||
|
|
loading = false;
|
||
|
|
await update();
|
||
|
|
};
|
||
|
|
}}
|
||
|
|
class="space-y-4"
|
||
|
|
>
|
||
|
|
<!-- Name Row -->
|
||
|
|
<div class="grid grid-cols-2 gap-4">
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="first_name" class="text-sm font-medium text-slate-700">
|
||
|
|
First name <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="first_name"
|
||
|
|
name="first_name"
|
||
|
|
type="text"
|
||
|
|
placeholder="John"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
value={form?.first_name || ''}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="given-name"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="last_name" class="text-sm font-medium text-slate-700">
|
||
|
|
Last name <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="last_name"
|
||
|
|
name="last_name"
|
||
|
|
type="text"
|
||
|
|
placeholder="Doe"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
value={form?.last_name || ''}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="family-name"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Email -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="email" class="text-sm font-medium text-slate-700">
|
||
|
|
Email address <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="email"
|
||
|
|
name="email"
|
||
|
|
type="email"
|
||
|
|
placeholder="you@example.com"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
value={form?.email || ''}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="email"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Phone -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="phone" class="text-sm font-medium text-slate-700">
|
||
|
|
Phone number <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="phone"
|
||
|
|
name="phone"
|
||
|
|
type="tel"
|
||
|
|
placeholder="+33 6 12 34 56 78"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
value={form?.phone || ''}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="tel"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Date of Birth - New DatePicker -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label class="text-sm font-medium text-slate-700">
|
||
|
|
Date of birth <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<DatePicker
|
||
|
|
bind:value={dateOfBirth}
|
||
|
|
maxValue={maxDateOfBirth}
|
||
|
|
disabled={loading}
|
||
|
|
placeholder="Select your birth date"
|
||
|
|
name="date_of_birth"
|
||
|
|
/>
|
||
|
|
<p class="text-xs text-slate-500">You must be at least 18 years old to join.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Address -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="address" class="text-sm font-medium text-slate-700">
|
||
|
|
Address <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="address"
|
||
|
|
name="address"
|
||
|
|
type="text"
|
||
|
|
placeholder="123 Avenue Princesse Grace, Monaco"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
value={form?.address || ''}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="street-address"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Nationality - New Dropdown Select -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label class="text-sm font-medium text-slate-700">
|
||
|
|
Nationality <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<NationalitySelect
|
||
|
|
bind:value={selectedNationalities}
|
||
|
|
disabled={loading}
|
||
|
|
placeholder="Search and select nationalities..."
|
||
|
|
name="nationality"
|
||
|
|
/>
|
||
|
|
{#if selectedNationalities.length === 0}
|
||
|
|
<p class="text-xs text-slate-500">Select at least one nationality.</p>
|
||
|
|
{/if}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Password -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="password" class="text-sm font-medium text-slate-700">
|
||
|
|
Password <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="password"
|
||
|
|
name="password"
|
||
|
|
type="password"
|
||
|
|
placeholder="Create a strong password"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
minlength={8}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="new-password"
|
||
|
|
/>
|
||
|
|
<p class="text-xs text-slate-500">At least 8 characters.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Confirm Password -->
|
||
|
|
<div class="space-y-2">
|
||
|
|
<Label for="confirm_password" class="text-sm font-medium text-slate-700">
|
||
|
|
Confirm password <span class="text-monaco-600">*</span>
|
||
|
|
</Label>
|
||
|
|
<Input
|
||
|
|
id="confirm_password"
|
||
|
|
name="confirm_password"
|
||
|
|
type="password"
|
||
|
|
placeholder="Confirm your password"
|
||
|
|
required
|
||
|
|
disabled={loading}
|
||
|
|
minlength={8}
|
||
|
|
class="h-11"
|
||
|
|
autocomplete="new-password"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Terms Agreement -->
|
||
|
|
<div class="flex items-start gap-2">
|
||
|
|
<input
|
||
|
|
type="checkbox"
|
||
|
|
id="terms"
|
||
|
|
name="terms"
|
||
|
|
required
|
||
|
|
class="mt-1 h-4 w-4 rounded border-slate-300 text-monaco-600 focus:ring-monaco-500"
|
||
|
|
/>
|
||
|
|
<label for="terms" class="text-sm text-slate-600">
|
||
|
|
I agree to the
|
||
|
|
<a href="/terms" class="text-monaco-600 hover:underline">Terms of Service</a>
|
||
|
|
and
|
||
|
|
<a href="/privacy" class="text-monaco-600 hover:underline">Privacy Policy</a>.
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Button type="submit" variant="monaco" size="lg" class="w-full" disabled={loading}>
|
||
|
|
{#if loading}
|
||
|
|
<LoadingSpinner size="sm" class="mr-2" />
|
||
|
|
Creating account...
|
||
|
|
{:else}
|
||
|
|
Create account
|
||
|
|
{/if}
|
||
|
|
</Button>
|
||
|
|
</form>
|
||
|
|
{/if}
|
||
|
|
|
||
|
|
<div class="text-center">
|
||
|
|
<p class="text-sm text-slate-600">
|
||
|
|
Already have an account?
|
||
|
|
<a href="/login" class="font-medium text-monaco-600 hover:text-monaco-700"> Sign in </a>
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|