diff --git a/apps/taiga-lumbermill/src/components/app/app.routes.ts b/apps/taiga-lumbermill/src/components/app/app.routes.ts index 52815df02..7a74da1ba 100644 --- a/apps/taiga-lumbermill/src/components/app/app.routes.ts +++ b/apps/taiga-lumbermill/src/components/app/app.routes.ts @@ -48,6 +48,13 @@ export const appRoutes: Route[] = [ (mod) => mod.CommonPageComponent, ), }, + { + path: 'login', + loadComponent: async () => + import('../../dashboards/login/login.component').then( + (mod) => mod.LoginComponent, + ), + }, { path: 'sign-up', loadComponent: async () => diff --git a/apps/taiga-lumbermill/src/dashboards/common-page/common-page.component.html b/apps/taiga-lumbermill/src/dashboards/common-page/common-page.component.html index 1b1f6e554..2961d5f70 100644 --- a/apps/taiga-lumbermill/src/dashboards/common-page/common-page.component.html +++ b/apps/taiga-lumbermill/src/dashboards/common-page/common-page.component.html @@ -49,6 +49,23 @@ +
+
+
+

+ Log in +

+
+
+
+
+

+ Log in +

+
+
+ + email + + + + + + + password + + + +
+ + Forgot password? +
+ + + + +
+
+

+ or +

+
+
+

Login with

+
+ + + +
+ + Don't have an account? + Sign up + + +
diff --git a/apps/taiga-lumbermill/src/dashboards/login/login.component.less b/apps/taiga-lumbermill/src/dashboards/login/login.component.less new file mode 100644 index 000000000..f51c25931 --- /dev/null +++ b/apps/taiga-lumbermill/src/dashboards/login/login.component.less @@ -0,0 +1,82 @@ +:host { + height: calc(100vh - 3rem); +} + +.card { + top: 50%; + bottom: 50%; + width: max-content; + margin: auto; + transform: translateY(-50%); + height: max-content; +} + +.block-input { + margin-bottom: 0.3rem; + width: 20rem; + + @media (max-width: 29rem) { + width: 16rem; + } +} + +.center { + margin-left: auto; + margin-right: auto; + text-align: center; +} + +form { + display: flex; + flex-direction: column; + align-items: center; +} + +.under-password { + display: flex; + justify-content: space-between; + width: 100%; + margin-bottom: 1.5rem; +} + +.or { + display: flex; + margin-top: 0.5rem; + width: 100%; + justify-content: center; + align-items: center; + color: var(--tui-text-secondary); + gap: 0.5rem; +} + +.line { + flex-grow: 1; +} + +.icon-login { + transition: opacity 0.3s; + width: 1.8rem; + height: 1.8rem; + + &:hover { + opacity: 0.8; + } + + &:active { + opacity: 0.95; + } +} + +.icons { + display: flex; + gap: 1rem; +} + +.icons-title { + font-weight: 700; +} + +.error { + margin-right: auto; + margin-bottom: 0.8rem; +} diff --git a/apps/taiga-lumbermill/src/dashboards/login/login.component.ts b/apps/taiga-lumbermill/src/dashboards/login/login.component.ts new file mode 100644 index 000000000..1293f72d5 --- /dev/null +++ b/apps/taiga-lumbermill/src/dashboards/login/login.component.ts @@ -0,0 +1,91 @@ +import {CommonModule} from '@angular/common'; +import {ChangeDetectionStrategy, Component, inject} from '@angular/core'; +import {toSignal} from '@angular/core/rxjs-interop'; +import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; +import {Router} from '@angular/router'; +import { + TuiAppearance, + TuiButton, + TuiError, + TuiLabel, + TuiLink, + TuiLoader, + TuiTitle, +} from '@taiga-ui/core'; +import {TUI_VALIDATION_ERRORS, TuiCheckbox, TuiFieldErrorPipe} from '@taiga-ui/kit'; +import {TuiCardLarge, TuiHeader} from '@taiga-ui/layout'; +import {TuiInputModule, TuiInputPasswordModule} from '@taiga-ui/legacy'; +import {map, of, startWith, Subject, switchMap, timer} from 'rxjs'; + +@Component({ + standalone: true, + selector: 'lmb-login', + imports: [ + CommonModule, + ReactiveFormsModule, + TuiAppearance, + TuiButton, + TuiCardLarge, + TuiCheckbox, + TuiError, + TuiFieldErrorPipe, + TuiHeader, + TuiInputModule, + TuiInputPasswordModule, + TuiLabel, + TuiLink, + TuiLoader, + TuiTitle, + ], + templateUrl: './login.component.html', + styleUrl: './login.component.less', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: TUI_VALIDATION_ERRORS, + useValue: { + required: "Value can't be empty", + email: 'Invalid email', + minlength: ({requiredLength}: {requiredLength: string}) => + of(`Minimum length — ${requiredLength}`), + }, + }, + ], +}) +export class LoginComponent { + private readonly router = inject(Router); + protected readonly form = new FormGroup({ + email: new FormControl('', [Validators.required, Validators.email]), + password: new FormControl('', [Validators.required, Validators.minLength(5)]), + rememberMe: new FormControl(false), + }); + + protected readonly submit$ = new Subject(); + + protected readonly submitLoader = toSignal( + this.submit$.pipe( + switchMap(() => + timer(4000).pipe( + map(() => this.goMain()), + startWith(true), + ), + ), + ), + {initialValue: false}, + ); + + protected goMain(): boolean { + this.router.navigate(['']); + + return false; + } + + protected onSubmit(): void { + if (!this.form.valid) { + return; + } + + this.form.disable(); + this.submit$.next(); + } +}