Refactor AppealComponent to remove dynamic height logic, integrate FullSizeComponent, and simplify imports.

This commit is contained in:
akastijn 2025-11-22 01:22:15 +01:00
parent 5876298ae9
commit 20ec3648c4
3 changed files with 145 additions and 196 deletions

View File

@ -6,156 +6,161 @@
</div> </div>
</app-header> </app-header>
<main> <main>
<section class="darkmodeSection appeal-container"> <app-full-size>
<div class="form-container"> <section class="darkmodeSection appeal-container">
<div class="pages"> <div class="form-container">
@if (currentPageIndex === 0) { <div class="pages">
@if (history()?.length === 0) { @if (currentPageIndex === 0) {
<section class="formPage"> @if (history()?.length === 0) {
<img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/> <section class="formPage">
<h1>Punishment Appeal</h1> <img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/>
<p>You have no punishments to appeal.</p> <h1>Punishment Appeal</h1>
</section> <p>You have no punishments to appeal.</p>
} @else { </section>
<section class="formPage"> } @else {
<img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/> <section class="formPage">
<h1>Punishment Appeal</h1> <img ngSrc="/public/img/logos/logo.png" alt="Discord" height="319" width="550"/>
<p>We aim to respond within 48 hours.</p> <h1>Punishment Appeal</h1>
<button mat-raised-button (click)="nextPage()" [disabled]="history() == null"> <p>We aim to respond within 48 hours.</p>
@if (history() == null) { <button mat-raised-button (click)="nextPage()" [disabled]="history() == null">
<mat-spinner></mat-spinner> @if (history() == null) {
} @else { <mat-spinner></mat-spinner>
Next } @else {
} Next
</button> }
</section> </button>
} </section>
}
@if (currentPageIndex === 1) {
<section class="formPage">
<div class="description">
<p>You are logged in as <strong>{{ authService.username() }}</strong>. If this is the correct account
please continue</p>
<br>
<p><strong>Notice: </strong> Submitting an appeal is <strong>not</strong> an instant process.
We will investigate the punishment you are appealing and respond within 48 hours.</p>
<p style="font-style: italic;">Appeals that seem to have been made with
little to no effort will be automatically denied.</p>
</div>
<button mat-raised-button (click)="nextPage()" [disabled]="authService.username() == null">
I, {{ authService.username() }}, understand and agree
</button>
</section>
}
@if (currentPageIndex === 2) {
<section class="formPage">
<div class="description">
<h2>Please select the punishment you want to appeal</h2>
</div>
<mat-form-field>
<mat-label>Punishment</mat-label>
<mat-select (valueChange)="onPunishmentSelected($event)">
@for (punishment of history(); track punishment) {
<mat-option [value]="punishment">{{ punishment.type }} - {{ punishment.reason }}</mat-option>
}
</mat-select>
</mat-form-field>
@if (selectedPunishment() != null) {
<button mat-raised-button (click)="nextPage()" [disabled]="selectedPunishment() == null">
Appeal {{ selectedPunishment()!.type }}
</button>
} }
</section> }
}
<form [formGroup]="form"> @if (currentPageIndex === 1) {
@if (currentPageIndex === 3) {
<section class="formPage"> <section class="formPage">
<div class="description"> <div class="description">
<h2>Please enter your email.</h2> <p>You are logged in as <strong>{{ authService.username() }}</strong>. If this is the correct
<p style="font-style: italic">It does not have to be your minecraft email. You will have to verify account
it</p> please continue</p>
<mat-form-field appearance="fill" style="width: 100%;"> <br>
<mat-label>Email</mat-label> <p><strong>Notice: </strong> Submitting an appeal is <strong>not</strong> an instant process.
<input matInput We will investigate the punishment you are appealing and respond within 48 hours.</p>
formControlName="email" <p style="font-style: italic;">Appeals that seem to have been made with
placeholder="Email"> little to no effort will be automatically denied.</p>
@if (form.controls.email.invalid && form.controls.email.touched) {
<mat-error>
@if (form.controls.email.errors?.['required']) {
Email is required
} @else if (form.controls.email.errors?.['email']) {
Please enter a valid email address
}
</mat-error>
}
</mat-form-field>
@if (emailIsValid()) {
<div class="valid-email">
<ng-container matSuffix>
<mat-icon>check</mat-icon>
<span>You have validated your email previously, and can continue to the next page!</span>
</ng-container>
</div>
}
</div> </div>
<button mat-raised-button (click)="validateMailOrNextPage()" [disabled]="form.controls.email.invalid"> <button mat-raised-button (click)="nextPage()" [disabled]="authService.username() == null">
Next I, {{ authService.username() }}, understand and agree
</button> </button>
</section> </section>
} }
@if (currentPageIndex === 4) { @if (currentPageIndex === 2) {
<section class="formPage"> <section class="formPage">
<div class="description"> <div class="description">
<h2>Why should your {{ selectedPunishment()?.type }} be reduced or removed?</h2> <h2>Please select the punishment you want to appeal</h2>
<p style="font-style: italic">Please take your time writing this, we're more likely to accept an
appeal if effort was put into it.</p>
<mat-form-field appearance="fill" style="width: 100%;">
<mat-label>Reason</mat-label>
<textarea matInput formControlName="appeal" placeholder="Reason" rows="6"></textarea>
@if (form.controls.appeal.invalid && form.controls.appeal.touched) {
<mat-error>
@if (form.controls.appeal.errors?.['required']) {
Reason is required
} @else if (form.controls.appeal.errors?.['minlength']) {
Reason must be at least 10 characters
}
</mat-error>
}
</mat-form-field>
</div> </div>
<button mat-raised-button (click)="onSubmit()" [disabled]="form.invalid"> <mat-form-field>
Submit Appeal <mat-label>Punishment</mat-label>
</button> <mat-select (valueChange)="onPunishmentSelected($event)">
@for (punishment of history(); track punishment) {
<mat-option [value]="punishment">{{ punishment.type }} - {{ punishment.reason }}</mat-option>
}
</mat-select>
</mat-form-field>
@if (selectedPunishment() != null) {
<button mat-raised-button (click)="nextPage()" [disabled]="selectedPunishment() == null">
Appeal {{ selectedPunishment()!.type }}
</button>
}
</section> </section>
} }
</form>
</div>
<!-- Navigation dots --> <form [formGroup]="form">
@if (totalPages.length > 1) { @if (currentPageIndex === 3) {
<div class="form-navigation"> <section class="formPage">
<button mat-icon-button class="nav-button" (click)="previousPage()" [disabled]="isFirstPage()"> <div class="description">
<mat-icon>navigate_before</mat-icon> <h2>Please enter your email.</h2>
</button> <p style="font-style: italic">It does not have to be your minecraft email. You will have to verify
it</p>
<mat-form-field appearance="fill" style="width: 100%;">
<mat-label>Email</mat-label>
<input matInput
formControlName="email"
placeholder="Email">
@if (form.controls.email.invalid && form.controls.email.touched) {
<mat-error>
@if (form.controls.email.errors?.['required']) {
Email is required
} @else if (form.controls.email.errors?.['email']) {
Please enter a valid email address
}
</mat-error>
}
</mat-form-field>
@if (emailIsValid()) {
<div class="valid-email">
<ng-container matSuffix>
<mat-icon>check</mat-icon>
<span>You have validated your email previously, and can continue to the next page!</span>
</ng-container>
</div>
}
</div>
<button mat-raised-button (click)="validateMailOrNextPage()"
[disabled]="form.controls.email.invalid">
Next
</button>
</section>
}
@for (i of totalPages; track i) { @if (currentPageIndex === 4) {
<div <section class="formPage">
class="nav-dot" <div class="description">
[class.active]="i === currentPageIndex" <h2>Why should your {{ selectedPunishment()?.type }} be reduced or removed?</h2>
(click)="goToPage(i)"> <p style="font-style: italic">Please take your time writing this, we're more likely to accept an
</div> appeal if effort was put into it.</p>
} <mat-form-field appearance="fill" style="width: 100%;">
<mat-label>Reason</mat-label>
<button mat-icon-button class="nav-button" (click)="nextPage()" [disabled]="isLastPage()"> <textarea matInput formControlName="appeal" placeholder="Reason" rows="6"></textarea>
<mat-icon>navigate_next</mat-icon> @if (form.controls.appeal.invalid && form.controls.appeal.touched) {
</button> <mat-error>
@if (form.controls.appeal.errors?.['required']) {
Reason is required
} @else if (form.controls.appeal.errors?.['minlength']) {
Reason must be at least 10 characters
}
</mat-error>
}
</mat-form-field>
</div>
<button mat-raised-button (click)="onSubmit()" [disabled]="form.invalid">
Submit Appeal
</button>
</section>
}
</form>
</div> </div>
}
</div> <!-- Navigation dots -->
</section> @if (totalPages.length > 1) {
<div class="form-navigation">
<button mat-icon-button class="nav-button" (click)="previousPage()" [disabled]="isFirstPage()">
<mat-icon>navigate_before</mat-icon>
</button>
@for (i of totalPages; track i) {
<div
class="nav-dot"
[class.active]="i === currentPageIndex"
(click)="goToPage(i)">
</div>
}
<button mat-icon-button class="nav-button" (click)="nextPage()" [disabled]="isLastPage()">
<mat-icon>navigate_next</mat-icon>
</button>
</div>
}
</div>
</section>
</app-full-size>
</main> </main>
</div> </div>

View File

@ -5,7 +5,7 @@
.appeal-container { .appeal-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 80vh; height: 100%;
} }
main { main {

View File

@ -1,14 +1,4 @@
import { import {Component, computed, inject, OnDestroy, OnInit, signal} from '@angular/core';
AfterViewInit,
Component,
computed,
ElementRef,
inject,
OnDestroy,
OnInit,
Renderer2,
signal
} from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms'; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {AppealsService, EmailEntry, HistoryService, MailService, MinecraftAppeal, PunishmentHistory} from '@api'; import {AppealsService, EmailEntry, HistoryService, MailService, MinecraftAppeal, PunishmentHistory} from '@api';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '@header/header.component';
@ -24,6 +14,7 @@ import {HistoryFormatService} from '@pages/reference/bans/history-format.service
import {MatDialog} from '@angular/material/dialog'; import {MatDialog} from '@angular/material/dialog';
import {VerifyMailDialogComponent} from '@pages/forms/verify-mail-dialog/verify-mail-dialog.component'; import {VerifyMailDialogComponent} from '@pages/forms/verify-mail-dialog/verify-mail-dialog.component';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {FullSizeComponent} from '@shared-components/full-size/full-size.component';
@Component({ @Component({
selector: 'app-appeal', selector: 'app-appeal',
@ -37,11 +28,12 @@ import {Router} from '@angular/router';
MatSelectModule, MatSelectModule,
MatInputModule, MatInputModule,
ReactiveFormsModule, ReactiveFormsModule,
FullSizeComponent,
], ],
templateUrl: './appeal.component.html', templateUrl: './appeal.component.html',
styleUrl: './appeal.component.scss' styleUrl: './appeal.component.scss'
}) })
export class AppealComponent implements OnInit, OnDestroy, AfterViewInit { export class AppealComponent implements OnInit, OnDestroy {
private mailService = inject(MailService); private mailService = inject(MailService);
private historyFormatService = inject(HistoryFormatService); private historyFormatService = inject(HistoryFormatService);
@ -61,10 +53,7 @@ export class AppealComponent implements OnInit, OnDestroy, AfterViewInit {
protected emailIsValid = signal<boolean>(false); protected emailIsValid = signal<boolean>(false);
protected dialog = inject(MatDialog); protected dialog = inject(MatDialog);
constructor( constructor() {
private elementRef: ElementRef,
private renderer: Renderer2
) {
this.form = new FormGroup({ this.form = new FormGroup({
email: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.email]}), email: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.email]}),
appeal: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.minLength(10)]}) appeal: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.minLength(10)]})
@ -97,16 +86,6 @@ export class AppealComponent implements OnInit, OnDestroy, AfterViewInit {
}) })
} }
ngAfterViewInit() {
this.setupResizeObserver();
this.updateContainerHeight();
this.boundHandleResize = this.handleResize.bind(this);
window.addEventListener('resize', this.boundHandleResize);
setTimeout(() => this.updateContainerHeight(), 0);
}
ngOnDestroy() { ngOnDestroy() {
if (this.resizeObserver) { if (this.resizeObserver) {
this.resizeObserver.disconnect(); this.resizeObserver.disconnect();
@ -118,41 +97,6 @@ export class AppealComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
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() { public onSubmit() {
if (this.form === undefined) { if (this.form === undefined) {
console.error('Form is undefined'); console.error('Form is undefined');