Add frontend support for user
This commit is contained in:
91
frontend/src/routes/login/+page.svelte
Normal file
91
frontend/src/routes/login/+page.svelte
Normal file
@@ -0,0 +1,91 @@
|
||||
<script lang="ts">
|
||||
import { writable } from 'svelte/store';
|
||||
import { goto } from '$app/navigation';
|
||||
import { API_URL } from '$lib/api';
|
||||
|
||||
// Login form state
|
||||
let loginUsername = $state('');
|
||||
let loginPassword = $state('');
|
||||
let loginError: string | null = $state(null);
|
||||
let loginLoading = $state(false);
|
||||
|
||||
async function handleLogin(e: Event) {
|
||||
e.preventDefault();
|
||||
loginError = null;
|
||||
loginLoading = true;
|
||||
try {
|
||||
const form = new FormData();
|
||||
form.append('username', loginUsername);
|
||||
form.append('password', loginPassword);
|
||||
|
||||
const response = await fetch(`${API_URL}/login`, {
|
||||
method: 'POST',
|
||||
body: form
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
loginError = data.detail || 'Login failed';
|
||||
loginLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
localStorage.setItem('token', data.access_token);
|
||||
goto('/').then(() => location.reload());
|
||||
} catch (err) {
|
||||
loginError = 'Network error - ' + err;
|
||||
} finally {
|
||||
loginLoading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-50">
|
||||
<form
|
||||
class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 w-full max-w-sm"
|
||||
onsubmit={handleLogin}
|
||||
>
|
||||
<h2 class="text-2xl font-bold mb-6 text-center">Login</h2>
|
||||
{#if loginError}
|
||||
<div class="mb-4 text-red-600 text-sm">{loginError}</div>
|
||||
{/if}
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="username"> Username </label>
|
||||
<input
|
||||
id="username"
|
||||
type="text"
|
||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
bind:value={loginUsername}
|
||||
required
|
||||
autocomplete="username"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="password"> Password </label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
|
||||
bind:value={loginPassword}
|
||||
required
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
|
||||
disabled={loginLoading}
|
||||
>
|
||||
{loginLoading ? 'Logging in...' : 'Login'}
|
||||
</button>
|
||||
<a
|
||||
href="/register"
|
||||
class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800 ml-4"
|
||||
>
|
||||
Create an account
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
Reference in New Issue
Block a user