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 @@
+
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();
+ }
+}