import { Component, OnInit, inject, DestroyRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatDividerModule } from '@angular/material/divider'; import { AdminService } from '../../../core/services/admin.service'; import { GuestSettings } from '../../../core/models/admin.model'; /** * GuestSettingsEditComponent * * Form component for editing guest access settings. * Allows administrators to configure guest user limitations and features. * * Features: * - Reactive form with validation * - Real-time validation errors * - Settings preview before save * - Form reset functionality * - Success/error handling * - Navigation back to view mode */ @Component({ selector: 'app-guest-settings-edit', standalone: true, imports: [ CommonModule, ReactiveFormsModule, MatCardModule, MatButtonModule, MatIconModule, MatInputModule, MatFormFieldModule, MatSlideToggleModule, MatProgressSpinnerModule, MatTooltipModule, MatDividerModule ], templateUrl: './guest-settings-edit.component.html', styleUrl: './guest-settings-edit.component.scss' }) export class GuestSettingsEditComponent implements OnInit { private readonly adminService = inject(AdminService); private readonly router = inject(Router); private readonly fb = inject(FormBuilder); private readonly destroyRef = inject(DestroyRef); // Service signals readonly settings = this.adminService.guestSettingsState; readonly isLoading = this.adminService.isLoadingSettings; readonly error = this.adminService.settingsError; // Form settingsForm!: FormGroup; isSubmitting = false; originalSettings: GuestSettings | null = null; ngOnInit(): void { this.initializeForm(); this.loadSettings(); } /** * Initialize the form with validation */ private initializeForm(): void { this.settingsForm = this.fb.group({ guestAccessEnabled: [false], maxQuizzesPerDay: [3, [Validators.required, Validators.min(1), Validators.max(100)]], maxQuestionsPerQuiz: [10, [Validators.required, Validators.min(1), Validators.max(50)]], sessionExpiryHours: [24, [Validators.required, Validators.min(1), Validators.max(168)]], upgradePromptMessage: [ 'You\'ve reached your quiz limit. Sign up for unlimited access!', [Validators.required, Validators.minLength(10), Validators.maxLength(500)] ] }); } /** * Load existing settings and populate form */ private loadSettings(): void { // If settings already loaded, use them if (this.settings()) { this.populateForm(this.settings()!); return; } // Otherwise fetch settings this.adminService.getGuestSettings() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(settings => { this.populateForm(settings); }); } /** * Populate form with existing settings */ private populateForm(settings: GuestSettings): void { this.originalSettings = settings; this.settingsForm.patchValue({ guestAccessEnabled: settings.guestAccessEnabled, maxQuizzesPerDay: settings.maxQuizzesPerDay, maxQuestionsPerQuiz: settings.maxQuestionsPerQuiz, sessionExpiryHours: settings.sessionExpiryHours, upgradePromptMessage: settings.upgradePromptMessage }); } /** * Submit form and update settings */ onSubmit(): void { if (this.settingsForm.invalid || this.isSubmitting) { this.settingsForm.markAllAsTouched(); return; } this.isSubmitting = true; const formData = this.settingsForm.value; this.adminService.updateGuestSettings(formData) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { this.isSubmitting = false; // Navigate back to view page after short delay setTimeout(() => { this.router.navigate(['/admin/guest-settings']); }, 1500); }, error: () => { this.isSubmitting = false; } }); } /** * Cancel editing and return to view page */ onCancel(): void { if (this.hasUnsavedChanges()) { if (confirm('You have unsaved changes. Are you sure you want to cancel?')) { this.router.navigate(['/admin/guest-settings']); } } else { this.router.navigate(['/admin/guest-settings']); } } /** * Reset form to original values */ onReset(): void { if (this.originalSettings) { this.populateForm(this.originalSettings); } } /** * Check if form has unsaved changes */ hasUnsavedChanges(): boolean { if (!this.originalSettings) return false; const formValue = this.settingsForm.value; return ( formValue.guestAccessEnabled !== this.originalSettings.guestAccessEnabled || formValue.maxQuizzesPerDay !== this.originalSettings.maxQuizzesPerDay || formValue.maxQuestionsPerQuiz !== this.originalSettings.maxQuestionsPerQuiz || formValue.sessionExpiryHours !== this.originalSettings.sessionExpiryHours || formValue.upgradePromptMessage !== this.originalSettings.upgradePromptMessage ); } /** * Get error message for a form field */ getErrorMessage(fieldName: string): string { const field = this.settingsForm.get(fieldName); if (!field?.errors || !field.touched) return ''; if (field.errors['required']) return 'This field is required'; if (field.errors['min']) return `Minimum value is ${field.errors['min'].min}`; if (field.errors['max']) return `Maximum value is ${field.errors['max'].max}`; if (field.errors['minlength']) return `Minimum length is ${field.errors['minlength'].requiredLength} characters`; if (field.errors['maxlength']) return `Maximum length is ${field.errors['maxlength'].requiredLength} characters`; return 'Invalid value'; } /** * Check if a field has an error */ hasError(fieldName: string): boolean { const field = this.settingsForm.get(fieldName); return !!(field?.invalid && field?.touched); } /** * Get preview of changes */ getChangesPreview(): Array<{label: string, old: any, new: any}> { if (!this.originalSettings || !this.hasUnsavedChanges()) return []; const changes: Array<{label: string, old: any, new: any}> = []; const formValue = this.settingsForm.value; if (formValue.guestAccessEnabled !== this.originalSettings.guestAccessEnabled) { changes.push({ label: 'Guest Access', old: this.originalSettings.guestAccessEnabled ? 'Enabled' : 'Disabled', new: formValue.guestAccessEnabled ? 'Enabled' : 'Disabled' }); } if (formValue.maxQuizzesPerDay !== this.originalSettings.maxQuizzesPerDay) { changes.push({ label: 'Max Quizzes Per Day', old: this.originalSettings.maxQuizzesPerDay, new: formValue.maxQuizzesPerDay }); } if (formValue.maxQuestionsPerQuiz !== this.originalSettings.maxQuestionsPerQuiz) { changes.push({ label: 'Max Questions Per Quiz', old: this.originalSettings.maxQuestionsPerQuiz, new: formValue.maxQuestionsPerQuiz }); } if (formValue.sessionExpiryHours !== this.originalSettings.sessionExpiryHours) { changes.push({ label: 'Session Expiry Hours', old: this.originalSettings.sessionExpiryHours, new: formValue.sessionExpiryHours }); } if (formValue.upgradePromptMessage !== this.originalSettings.upgradePromptMessage) { changes.push({ label: 'Upgrade Prompt Message', old: this.originalSettings.upgradePromptMessage, new: formValue.upgradePromptMessage }); } return changes; } /** * Format expiry time for display */ formatExpiryTime(hours: number): string { if (hours < 24) { return `${hours} hour${hours !== 1 ? 's' : ''}`; } const days = Math.floor(hours / 24); const remainingHours = hours % 24; if (remainingHours === 0) { return `${days} day${days !== 1 ? 's' : ''}`; } return `${days} day${days !== 1 ? 's' : ''} and ${remainingHours} hour${remainingHours !== 1 ? 's' : ''}`; } }