122 lines
4.9 KiB
HTML
122 lines
4.9 KiB
HTML
<div class="min-h-screen bg-gradient-to-tr from-indigo-600 via-purple-700 to-pink-600 flex items-center justify-center p-6 font-sans">
|
|
<div class="bg-white rounded-xl shadow-lg max-w-md w-full p-8 animate-fadeIn">
|
|
<!-- Header -->
|
|
<div class="mb-8 text-center">
|
|
<h1 class="text-3xl font-extrabold text-gray-900 mb-2">Welcome Back</h1>
|
|
<p class="text-gray-600">Please sign in to your account</p>
|
|
</div>
|
|
|
|
<!-- Success Message -->
|
|
@if (success) {
|
|
<div class="mb-6 p-4 bg-green-100 border border-green-400 text-green-800 rounded flex items-center gap-2" role="alert">
|
|
<svg class="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path d="M9 12l2 2 4-4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
|
</svg>
|
|
<span>{{ success }}</span>
|
|
</div>
|
|
}
|
|
|
|
<!-- Error Message -->
|
|
@if (error) {
|
|
<div class="mb-6 p-4 bg-red-100 border border-red-400 text-red-800 rounded flex items-center gap-2" role="alert">
|
|
<svg class="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
|
|
</svg>
|
|
<span>{{ error }}</span>
|
|
</div>
|
|
}
|
|
|
|
<!-- Login Form -->
|
|
<form (ngSubmit)="onSubmit()" [formGroup]="loginForm" class="space-y-6" novalidate>
|
|
|
|
<!-- Email Field -->
|
|
<div>
|
|
<label class="block text-sm font-semibold text-gray-700 mb-1" for="email">Email Address</label>
|
|
<input
|
|
(input)="clearMessages()"
|
|
[ngClass]="{'border-red-500': hasFieldError('email')}"
|
|
autocomplete="email"
|
|
class="w-full rounded-md border border-gray-300 px-4 py-3 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
|
|
formControlName="email"
|
|
id="email"
|
|
placeholder="Enter your email"
|
|
type="email"
|
|
/>
|
|
@if (hasFieldError('email')) {
|
|
<p class="mt-1 text-xs text-red-600">{{ getFieldError('email') }}</p>
|
|
}
|
|
</div>
|
|
|
|
<!-- Password Field -->
|
|
<div>
|
|
<label class="block text-sm font-semibold text-gray-700 mb-1" for="password">Password</label>
|
|
<input
|
|
(input)="clearMessages()"
|
|
[ngClass]="{'border-red-500': hasFieldError('password')}"
|
|
[type]="showPassword ? 'text' : 'password'"
|
|
autocomplete="current-password"
|
|
class="w-full rounded-md border border-gray-300 px-4 py-3 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"
|
|
formControlName="password"
|
|
id="password"
|
|
placeholder="Enter your password"
|
|
/>
|
|
<button
|
|
(click)="togglePasswordVisibility()"
|
|
[attr.aria-label]="showPassword ? 'Hide password' : 'Show password'"
|
|
class="absolute right-4 top-10"
|
|
type="button"
|
|
>
|
|
<i [class]="showPassword ? 'fas fa-eye-slash' : 'fas fa-eye'"></i>
|
|
</button>
|
|
@if (hasFieldError('password')) {
|
|
<p class="mt-1 text-xs text-red-600">{{ getFieldError('password') }}</p>
|
|
}
|
|
</div>
|
|
|
|
<!-- Remember Me & Forgot Password -->
|
|
<div class="flex justify-between items-center">
|
|
<label class="flex items-center">
|
|
<input
|
|
class="checkbox-input"
|
|
formControlName="rememberMe"
|
|
type="checkbox"
|
|
/>
|
|
<span class="ml-2 text-sm text-gray-600">Remember me</span>
|
|
</label>
|
|
<button
|
|
(click)="goToForgotPassword()"
|
|
class="text-sm text-indigo-600 hover:text-indigo-700 focus:outline-none"
|
|
type="button"
|
|
>
|
|
Forgot password?
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<button
|
|
[disabled]="loading"
|
|
class="w-full flex justify-center items-center gap-2 rounded-lg bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-300 disabled:cursor-not-allowed text-white font-semibold py-3 transition"
|
|
type="submit"
|
|
>
|
|
@if (loading) {
|
|
<svg class="animate-spin h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" d="M4 12a8 8 0 018-8v8H4z" fill="currentColor"></path>
|
|
</svg>
|
|
<span>Signing in...</span>
|
|
} @else {
|
|
<span>Sign In</span>
|
|
}
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Footer -->
|
|
<div class="mt-6 text-center text-sm text-gray-600">
|
|
Don't have an account?
|
|
<button (click)="goToRegister()" class="font-semibold text-indigo-600 hover:text-indigo-700 focus:outline-none" type="button">
|
|
Sign up here
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|