mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
added login modal
This commit is contained in:
217
src/lib/components/AuthModal.svelte
Normal file
217
src/lib/components/AuthModal.svelte
Normal file
@@ -0,0 +1,217 @@
|
||||
<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 on:keydown={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={mode === 'signin' ? '/auth/signin' : '/auth/signup'}
|
||||
use:enhance={({ formData }) => {
|
||||
if (mode === 'signup' && 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}
|
||||
Reference in New Issue
Block a user