255 lines
7.6 KiB
TypeScript
255 lines
7.6 KiB
TypeScript
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<Appeal>;
|
|
protected history = signal<PunishmentHistory[] | null>(null);
|
|
protected selectedPunishment = signal<PunishmentHistory | null>(null);
|
|
private emails = signal<EmailEntry[]>([]);
|
|
protected verifiedEmails = computed(() => this.emails()
|
|
.filter(email => email.verified)
|
|
.map(email => email.email.toLowerCase()));
|
|
protected emailIsValid = signal<boolean>(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<string>;
|
|
appeal: FormControl<string>;
|
|
}
|