Files
TypstDrive/src/routes/register/+page.svelte
T
2026-05-14 18:40:27 -04:00

152 lines
8.9 KiB
Svelte

<script lang="ts">
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { userStore } from '$lib/ts/auth';
import Icon from '@iconify/svelte';
import Footer from '$lib/components/Footer.svelte';
let username = $state('');
let email = $state('');
let password = $state('');
let errorMsg = $state('');
let registrationDisabled = $state(false);
onMount(async () => {
if ($userStore) { goto('/dashboard'); return; }
const res = await fetch('/api/setup');
if (res.ok) {
const data = await res.json();
registrationDisabled = !data.registration_enabled;
}
});
async function register(e: Event) {
e.preventDefault();
errorMsg = '';
try {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, email, password })
});
if (!res.ok) {
const text = await res.text();
errorMsg = text || 'Registration failed';
return;
}
// Auto login after successful registration using email
const loginRes = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (loginRes.ok) {
const user = await loginRes.json();
userStore.set(user);
goto('/dashboard');
}
} catch (e: any) {
errorMsg = e.message;
}
}
onMount(() => {
if ($userStore) goto('/dashboard');
});
</script>
<svelte:head>
<title>Register - TypstDrive</title>
<meta name="description" content="Create a new TypstDrive account." />
</svelte:head>
<div class="min-h-screen flex flex-col relative overflow-hidden">
<div class="flex-grow flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Background decorative elements -->
<div class="absolute -top-40 right-20 w-96 h-96 bg-emerald-400/20 dark:bg-emerald-600/10 rounded-full blur-3xl mix-blend-multiply z-0"></div>
<div class="absolute top-40 -left-20 w-96 h-96 bg-teal-400/20 dark:bg-teal-600/10 rounded-full blur-3xl mix-blend-multiply z-0"></div>
<div class="absolute -bottom-40 right-40 w-96 h-96 bg-blue-400/20 dark:bg-blue-600/10 rounded-full blur-3xl mix-blend-multiply z-0"></div>
<div class="max-w-md w-full space-y-8 bg-white/80 dark:bg-black/40 backdrop-blur-xl p-10 rounded-2xl shadow-2xl border border-gray-200/50 dark:border-white/10 relative z-10">
<div class="text-center">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-100/50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-500 mb-6 mx-auto shadow-sm">
<Icon icon="mdi:account-plus" class="text-3xl" />
</div>
<h2 class="text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">
Create an account
</h2>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400 font-medium">
Join TypstDrive to start collaborating
</p>
</div>
{#if registrationDisabled}
<div class="flex flex-col items-center gap-4 py-4">
<div class="flex items-center gap-3 w-full bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-400 p-4 rounded-xl border border-amber-200 dark:border-amber-500/20">
<Icon icon="mdi:lock-outline" class="text-2xl flex-shrink-0" />
<div>
<p class="font-semibold text-sm">Registration Disabled</p>
<p class="text-xs mt-0.5 text-amber-600 dark:text-amber-500">New account creation has been disabled by the administrator. Please contact your administrator to get an account.</p>
</div>
</div>
</div>
{:else}
<form class="mt-8 space-y-6" onsubmit={register}>
<div class="space-y-5">
<div>
<label for="username" class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Username</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Icon icon="mdi:account" class="text-gray-400 dark:text-gray-500" />
</div>
<input id="username" name="username" type="text" required bind:value={username} class="block w-full rounded-xl border border-gray-300 dark:border-white/20 pl-10 pr-3 py-2.5 bg-white/50 dark:bg-black/40 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 sm:text-sm transition-all duration-200" placeholder="Choose a username">
</div>
</div>
<div>
<label for="email" class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Email Address</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Icon icon="mdi:email" class="text-gray-400 dark:text-gray-500" />
</div>
<input id="email" name="email" type="email" required bind:value={email} class="block w-full rounded-xl border border-gray-300 dark:border-white/20 pl-10 pr-3 py-2.5 bg-white/50 dark:bg-black/40 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 sm:text-sm transition-all duration-200" placeholder="[email protected]">
</div>
</div>
<div>
<label for="password" class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1.5">Password</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Icon icon="mdi:lock" class="text-gray-400 dark:text-gray-500" />
</div>
<input id="password" name="password" type="password" required bind:value={password} class="block w-full rounded-xl border border-gray-300 dark:border-white/20 pl-10 pr-3 py-2.5 bg-white/50 dark:bg-black/40 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 sm:text-sm transition-all duration-200" placeholder="••••••••">
</div>
</div>
</div>
{#if errorMsg}
<div class="flex items-center gap-2 text-red-600 bg-red-50 dark:bg-red-500/10 dark:text-red-400 p-3 rounded-lg text-sm border border-red-200 dark:border-red-500/20 shadow-sm animate-in slide-in-from-top-1 fade-in duration-200">
<Icon icon="mdi:alert-circle" class="text-lg flex-shrink-0" />
<span class="font-medium">{errorMsg}</span>
</div>
{/if}
<div class="pt-2">
<button type="submit" class="group w-full flex justify-center items-center gap-2 py-3 px-4 border border-transparent text-sm font-bold rounded-xl text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-zinc-900 focus:ring-blue-500 transition-all duration-200 shadow-md hover:shadow-lg transform hover:-translate-y-0.5">
Register
<Icon icon="mdi:arrow-right" class="text-lg group-hover:translate-x-1 transition-transform" />
</button>
</div>
</form>
{/if}
<div class="text-sm text-center mt-6 pt-4 border-t border-gray-200 dark:border-white/10">
<span class="text-gray-500 dark:text-gray-400">Already have an account? </span>
<a href="/login" class="font-bold text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 transition-colors hover:underline">
Sign in
</a>
</div>
</div>
</div>
<Footer />
</div>