277 lines
8.7 KiB
TypeScript
277 lines
8.7 KiB
TypeScript
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' : ''}`;
|
|
}
|
|
}
|