mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
237 lines
7.1 KiB
Svelte
237 lines
7.1 KiB
Svelte
<script lang="ts">
|
||
import { enhance } from '$app/forms';
|
||
import { browser } from '$app/environment';
|
||
import Container from './Container.svelte';
|
||
|
||
let {
|
||
isOpen = $bindable(),
|
||
anonymousId = ''
|
||
}: {
|
||
isOpen: boolean;
|
||
anonymousId: string;
|
||
} = $props();
|
||
|
||
let mode = $state<'signin' | 'signup'>('signin');
|
||
let loading = $state(false);
|
||
let error = $state('');
|
||
let success = $state('');
|
||
|
||
let email = $state('');
|
||
let password = $state('');
|
||
let firstName = $state('');
|
||
let lastName = $state('');
|
||
|
||
function resetForm() {
|
||
email = '';
|
||
password = '';
|
||
firstName = '';
|
||
lastName = '';
|
||
error = '';
|
||
success = '';
|
||
}
|
||
|
||
function switchMode() {
|
||
mode = mode === 'signin' ? 'signup' : 'signin';
|
||
resetForm();
|
||
}
|
||
|
||
function closeModal() {
|
||
isOpen = false;
|
||
resetForm();
|
||
}
|
||
|
||
function handleKeydown(event: KeyboardEvent) {
|
||
if (event.key === 'Escape') {
|
||
closeModal();
|
||
}
|
||
}
|
||
|
||
function handleSubmit() {
|
||
loading = true;
|
||
error = '';
|
||
success = '';
|
||
}
|
||
|
||
function handleResult(event: any) {
|
||
loading = false;
|
||
const result = event.result;
|
||
|
||
if (result.type === 'success') {
|
||
if (result.data?.success) {
|
||
success = mode === 'signin' ? 'Signed in successfully!' : 'Account created successfully!';
|
||
setTimeout(() => {
|
||
if (browser) {
|
||
window.location.reload();
|
||
}
|
||
}, 1000);
|
||
} else if (result.data?.error) {
|
||
error = result.data.error;
|
||
}
|
||
} else if (result.type === 'failure') {
|
||
error = result.data?.error || 'An error occurred. Please try again.';
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<svelte:window onkeydown={handleKeydown} />
|
||
|
||
{#if isOpen}
|
||
<div class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm">
|
||
<Container class="w-full max-w-md p-6 relative">
|
||
<button
|
||
type="button"
|
||
onclick={closeModal}
|
||
class="absolute top-4 right-4 text-white hover:text-gray-300 text-2xl leading-none"
|
||
>
|
||
×
|
||
</button>
|
||
<div class="mb-6">
|
||
<h2 class="text-2xl font-bold text-white">
|
||
{mode === 'signin' ? 'Sign In' : 'Create Account'}
|
||
</h2>
|
||
</div>
|
||
|
||
<form method="POST" action="/auth/apple">
|
||
<input type="hidden" name="anonymousId" value={anonymousId} />
|
||
<button
|
||
type="submit"
|
||
class="w-full flex items-center justify-center gap-2 px-4 py-2.5 bg-white text-black rounded-md hover:bg-gray-100 transition-colors font-medium"
|
||
data-umami-event="Sign in with Apple"
|
||
>
|
||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
|
||
</svg>
|
||
Sign in with Apple
|
||
</button>
|
||
</form>
|
||
|
||
<div class="flex items-center my-4">
|
||
<div class="flex-1 border-t border-white/20"></div>
|
||
<span class="px-3 text-sm text-white/60">or</span>
|
||
<div class="flex-1 border-t border-white/20"></div>
|
||
</div>
|
||
|
||
<form
|
||
method="POST"
|
||
action={mode === 'signin' ? '/auth/signin' : '/auth/signup'}
|
||
use:enhance={({ formData }) => {
|
||
if (anonymousId) {
|
||
formData.append('anonymousId', anonymousId);
|
||
}
|
||
handleSubmit();
|
||
return handleResult;
|
||
}}
|
||
>
|
||
<div class="space-y-4">
|
||
{#if mode === 'signup'}
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="firstName" class="block text-sm font-medium text-white mb-1">
|
||
First Name
|
||
</label>
|
||
<input
|
||
id="firstName"
|
||
name="firstName"
|
||
type="text"
|
||
bind:value={firstName}
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white bg-transparent placeholder-white/60"
|
||
placeholder="John"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label for="lastName" class="block text-sm font-medium text-white mb-1">
|
||
Last Name
|
||
</label>
|
||
<input
|
||
id="lastName"
|
||
name="lastName"
|
||
type="text"
|
||
bind:value={lastName}
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white bg-transparent placeholder-white/60"
|
||
placeholder="Doe"
|
||
/>
|
||
</div>
|
||
</div>
|
||
{/if}
|
||
|
||
<div>
|
||
<label for="email" class="block text-sm font-medium text-white mb-1">
|
||
Email
|
||
</label>
|
||
<input
|
||
id="email"
|
||
name="email"
|
||
type="email"
|
||
required
|
||
bind:value={email}
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white bg-transparent placeholder-white/60"
|
||
placeholder="john@example.com"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="password" class="block text-sm font-medium text-white mb-1">
|
||
Password
|
||
</label>
|
||
<input
|
||
id="password"
|
||
name="password"
|
||
type="password"
|
||
required
|
||
bind:value={password}
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white bg-transparent placeholder-white/60"
|
||
placeholder="••••••••"
|
||
minlength="6"
|
||
/>
|
||
{#if mode === 'signup'}
|
||
<p class="text-xs text-white/80 mt-1">Minimum 6 characters</p>
|
||
{/if}
|
||
</div>
|
||
</div>
|
||
|
||
{#if error}
|
||
<div class="mt-4 p-3 bg-red-50 border border-red-200 rounded-md">
|
||
<p class="text-sm text-red-600">{error}</p>
|
||
</div>
|
||
{/if}
|
||
|
||
{#if success}
|
||
<div class="mt-4 p-3 bg-green-50 border border-green-200 rounded-md">
|
||
<p class="text-sm text-green-600">{success}</p>
|
||
</div>
|
||
{/if}
|
||
|
||
<button
|
||
type="submit"
|
||
disabled={loading}
|
||
class="w-full mt-6 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{#if loading}
|
||
<span class="inline-flex items-center">
|
||
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||
</svg>
|
||
{mode === 'signin' ? 'Signing in...' : 'Creating account...'}
|
||
</span>
|
||
{:else}
|
||
{mode === 'signin' ? 'Sign In' : 'Create Account'}
|
||
{/if}
|
||
</button>
|
||
</form>
|
||
|
||
<div class="mt-6 text-center">
|
||
<p class="text-sm text-white">
|
||
{mode === 'signin' ? "Don't have an account?" : 'Already have an account?'}
|
||
<button
|
||
type="button"
|
||
onclick={switchMode}
|
||
class="text-blue-300 hover:text-blue-200 font-medium ml-1"
|
||
>
|
||
{mode === 'signin' ? 'Create one' : 'Sign in'}
|
||
</button>
|
||
</p>
|
||
</div>
|
||
</Container>
|
||
</div>
|
||
{/if} |