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