This commit is contained in:
Nikolaj
2026-03-16 14:01:22 +01:00
parent 5d54d1cf7b
commit 65b719334f
19 changed files with 848 additions and 96 deletions

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import '../app.css';
import favicon from '$lib/assets/favicon.svg';
let { children } = $props();

View File

@@ -1,21 +1,29 @@
<script>
import Card from "$lib/Card.svelte";
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import Card from '$lib/Card.svelte';
let cards = $state([]);
let loading = $state(false);
onMount(() => {
if (!localStorage.getItem('token')) goto('/auth');
});
async function openPack() {
loading = true;
try {
const res = await fetch("http://localhost:8000/pack/14");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
cards = await res.json();
} catch (e) {
console.error("Failed to open pack:", e);
} finally {
loading = false;
loading = true;
try {
const res = await fetch('http://localhost:8000/pack/10', {
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
});
if (res.status === 401) { goto('/auth'); return; }
cards = await res.json();
} catch (e) {
console.error(e);
} finally {
loading = false;
}
}
}
</script>
<main>
@@ -33,7 +41,7 @@
<style>
main {
min-height: 100vh;
background: #0d0a04;
background: #887859;
padding: 2rem;
}

View File

@@ -0,0 +1,201 @@
<script>
import { goto } from '$app/navigation';
let mode = $state('login');
let username = $state('');
let email = $state('');
let password = $state('');
let error = $state('');
let loading = $state(false);
async function submit() {
error = '';
if (!username.trim()) {
error = 'Username is required';
return;
}
loading = true;
try {
if (mode === 'register') {
const res = await fetch('http://localhost:8000/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, email, password }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail);
mode = 'login';
error = '';
return;
}
const form = new FormData();
form.append('username', username);
form.append('password', password);
const res = await fetch('http://localhost:8000/login', {
method: 'POST',
body: form,
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail);
localStorage.setItem('token', data.access_token);
goto('/');
} catch (e) {
error = e.message;
} finally {
loading = false;
}
}
</script>
<main>
<div class="card">
<h1>{mode === 'login' ? 'Sign In' : 'Register'}</h1>
<div class="fields">
<input
type="text"
placeholder="Username"
bind:value={username}
/>
{#if mode === 'register'}
<input
type="email"
placeholder="Email"
bind:value={email}
/>
{/if}
<input
type="password"
placeholder="Password"
bind:value={password}
/>
</div>
{#if error}
<p class="error">{error}</p>
{/if}
<button onclick={submit} disabled={loading}>
{loading ? 'Please wait...' : mode === 'login' ? 'Sign In' : 'Create Account'}
</button>
<p class="toggle">
{mode === 'login' ? "Don't have an account?" : 'Already have an account?'}
<button class="link" onclick={() => { mode = mode === 'login' ? 'register' : 'login'; error = ''; }}>
{mode === 'login' ? 'Register' : 'Sign in'}
</button>
</p>
</div>
</main>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Crimson+Text:ital,wght@0,400;0,600;1,400&display=swap');
main {
min-height: 100vh;
background: #0d0a04;
display: flex;
align-items: center;
justify-content: center;
}
.card {
width: 340px;
background: #2e1c05;
border: 2px solid #6b4c1e;
border-radius: 12px;
padding: 2rem;
display: flex;
flex-direction: column;
gap: 1.2rem;
}
h1 {
font-family: 'Cinzel', serif;
font-size: 20px;
color: #f0d080;
text-align: center;
margin: 0;
}
.fields {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
input {
width: 100%;
padding: 9px 12px;
background: #1a1008;
border: 1.5px solid #8b6420;
border-radius: 6px;
color: #f0d080;
font-family: 'Crimson Text', serif;
font-size: 15px;
box-sizing: border-box;
outline: none;
}
input::placeholder {
color: rgba(240, 180, 80, 0.4);
}
input:focus {
border-color: #f0d080;
}
button {
width: 100%;
padding: 10px;
background: #6b4c1e;
color: #f0d080;
border: 1.5px solid #8b6420;
border-radius: 6px;
font-family: 'Cinzel', serif;
font-size: 13px;
cursor: pointer;
transition: background 0.15s;
}
button:hover:not(:disabled) {
background: #8b6420;
}
button:disabled {
opacity: 0.5;
cursor: default;
}
.error {
color: #c84040;
font-family: 'Crimson Text', serif;
font-size: 13px;
text-align: center;
margin: 0;
}
.toggle {
font-family: 'Crimson Text', serif;
font-size: 13px;
color: rgba(240, 180, 80, 0.7);
text-align: center;
margin: 0;
}
.link {
all: unset;
color: #f0d080;
cursor: pointer;
text-decoration: underline;
width: auto;
padding: 0;
font-size: 13px;
font-family: 'Crimson Text', serif;
}
.link:hover {
color: #f0d080;
}
</style>