82 lines
5.1 KiB
HTML
82 lines
5.1 KiB
HTML
<div class="page page-center login-page-shell">
|
|
<div class="container py-4">
|
|
<div class="row justify-content-center align-items-stretch g-4 login-layout">
|
|
<div class="col-12 col-md-10 col-lg-7 col-xl-5">
|
|
<div class="card card-md login-card login-card-enhanced">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start gap-3 mb-4 flex-wrap">
|
|
<div>
|
|
<h1 class="h2 mb-1">{{ appSettings.appName() }}</h1>
|
|
<div class="text-secondary">{{ mode() === 'login' ? loginSubtitle() : registerSubtitle() }}</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-icon btn-ghost-secondary"
|
|
type="button"
|
|
[attr.aria-label]="ui.t('theme.label')"
|
|
[attr.title]="ui.t('theme.label')"
|
|
(click)="ui.toggleTheme()">
|
|
@if (ui.theme() === 'dark') {
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 3l0 1"/><path d="M12 20l0 1"/><path d="M3 12l1 0"/><path d="M20 12l1 0"/><path d="M5.6 5.6l.7 .7"/><path d="M18.4 18.4l.7 .7"/><path d="M18.4 5.6l-.7 .7"/><path d="M5.6 18.4l-.7 .7"/><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/></svg>
|
|
} @else {
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 3c.132 0 .263 0 .393 .007a9 9 0 1 0 0 17.986a9 9 0 0 1 -.393 -17.993z"/></svg>
|
|
}
|
|
</button>
|
|
|
|
<button class="btn btn-icon btn-ghost-secondary"
|
|
type="button"
|
|
[attr.aria-label]="currentLanguageLabel()"
|
|
[attr.title]="currentLanguageLabel()"
|
|
(click)="toggleLanguage()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 5h7"/><path d="M7 4c0 4.846 0 7 .5 8"/><path d="M10 8l-3 4l-3 -4"/><path d="M19 22l0 -3"/><path d="M17 19h4"/><path d="M20 19l-3 -7l-3 7"/><path d="M11 19l4 0"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<form [formGroup]="form" (ngSubmit)="submit()" class="login-input-stack">
|
|
@if (mode() === 'register') {
|
|
<div>
|
|
<label class="form-label">{{ ui.t('login.fullName') }}</label>
|
|
<input class="form-control form-control-lg" formControlName="fullName" autocomplete="name" />
|
|
</div>
|
|
}
|
|
|
|
<div>
|
|
<label class="form-label">{{ ui.t('login.email') }}</label>
|
|
<input class="form-control form-control-lg" formControlName="email" autocomplete="username" />
|
|
</div>
|
|
|
|
<div>
|
|
<label class="form-label">{{ ui.t('login.password') }}</label>
|
|
<input class="form-control form-control-lg" type="password" formControlName="password" autocomplete="current-password" />
|
|
</div>
|
|
|
|
@if (errorMessage()) {
|
|
<div class="alert alert-danger py-2 mb-0">{{ errorMessage() }}</div>
|
|
}
|
|
|
|
<button class="btn btn-primary btn-lg w-100 login-submit-button" [disabled]="form.invalid || loading()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 15l6 6"/><path d="M4 11a7 7 0 1 1 14 0a7 7 0 0 1 -14 0"/></svg>
|
|
{{ loading() ? (mode() === 'login' ? ui.t('action.loggingIn') : ui.t('action.creatingAccount')) : (mode() === 'login' ? ui.t('action.login') : ui.t('action.createAccount')) }}
|
|
</button>
|
|
</form>
|
|
|
|
<div class="login-footer-note">
|
|
{{ mode() === 'login' ? ui.t('login.footer') : ui.t('register.footer') }}
|
|
</div>
|
|
|
|
@if (appSettings.registrationEnabled()) {
|
|
<div class="login-footer-note d-flex justify-content-between align-items-center gap-2 flex-wrap">
|
|
<span>{{ mode() === 'login' ? switchToRegisterLabel() : switchToLoginLabel() }}</span>
|
|
<button class="btn btn-ghost-primary btn-sm" type="button" (click)="switchMode()">
|
|
{{ mode() === 'login' ? ui.t('action.registerMode') : ui.t('action.loginMode') }}
|
|
</button>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|