Files
Cleanuparr/code/frontend/src/app/features/auth/login/login.component.html
T
2026-03-12 22:12:20 +02:00

154 lines
4.0 KiB
HTML

<h2 class="login-title">Sign In</h2>
@if (error()) {
<div class="error-message">{{ error() }}</div>
}
@if (retryCountdown() > 0) {
<div class="retry-countdown">Try again in {{ retryCountdown() }}s</div>
}
<!-- Credentials view -->
@if (view() === 'credentials') {
<div class="login-view">
@if (!oidcExclusiveMode()) {
<form class="login-form" (ngSubmit)="submitLogin()">
<app-input
#usernameInput
label="Username"
placeholder="Enter your username"
type="text"
[value]="username()"
(valueChange)="username.set($event)"
/>
<app-input
label="Password"
placeholder="Enter your password"
type="password"
[value]="password()"
(valueChange)="password.set($event)"
/>
<app-button
variant="primary"
class="login-submit"
[disabled]="!username() || !password() || loading() || retryCountdown() > 0"
type="submit"
>
@if (loading()) {
<app-spinner size="sm" />
} @else {
Sign In
}
</app-button>
</form>
@if (plexLinked() || oidcEnabled()) {
<div class="divider">
<span>or</span>
</div>
}
@if (plexLinked()) {
<button
class="plex-login-btn"
[disabled]="plexLoading()"
(click)="startPlexLogin()"
>
@if (plexLoading()) {
<app-spinner size="sm" />
Waiting for Plex...
} @else {
<svg class="plex-login-btn__icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M5.074 1L12 12 5.074 23h5.854L18.926 12 11.928 1z"/>
</svg>
Sign in with Plex
}
</button>
}
}
@if (oidcEnabled()) {
<button
class="oidc-login-btn"
[disabled]="oidcLoading()"
(click)="startOidcLogin()"
>
@if (oidcLoading()) {
<app-spinner size="sm" />
Redirecting...
} @else {
<svg class="oidc-login-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
Sign in with {{ oidcProviderName() }}
}
</button>
}
</div>
}
<!-- 2FA view -->
@if (view() === '2fa') {
<div class="login-view">
<form class="login-form" (ngSubmit)="submit2fa()">
<app-input
#totpInput
label="Authentication Code"
placeholder="Enter 6-digit code"
type="text"
[value]="totpCode()"
(valueChange)="totpCode.set($event)"
/>
<app-button
variant="primary"
class="login-submit"
[disabled]="totpCode().length !== 6 || loading()"
type="submit"
>
@if (loading()) {
<app-spinner size="sm" />
} @else {
Verify
}
</app-button>
</form>
<div class="login-links">
<button class="text-link" (click)="useRecoveryCode()">Use a recovery code</button>
<button class="text-link" (click)="backToCredentials()">Back to sign in</button>
</div>
</div>
}
<!-- Recovery code view -->
@if (view() === 'recovery') {
<div class="login-view">
<form class="login-form" (ngSubmit)="submitRecoveryCode()">
<app-input
#recoveryInput
label="Recovery Code"
placeholder="XXXX-XXXX"
type="text"
[value]="recoveryCode()"
(valueChange)="recoveryCode.set($event)"
/>
<app-button
variant="primary"
class="login-submit"
[disabled]="!recoveryCode() || loading()"
type="submit"
>
@if (loading()) {
<app-spinner size="sm" />
} @else {
Verify Recovery Code
}
</app-button>
</form>
<div class="login-links">
<button class="text-link" (click)="backTo2fa()">Back to authenticator code</button>
</div>
</div>
}