Refactor FormsComponent and add AuthGuard for appeal route protection. Replace dynamic routing with static appeal route, restructure AppealComponent layout, and introduce responsive design adjustments. Update environment configuration for default auth status.

This commit is contained in:
akastijn 2025-08-03 00:09:27 +02:00
parent 1f03a4bdc3
commit c277306c2c
10 changed files with 140 additions and 82 deletions

View File

@ -107,8 +107,12 @@ export const routes: Routes = [
loadComponent: () => import('./pages/reference/staffpowers/staffpowers.component').then(m => m.StaffpowersComponent) loadComponent: () => import('./pages/reference/staffpowers/staffpowers.component').then(m => m.StaffpowersComponent)
}, },
{ {
path: 'forms/:form', path: 'forms/appeal',
loadComponent: () => import('./pages/forms/forms.component').then(m => m.FormsComponent) loadComponent: () => import('./pages/forms/appeal/appeal.component').then(m => m.AppealComponent),
canActivate: [AuthGuard],
data: {
requiredAuthorizations: ['SCOPE_user']
}
}, },
{ {
path: 'forms', path: 'forms',

View File

@ -2,6 +2,7 @@ import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router'; import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {AuthService} from '@services/auth.service'; import {AuthService} from '@services/auth.service';
import {environment} from '@environment';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -18,7 +19,9 @@ export class AuthGuard implements CanActivate {
route: ActivatedRouteSnapshot, route: ActivatedRouteSnapshot,
state: RouterStateSnapshot state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
if (environment.defaultAuthStatus) {
return true;
}
if (!this.authService.checkAuthStatus()) { if (!this.authService.checkAuthStatus()) {
return this.router.createUrlTree(['/']); return this.router.createUrlTree(['/']);
} }

View File

@ -1,5 +1,19 @@
<app-forms [currentPage]="'appeal'" [formTitle]="'Minecraft Appeal'"> <div>
<div form-content> <app-header [current_page]="'appeal'" height="200px" background_image="/public/img/backgrounds/staff.png"
[overlay_gradient]="0.5">
<div class="title" header-content>
<h1>Appeal</h1>
</div> </div>
</app-forms> </app-header>
<main>
<section class="darkmodeSection appeal-container">
<section class="columnSection">
<div class="columnContainer">
<div class="columnParagraph">
<p>hi</p>
</div>
</div>
</section>
</section>
</main>
</div>

View File

@ -0,0 +1,16 @@
:host {
display: block;
}
.appeal-container {
display: flex;
flex-direction: column;
min-height: 80vh;
}
main {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
}

View File

@ -1,21 +1,27 @@
import {Component, OnInit} from '@angular/core'; import {AfterViewInit, Component, ElementRef, OnInit, Renderer2} from '@angular/core';
import {FormsComponent} from '../forms.component';
import {FormControl, FormGroup, Validators} from '@angular/forms'; import {FormControl, FormGroup, Validators} from '@angular/forms';
import {AppealsService, MinecraftAppeal} from '@api'; import {AppealsService, MinecraftAppeal} from '@api';
import {HeaderComponent} from '@header/header.component';
@Component({ @Component({
selector: 'app-appeal', selector: 'app-appeal',
imports: [ imports: [
FormsComponent HeaderComponent
], ],
templateUrl: './appeal.component.html', templateUrl: './appeal.component.html',
styleUrl: './appeal.component.scss' styleUrl: './appeal.component.scss'
}) })
export class AppealComponent implements OnInit { export class AppealComponent implements OnInit, AfterViewInit {
public form: FormGroup<Appeal>; public form: FormGroup<Appeal>;
private resizeObserver: ResizeObserver | null = null;
private boundHandleResize: any;
constructor(private appealApi: AppealsService) { constructor(
private appealApi: AppealsService,
private elementRef: ElementRef,
private renderer: Renderer2
) {
this.form = new FormGroup({ this.form = new FormGroup({
username: new FormControl('', {nonNullable: true, validators: [Validators.required]}), username: new FormControl('', {nonNullable: true, validators: [Validators.required]}),
punishmentId: new FormControl('', {nonNullable: true, validators: [Validators.required]}), punishmentId: new FormControl('', {nonNullable: true, validators: [Validators.required]}),
@ -27,6 +33,62 @@ export class AppealComponent implements OnInit {
ngOnInit() { ngOnInit() {
} }
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() { public onSubmit() {
if (this.form === undefined) { if (this.form === undefined) {
console.error('Form is undefined'); console.error('Form is undefined');
@ -35,7 +97,6 @@ export class AppealComponent implements OnInit {
if (this.form.valid) { if (this.form.valid) {
this.sendForm() this.sendForm()
} else { } else {
// Mark all fields as touched to trigger validation display
Object.keys(this.form.controls).forEach(field => { Object.keys(this.form.controls).forEach(field => {
const control = this.form!.get(field); const control = this.form!.get(field);
if (!(control instanceof FormGroup)) { if (!(control instanceof FormGroup)) {

View File

@ -5,14 +5,20 @@
<h1>{{ formTitle }}</h1> <h1>{{ formTitle }}</h1>
</div> </div>
</app-header> </app-header>
@if (!type) { <main>
@for (formType of FormType | keyvalue; track formType) { <section class="darkmodeSection">
<button mat-raised-button (click)="setFormType(formType.value)"> <section class="columnSection">
{{ formType }} <div class="columnContainer">
</button> <div class="columnParagraph">
} <a [routerLink]="['/forms/appeal']">
} <h2>Appeal</h2>
<div> <p>
<ng-content select="[form-content]"></ng-content> If you feel your punishment was unjust, click here to appeal.
</p>
</a>
</div> </div>
</div>
</section>
</section>
</main>
</ng-container> </ng-container>

View File

@ -1,66 +1,17 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input} from '@angular/core';
import {HeaderComponent} from '@header/header.component'; import {HeaderComponent} from '@header/header.component';
import {MatDialog} from '@angular/material/dialog'; import {RouterLink} from '@angular/router';
import {ActivatedRoute} from '@angular/router';
import {LoginDialogComponent} from '@shared-components/login/login.component';
import { KeyValuePipe } from '@angular/common';
import {FormType} from './form_type';
import {MatButton} from '@angular/material/button';
import {AuthService} from '@services/auth.service';
@Component({ @Component({
selector: 'app-forms', selector: 'app-forms',
imports: [ imports: [
HeaderComponent, HeaderComponent,
MatButton, RouterLink
KeyValuePipe ],
],
templateUrl: './forms.component.html', templateUrl: './forms.component.html',
styleUrl: './forms.component.scss' styleUrl: './forms.component.scss'
}) })
export class FormsComponent implements OnInit { export class FormsComponent {
@Input() formTitle: string = 'Form'; @Input() formTitle: string = 'Form';
@Input() currentPage: string = 'forms'; @Input() currentPage: string = 'forms';
public type: FormType | undefined;
constructor(private authService: AuthService,
private dialog: MatDialog,
private route: ActivatedRoute,
) {
this.route.paramMap.subscribe(async params => {
const code = params.get('code');
if (code) {
this.authService.login(code).subscribe();
} else if (!this.authService.checkAuthStatus()) {
const dialogRef = this.dialog.open(LoginDialogComponent, {
width: '400px',
disableClose: true
});
dialogRef.afterClosed().subscribe();
}
});
}
ngOnInit() {
this.route.paramMap.subscribe(params => {
switch (params.get('form')) {
case FormType.APPEAL:
this.type = FormType.APPEAL;
this.currentPage = 'appeal';
break;
default:
throw new Error("Invalid type");
}
});
}
protected readonly FormType = FormType;
protected readonly Object = Object;
public setFormType(formType: FormType) {
this.type = formType;
}
} }

View File

@ -5,12 +5,13 @@ import {catchError, tap} from 'rxjs/operators';
import {MatSnackBar} from '@angular/material/snack-bar'; import {MatSnackBar} from '@angular/material/snack-bar';
import {JwtHelperService} from '@auth0/angular-jwt'; import {JwtHelperService} from '@auth0/angular-jwt';
import {JwtClaims} from '@custom-types/jwt_interface' import {JwtClaims} from '@custom-types/jwt_interface'
import {environment} from '@environment';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class AuthService { export class AuthService {
private isAuthenticatedSubject = new BehaviorSubject<boolean>(false); private isAuthenticatedSubject = new BehaviorSubject<boolean>(environment.defaultAuthStatus);
public isAuthenticated$ = this.isAuthenticatedSubject.asObservable(); public isAuthenticated$ = this.isAuthenticatedSubject.asObservable();
private userClaimsSubject = new BehaviorSubject<JwtClaims | null>(null); private userClaimsSubject = new BehaviorSubject<JwtClaims | null>(null);

View File

@ -1,4 +1,5 @@
export const environment = { export const environment = {
production: false, production: false,
apiUrl: 'http://localhost:8080' apiUrl: 'http://localhost:8080',
defaultAuthStatus: true
}; };

View File

@ -1,4 +1,5 @@
export const environment = { export const environment = {
production: true, production: true,
apiUrl: 'https://alttd.com' apiUrl: 'https://alttd.com',
defaultAuthStatus: false
}; };