164 lines
5.4 KiB
TypeScript
164 lines
5.4 KiB
TypeScript
import { Component, inject, signal } from '@angular/core';
|
|
import { Router } from '@angular/router';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { AuthService } from '../../core/services/auth.service';
|
|
import { ThemeService } from '../../core/services/theme.service';
|
|
import {
|
|
CardComponent,
|
|
CardHeaderComponent,
|
|
CardTitleComponent,
|
|
CardDescriptionComponent,
|
|
CardContentComponent,
|
|
CardFooterComponent,
|
|
} from '../../ui/card/card.component';
|
|
import { ButtonComponent } from '../../ui/button/button.component';
|
|
import { InputComponent } from '../../ui/input/input.component';
|
|
import { LabelComponent } from '../../ui/label/label.component';
|
|
import { AlertComponent, AlertDescriptionComponent } from '../../ui/alert/alert.component';
|
|
|
|
@Component({
|
|
selector: 'app-login',
|
|
standalone: true,
|
|
imports: [
|
|
FormsModule,
|
|
CardComponent,
|
|
CardHeaderComponent,
|
|
CardTitleComponent,
|
|
CardDescriptionComponent,
|
|
CardContentComponent,
|
|
CardFooterComponent,
|
|
ButtonComponent,
|
|
InputComponent,
|
|
LabelComponent,
|
|
AlertComponent,
|
|
AlertDescriptionComponent,
|
|
],
|
|
template: `
|
|
<div class="min-h-screen flex items-center justify-center bg-background p-4">
|
|
<div class="absolute top-4 right-4">
|
|
<button
|
|
uiButton
|
|
variant="ghost"
|
|
size="icon"
|
|
(click)="theme.toggle()"
|
|
>
|
|
@if (theme.resolvedTheme() === 'dark') {
|
|
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
</svg>
|
|
} @else {
|
|
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
|
</svg>
|
|
}
|
|
</button>
|
|
</div>
|
|
|
|
<ui-card class="w-full max-w-md">
|
|
<ui-card-header class="text-center">
|
|
<div class="flex justify-center mb-4">
|
|
<svg class="h-12 w-12 text-primary" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
|
</svg>
|
|
</div>
|
|
<ui-card-title>Welcome to Onebox</ui-card-title>
|
|
<ui-card-description>Enter your credentials to sign in</ui-card-description>
|
|
</ui-card-header>
|
|
|
|
<form (ngSubmit)="onSubmit()">
|
|
<ui-card-content class="space-y-4">
|
|
@if (error()) {
|
|
<ui-alert variant="destructive">
|
|
<ui-alert-description>{{ error() }}</ui-alert-description>
|
|
</ui-alert>
|
|
}
|
|
|
|
<div class="space-y-2">
|
|
<label uiLabel for="username">Username</label>
|
|
<input
|
|
uiInput
|
|
id="username"
|
|
type="text"
|
|
[(ngModel)]="username"
|
|
name="username"
|
|
placeholder="Enter username"
|
|
autocomplete="username"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<label uiLabel for="password">Password</label>
|
|
<input
|
|
uiInput
|
|
id="password"
|
|
type="password"
|
|
[(ngModel)]="password"
|
|
name="password"
|
|
placeholder="Enter password"
|
|
autocomplete="current-password"
|
|
required
|
|
/>
|
|
</div>
|
|
</ui-card-content>
|
|
|
|
<ui-card-footer>
|
|
<button
|
|
uiButton
|
|
type="submit"
|
|
class="w-full"
|
|
[disabled]="loading()"
|
|
>
|
|
@if (loading()) {
|
|
<svg class="animate-spin h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
Signing in...
|
|
} @else {
|
|
Sign in
|
|
}
|
|
</button>
|
|
</ui-card-footer>
|
|
</form>
|
|
|
|
<div class="px-6 pb-6">
|
|
<p class="text-xs text-center text-muted-foreground">
|
|
Default credentials: admin / admin
|
|
</p>
|
|
</div>
|
|
</ui-card>
|
|
</div>
|
|
`,
|
|
})
|
|
export class LoginComponent {
|
|
private auth = inject(AuthService);
|
|
private router = inject(Router);
|
|
theme = inject(ThemeService);
|
|
|
|
username = '';
|
|
password = '';
|
|
loading = signal(false);
|
|
error = signal<string | null>(null);
|
|
|
|
async onSubmit(): Promise<void> {
|
|
if (!this.username || !this.password) {
|
|
this.error.set('Please enter username and password');
|
|
return;
|
|
}
|
|
|
|
this.loading.set(true);
|
|
this.error.set(null);
|
|
|
|
const result = await this.auth.login(this.username, this.password);
|
|
|
|
this.loading.set(false);
|
|
|
|
if (result.success) {
|
|
this.router.navigate(['/dashboard']);
|
|
} else {
|
|
this.error.set(result.error || 'Invalid credentials');
|
|
}
|
|
}
|
|
}
|