import { ChangeDetectionStrategy, Component, computed, HostListener, inject, input, output, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { passwordMatchValidator, PasswordPolicy, } from './change-password-modal.validators'; export type ChangePasswordMode = 'self' | 'admin-reset'; export interface ChangePasswordSubmitPayload { oldPassword: string; newPassword: string; confirmNewPassword: string; } const DEFAULT_POLICY: PasswordPolicy = { minLength: 12, requireMixed: true, description: 'At least 12 characters with upper, lower, digit and symbol', }; @Component({ selector: 'app-change-password-modal', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, ReactiveFormsModule], template: ` @if (open()) { } `, }) export class ChangePasswordModalComponent { private readonly fb = inject(FormBuilder); readonly open = input(false); readonly mode = input('self'); readonly policy = input(DEFAULT_POLICY); readonly errorMessage = input(null); readonly submitting = input(false); readonly fieldErrors = input | null>(null); readonly cancel = output(); readonly submit = output(); readonly form = this.fb.nonNullable.group( { oldPassword: this.fb.nonNullable.control(''), newPassword: this.fb.nonNullable.control('', [Validators.required]), confirmNewPassword: this.fb.nonNullable.control('', [Validators.required]), }, { validators: passwordMatchValidator }, ); readonly formError = computed(() => { const errs = this.form.errors; if (errs?.['passwordMismatch']) return 'Passwords do not match'; return null; }); fieldErrorFor(path: string): string | null { const map = this.fieldErrors(); if (!map) return null; return map[path] ?? null; } @HostListener('document:keydown.escape') onEscape(): void { if (this.open() && !this.submitting()) this.onCancel(); } onCancel(): void { if (this.submitting()) return; this.form.reset({ oldPassword: '', newPassword: '', confirmNewPassword: '' }); this.cancel.emit(); } onSubmit(): void { if (this.submitting()) return; const v = this.form.getRawValue(); if (this.mode() === 'self' && !v.oldPassword) return; if (!v.newPassword || !v.confirmNewPassword) return; if (this.form.invalid) return; this.submit.emit({ oldPassword: v.oldPassword, newPassword: v.newPassword, confirmNewPassword: v.confirmNewPassword, }); } }