mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-09-13 05:47:33 -04:00
239 lines
6.9 KiB
HTML
239 lines
6.9 KiB
HTML
<h2 class="setup-title">Initial Setup</h2>
|
|
|
|
<div class="steps-indicator">
|
|
<div class="step-group" [class.active]="currentStep() === 1" [class.completed]="currentStep() > 1">
|
|
<div class="step-marker">
|
|
@if (currentStep() > 1) {
|
|
<ng-icon name="tablerCheck" size="14" />
|
|
} @else {
|
|
1
|
|
}
|
|
</div>
|
|
<span class="step-label">Account</span>
|
|
</div>
|
|
<div class="step-connector" [class.active]="currentStep() > 1"></div>
|
|
<div class="step-group" [class.active]="currentStep() === 2" [class.completed]="currentStep() > 2">
|
|
<div class="step-marker">
|
|
@if (currentStep() > 2) {
|
|
<ng-icon name="tablerCheck" size="14" />
|
|
} @else {
|
|
2
|
|
}
|
|
</div>
|
|
<span class="step-label">2FA</span>
|
|
</div>
|
|
<div class="step-connector" [class.active]="currentStep() > 2"></div>
|
|
<div class="step-group" [class.active]="currentStep() === 3" [class.completed]="currentStep() > 3">
|
|
<div class="step-marker">3</div>
|
|
<span class="step-label">Plex</span>
|
|
</div>
|
|
</div>
|
|
|
|
@if (error()) {
|
|
<div class="error-message">{{ error() }}</div>
|
|
}
|
|
|
|
<!-- Step 1: Create Account -->
|
|
@if (currentStep() === 1) {
|
|
<div class="step-content">
|
|
<h2 class="step-title">Create Admin Account</h2>
|
|
<p class="step-subtitle">Choose a username and strong password</p>
|
|
|
|
<form class="setup-form" (ngSubmit)="createAccount()">
|
|
<app-input
|
|
#usernameInput
|
|
label="Username"
|
|
placeholder="Enter a username"
|
|
type="text"
|
|
[value]="username()"
|
|
(valueChange)="username.set($event)"
|
|
/>
|
|
<app-input
|
|
label="Password"
|
|
placeholder="Minimum 8 characters"
|
|
type="password"
|
|
[value]="password()"
|
|
(valueChange)="password.set($event)"
|
|
/>
|
|
|
|
@if (password()) {
|
|
<div class="password-strength">
|
|
<div class="password-strength__bar">
|
|
<div class="password-strength__fill password-strength__fill--{{ passwordStrength() }}"></div>
|
|
</div>
|
|
<span class="password-strength__label password-strength__label--{{ passwordStrength() }}">{{ passwordStrength() }}</span>
|
|
</div>
|
|
}
|
|
|
|
<app-input
|
|
label="Confirm Password"
|
|
placeholder="Re-enter your password"
|
|
type="password"
|
|
[value]="confirmPassword()"
|
|
(valueChange)="confirmPassword.set($event)"
|
|
/>
|
|
|
|
@if (password() && !passwordValid) {
|
|
<p class="field-error">Password must be at least 8 characters</p>
|
|
}
|
|
@if (confirmPassword() && !passwordsMatch) {
|
|
<p class="field-error">Passwords do not match</p>
|
|
}
|
|
|
|
<app-button
|
|
variant="primary"
|
|
class="submit-btn"
|
|
[disabled]="!username() || !passwordValid || !passwordsMatch || loading()"
|
|
type="submit"
|
|
>
|
|
@if (loading()) {
|
|
<app-spinner size="sm" />
|
|
} @else {
|
|
Continue
|
|
}
|
|
</app-button>
|
|
</form>
|
|
</div>
|
|
}
|
|
|
|
<!-- Step 2: Two-Factor Authentication -->
|
|
@if (currentStep() === 2) {
|
|
<div class="step-content">
|
|
<h2 class="step-title">
|
|
<ng-icon name="tablerShieldLock" size="20" />
|
|
Two-Factor Authentication
|
|
</h2>
|
|
|
|
@if (!totpVerified()) {
|
|
<p class="step-subtitle">Scan the QR code with your authenticator app</p>
|
|
|
|
@if (loading()) {
|
|
<div class="loading-center">
|
|
<app-spinner />
|
|
</div>
|
|
} @else {
|
|
<div class="qr-section">
|
|
<div class="qr-code-wrapper">
|
|
<qrcode [qrdata]="qrCodeUri()" [width]="200" errorCorrectionLevel="M" [margin]="2" />
|
|
</div>
|
|
|
|
<details class="qr-manual-entry">
|
|
<summary>Can't scan? Enter manually</summary>
|
|
<div class="qr-manual-content">
|
|
<p class="qr-manual-label">Secret key:</p>
|
|
<code class="qr-secret">{{ totpSecret() }}</code>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<form class="setup-form" (ngSubmit)="verifyTotp()">
|
|
<app-input
|
|
#verificationInput
|
|
label="Verification Code"
|
|
placeholder="Enter 6-digit code"
|
|
type="text"
|
|
[value]="verificationCode()"
|
|
(valueChange)="verificationCode.set($event)"
|
|
/>
|
|
<app-button
|
|
variant="primary"
|
|
class="submit-btn"
|
|
[disabled]="verificationCode().length !== 6"
|
|
type="submit"
|
|
>
|
|
Verify Code
|
|
</app-button>
|
|
</form>
|
|
|
|
<button class="skip-link" (click)="skip2fa()">Skip for now</button>
|
|
}
|
|
} @else {
|
|
<p class="step-subtitle success-text">
|
|
<ng-icon name="tablerCheck" size="16" />
|
|
2FA verified successfully!
|
|
</p>
|
|
|
|
<div class="recovery-section">
|
|
<h3 class="recovery-title">Save Your Recovery Codes</h3>
|
|
<p class="recovery-desc">Store these codes in a safe place. Each code can only be used once.</p>
|
|
|
|
<div class="recovery-codes">
|
|
@for (code of recoveryCodes(); track code) {
|
|
<code class="recovery-code">{{ code }}</code>
|
|
}
|
|
</div>
|
|
|
|
<div class="recovery-actions">
|
|
<app-button variant="secondary" (click)="copyRecoveryCodes()">
|
|
<ng-icon name="tablerCopy" size="16" />
|
|
Copy Codes
|
|
</app-button>
|
|
<app-button variant="secondary" (click)="downloadRecoveryCodes()">
|
|
Download
|
|
</app-button>
|
|
</div>
|
|
|
|
<label class="checkbox-label">
|
|
<input type="checkbox" [checked]="codesSaved()" (change)="codesSaved.set(!codesSaved())" />
|
|
I have saved my recovery codes
|
|
</label>
|
|
|
|
<app-button
|
|
variant="primary"
|
|
class="submit-btn"
|
|
[disabled]="!codesSaved()"
|
|
(click)="goToStep3()"
|
|
>
|
|
Continue
|
|
</app-button>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<!-- Step 3: Plex (Optional) -->
|
|
@if (currentStep() === 3) {
|
|
<div class="step-content">
|
|
<h2 class="step-title">Link Plex Account</h2>
|
|
<p class="step-subtitle">Optional: Link your Plex account for quick sign-in</p>
|
|
|
|
@if (!plexLinked()) {
|
|
<button
|
|
class="plex-btn"
|
|
[disabled]="plexLinking()"
|
|
(click)="startPlexLink()"
|
|
>
|
|
@if (plexLinking()) {
|
|
<app-spinner size="sm" />
|
|
Waiting for Plex authorization...
|
|
} @else {
|
|
<svg class="plex-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>
|
|
Link Plex Account
|
|
}
|
|
</button>
|
|
} @else {
|
|
<div class="plex-success">
|
|
<ng-icon name="tablerCheck" size="16" />
|
|
Plex account linked
|
|
</div>
|
|
}
|
|
|
|
<div class="step-actions">
|
|
<app-button
|
|
variant="primary"
|
|
class="submit-btn"
|
|
[disabled]="loading()"
|
|
(click)="completeSetup()"
|
|
>
|
|
@if (loading()) {
|
|
<app-spinner size="sm" />
|
|
} @else {
|
|
Complete Setup
|
|
}
|
|
</app-button>
|
|
</div>
|
|
</div>
|
|
}
|