import { AfterViewInit, Component, computed, ElementRef, inject, OnDestroy, OnInit, Renderer2, signal } from '@angular/core'; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; import {AppealsService, EmailEntry, HistoryService, MailService, MinecraftAppeal, PunishmentHistory} from '@api'; import {HeaderComponent} from '@header/header.component'; import {NgOptimizedImage} from '@angular/common'; import {MatButtonModule} from '@angular/material/button'; import {MatIconModule} from '@angular/material/icon'; import {AuthService} from '@services/auth.service'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatSelectModule} from '@angular/material/select'; import {MatInputModule} from '@angular/material/input'; import {HistoryFormatService} from '@pages/reference/bans/history-format.service'; import {MatDialog} from '@angular/material/dialog'; import {VerifyMailDialogComponent} from '@pages/forms/verify-mail-dialog/verify-mail-dialog.component'; import {Router} from '@angular/router'; @Component({ selector: 'app-appeal', imports: [ HeaderComponent, NgOptimizedImage, MatButtonModule, MatIconModule, MatProgressSpinnerModule, MatFormFieldModule, MatSelectModule, MatInputModule, ReactiveFormsModule, ], templateUrl: './appeal.component.html', styleUrl: './appeal.component.scss' }) export class AppealComponent implements OnInit, OnDestroy, AfterViewInit { private mailService = inject(MailService); private historyFormatService = inject(HistoryFormatService); private appealsService = inject(AppealsService); private historyService = inject(HistoryService); public authService = inject(AuthService); private resizeObserver: ResizeObserver | null = null; private boundHandleResize: any; protected form: FormGroup; protected history = signal(null); protected selectedPunishment = signal(null); private emails = signal([]); protected verifiedEmails = computed(() => this.emails() .filter(email => email.verified) .map(email => email.email.toLowerCase())); protected emailIsValid = signal(false); protected dialog = inject(MatDialog); constructor( private elementRef: ElementRef, private renderer: Renderer2 ) { this.form = new FormGroup({ email: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.email]}), appeal: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.minLength(10)]}) }); this.mailService.getUserEmails().subscribe(emails => { this.emails.set(emails); }); this.form.valueChanges.subscribe(() => { if (this.verifiedEmails().includes(this.form.getRawValue().email.toLowerCase())) { this.emailIsValid.set(true); } else { this.emailIsValid.set(false); } }); computed(() => { if (this.verifiedEmails().length > 0) { this.form.get('email')?.setValue(this.verifiedEmails()[0]); this.emailIsValid.set(true); } }); } ngOnInit() { const uuid = this.authService.getUuid(); if (uuid === null) { throw new Error('JWT subject is null, are you logged in?'); } this.historyService.getAllHistoryForUUID(uuid).subscribe(history => { this.history.set(history.filter(item => this.historyFormatService.isActive(item))); }) } ngAfterViewInit() { this.setupResizeObserver(); this.updateContainerHeight(); this.boundHandleResize = this.handleResize.bind(this); window.addEventListener('resize', this.boundHandleResize); setTimeout(() => this.updateContainerHeight(), 0); } ngOnDestroy() { if (this.resizeObserver) { this.resizeObserver.disconnect(); this.resizeObserver = null; } if (this.boundHandleResize) { window.removeEventListener('resize', this.boundHandleResize); } } private handleResize() { this.updateContainerHeight(); } private setupResizeObserver() { this.resizeObserver = new ResizeObserver(() => { this.updateContainerHeight(); }); const headerElement = document.querySelector('app-header'); if (headerElement) { this.resizeObserver.observe(headerElement); } const footerElement = document.querySelector('footer'); if (footerElement) { this.resizeObserver.observe(footerElement); } } private updateContainerHeight() { const headerElement = document.querySelector('app-header'); const footerElement = document.querySelector('footer'); const container = this.elementRef.nativeElement.querySelector('.appeal-container'); if (headerElement && footerElement && container) { const headerHeight = headerElement.getBoundingClientRect().height; const footerHeight = footerElement.getBoundingClientRect().height; const calculatedHeight = `calc(100vh - ${headerHeight}px - ${footerHeight}px)`; this.renderer.setStyle(container, 'min-height', calculatedHeight); } } public onSubmit() { if (this.form === undefined) { console.error('Form is undefined'); return } if (this.form.valid) { this.sendForm() } else { Object.keys(this.form.controls).forEach(field => { const control = this.form!.get(field); if (!(control instanceof FormGroup)) { console.error('Control [' + control + '] is not a FormGroup'); return; } control.markAsTouched({onlySelf: true}); }); } } private router = inject(Router) private sendForm() { const rawValue = this.form.getRawValue(); const uuid = this.authService.getUuid(); if (uuid === null) { throw new Error('JWT subject is null, are you logged in?'); } const appeal: MinecraftAppeal = { appeal: rawValue.appeal, email: rawValue.email, punishmentId: this.selectedPunishment()!.id, punishmentType: this.selectedPunishment()!.type, username: this.authService.username()!, uuid: uuid } this.appealsService.submitMinecraftAppeal(appeal).subscribe((result) => { if (!result.verified_mail) { throw new Error('Mail not verified'); } this.router.navigate(['/forms/sent'], { state: {message: result.message} }).then(); }) } public currentPageIndex: number = 0; public totalPages: number[] = [0]; public goToPage(pageIndex: number): void { if (pageIndex >= 0 && pageIndex < this.totalPages.length) { this.currentPageIndex = pageIndex; } } public previousPage() { this.goToPage(this.currentPageIndex - 1); } public nextPage() { if (this.currentPageIndex === this.totalPages.length - 1) { this.totalPages.push(this.currentPageIndex + 1); } this.goToPage(this.currentPageIndex + 1); } public isFirstPage(): boolean { return this.currentPageIndex === 0; } public isLastPage(): boolean { return this.currentPageIndex === this.totalPages.length - 1; } protected readonly length = length; onPunishmentSelected($event: PunishmentHistory) { this.selectedPunishment.set($event); } protected validateMailOrNextPage() { if (this.emailIsValid()) { this.nextPage(); return; } const dialogRef = this.dialog.open(VerifyMailDialogComponent, { data: {email: this.form.getRawValue().email}, }); dialogRef.afterClosed().subscribe(result => { if (result === true) { this.emailIsValid.set(true); } }); } } interface Appeal { email: FormControl; appeal: FormControl; }