Files
webref/frontend/src/lib/components/auth/LoginForm.svelte
Danilo Reyes c52ac86739
Some checks failed
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 3s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 3s
CI/CD Pipeline / VM Test - performance (push) Successful in 2s
CI/CD Pipeline / VM Test - security (push) Successful in 2s
CI/CD Pipeline / Backend Linting (push) Successful in 2s
CI/CD Pipeline / Frontend Linting (push) Failing after 12s
CI/CD Pipeline / Nix Flake Check (push) Successful in 37s
CI/CD Pipeline / CI Summary (push) Failing after 0s
lib was accidentally being ignored
2025-11-02 12:23:46 -06:00

175 lines
3.4 KiB
Svelte

<script lang="ts">
import { createEventDispatcher } from 'svelte';
export let isLoading = false;
let email = '';
let password = '';
let errors: Record<string, string> = {};
const dispatch = createEventDispatcher<{
submit: { email: string; password: string };
}>();
function validateForm(): boolean {
errors = {};
if (!email) {
errors.email = 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
errors.email = 'Please enter a valid email address';
}
if (!password) {
errors.password = 'Password is required';
}
return Object.keys(errors).length === 0;
}
function handleSubmit(event: Event) {
event.preventDefault();
if (!validateForm()) {
return;
}
dispatch('submit', { email, password });
}
</script>
<form on:submit={handleSubmit} class="login-form">
<div class="form-group">
<label for="email">Email</label>
<input
id="email"
type="email"
bind:value={email}
disabled={isLoading}
placeholder="you@example.com"
class:error={errors.email}
autocomplete="email"
/>
{#if errors.email}
<span class="error-text">{errors.email}</span>
{/if}
</div>
<div class="form-group">
<label for="password">Password</label>
<input
id="password"
type="password"
bind:value={password}
disabled={isLoading}
placeholder="••••••••"
class:error={errors.password}
autocomplete="current-password"
/>
{#if errors.password}
<span class="error-text">{errors.password}</span>
{/if}
</div>
<button type="submit" disabled={isLoading} class="submit-button">
{#if isLoading}
<span class="spinner"></span>
Logging in...
{:else}
Login
{/if}
</button>
</form>
<style>
.login-form {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
label {
font-weight: 500;
color: #374151;
font-size: 0.95rem;
}
input {
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 1rem;
transition: all 0.2s;
}
input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
input.error {
border-color: #ef4444;
}
input:disabled {
background-color: #f9fafb;
cursor: not-allowed;
}
.error-text {
color: #ef4444;
font-size: 0.875rem;
}
.submit-button {
padding: 0.875rem 1.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition:
transform 0.2s,
box-shadow 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.submit-button:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.submit-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.spinner {
width: 16px;
height: 16px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>