Spaces:
Paused
Paused
| import { Component, inject } from '@angular/core'; | |
| import { CommonModule } from '@angular/common'; | |
| import { FormsModule } from '@angular/forms'; | |
| import { Router } from '@angular/router'; | |
| import { MatCardModule } from '@angular/material/card'; | |
| import { MatFormFieldModule } from '@angular/material/form-field'; | |
| import { MatInputModule } from '@angular/material/input'; | |
| import { MatButtonModule } from '@angular/material/button'; | |
| import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; | |
| import { MatIconModule } from '@angular/material/icon'; | |
| import { AuthService } from '../../services/auth.service'; | |
| ({ | |
| selector: 'app-login', | |
| standalone: true, | |
| imports: [ | |
| CommonModule, | |
| FormsModule, | |
| MatCardModule, | |
| MatFormFieldModule, | |
| MatInputModule, | |
| MatButtonModule, | |
| MatProgressSpinnerModule, | |
| MatIconModule | |
| ], | |
| template: ` | |
| <div class="login-container"> | |
| <mat-card class="login-card"> | |
| <mat-card-header> | |
| <mat-card-title>Flare Administration</mat-card-title> | |
| </mat-card-header> | |
| <mat-card-content> | |
| <form (ngSubmit)="login()" #loginForm="ngForm"> | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Username</mat-label> | |
| <input | |
| matInput | |
| type="text" | |
| name="username" | |
| [(ngModel)]="username" | |
| required | |
| [disabled]="loading" | |
| autocomplete="username" | |
| > | |
| <mat-icon matPrefix>person</mat-icon> | |
| <mat-error>Username is required</mat-error> | |
| </mat-form-field> | |
| <mat-form-field appearance="outline" class="full-width"> | |
| <mat-label>Password</mat-label> | |
| <input | |
| matInput | |
| [type]="hidePassword ? 'password' : 'text'" | |
| name="password" | |
| [(ngModel)]="password" | |
| required | |
| [disabled]="loading" | |
| autocomplete="current-password" | |
| > | |
| <mat-icon matPrefix>lock</mat-icon> | |
| <button mat-icon-button matSuffix (click)="hidePassword = !hidePassword" type="button"> | |
| <mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon> | |
| </button> | |
| <mat-error>Password is required</mat-error> | |
| </mat-form-field> | |
| @if (error) { | |
| <div class="error-message"> | |
| <mat-icon>error</mat-icon> | |
| {{ error }} | |
| </div> | |
| } | |
| <button | |
| mat-raised-button | |
| color="primary" | |
| type="submit" | |
| class="full-width submit-button" | |
| [disabled]="loading || !loginForm.valid" | |
| > | |
| @if (loading) { | |
| <mat-spinner diameter="20" class="button-spinner"></mat-spinner> | |
| Logging in... | |
| } @else { | |
| <mat-icon>login</mat-icon> | |
| Login | |
| } | |
| </button> | |
| </form> | |
| </mat-card-content> | |
| </mat-card> | |
| </div> | |
| `, | |
| styles: [` | |
| .login-container { | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background-color: #f5f5f5; | |
| } | |
| .login-card { | |
| width: 100%; | |
| max-width: 400px; | |
| padding: 20px; | |
| mat-card-header { | |
| display: flex; | |
| justify-content: center; | |
| margin-bottom: 30px; | |
| mat-card-title { | |
| font-size: 24px; | |
| font-weight: 500; | |
| color: #333; | |
| } | |
| } | |
| } | |
| .full-width { | |
| width: 100%; | |
| } | |
| mat-form-field { | |
| margin-bottom: 20px; | |
| } | |
| .error-message { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| color: #f44336; | |
| font-size: 14px; | |
| margin-bottom: 20px; | |
| padding: 12px; | |
| background-color: #ffebee; | |
| border-radius: 4px; | |
| mat-icon { | |
| font-size: 20px; | |
| width: 20px; | |
| height: 20px; | |
| } | |
| } | |
| .submit-button { | |
| height: 48px; | |
| font-size: 16px; | |
| margin-top: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| mat-icon { | |
| margin-right: 4px; | |
| } | |
| } | |
| .button-spinner { | |
| display: inline-block; | |
| margin-right: 8px; | |
| } | |
| ::ng-deep { | |
| .mat-mdc-card { | |
| box-shadow: 0 10px 40px rgba(0, 0, 0, 0.16) !important; | |
| } | |
| .mat-mdc-form-field-icon-prefix, | |
| .mat-mdc-form-field-icon-suffix { | |
| padding: 0 4px; | |
| } | |
| .mat-mdc-progress-spinner { | |
| --mdc-circular-progress-active-indicator-color: white; | |
| } | |
| .mat-mdc-form-field-error { | |
| font-size: 12px; | |
| } | |
| } | |
| `] | |
| }) | |
| export class LoginComponent { | |
| private authService = inject(AuthService); | |
| private router = inject(Router); | |
| username = ''; | |
| password = ''; | |
| loading = false; | |
| error = ''; | |
| hidePassword = true; | |
| async login() { | |
| this.loading = true; | |
| this.error = ''; | |
| try { | |
| await this.authService.login(this.username, this.password).toPromise(); | |
| this.router.navigate(['/']); | |
| } catch (err: any) { | |
| this.error = err.error?.detail || 'Invalid credentials'; | |
| } finally { | |
| this.loading = false; | |
| } | |
| } | |
| } |