Rework folder structure in frontend

Pages are now grouped per group they appear in on in the header (where possible)
Utilities used by multiple pages in the project are grouped in folders such as services/pipes/etc
This commit is contained in:
2025-07-04 19:50:21 +02:00
parent c42fc38b2c
commit ebe66c87c0
169 changed files with 123 additions and 113 deletions
@@ -0,0 +1,5 @@
<app-forms [currentPage]="'appeal'" [formTitle]="'Minecraft Appeal'">
<div form-content>
</div>
</app-forms>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppealComponent } from './appeal.component';
describe('AppealComponent', () => {
let component: AppealComponent;
let fixture: ComponentFixture<AppealComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppealComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AppealComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,69 @@
import {Component, OnInit} from '@angular/core';
import {FormsComponent} from '../forms.component';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {AppealsService, MinecraftAppeal} from '@api';
@Component({
selector: 'app-appeal',
imports: [
FormsComponent
],
templateUrl: './appeal.component.html',
styleUrl: './appeal.component.scss'
})
export class AppealComponent implements OnInit {
public form: FormGroup<Appeal>;
constructor(private appealApi: AppealsService) {
this.form = new FormGroup({
username: new FormControl('', {nonNullable: true, validators: [Validators.required]}),
punishmentId: new FormControl('', {nonNullable: true, validators: [Validators.required]}),
email: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.email]}),
appeal: new FormControl('', {nonNullable: true, validators: [Validators.required, Validators.minLength(10)]})
});
}
ngOnInit() {
}
public onSubmit() {
if (this.form === undefined) {
console.error('Form is undefined');
return
}
if (this.form.valid) {
this.sendForm()
} else {
// Mark all fields as touched to trigger validation display
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 sendForm() {
const rawValue = this.form.getRawValue();
const appeal: MinecraftAppeal = {
appeal: rawValue.appeal,
email: rawValue.email,
punishmentId: parseInt(rawValue.punishmentId),
username: rawValue.username,
uuid: ''//TODO
}
this.appealApi.submitMinecraftAppeal(appeal).subscribe()
}
}
interface Appeal {
username: FormControl<string>;
punishmentId: FormControl<string>;
email: FormControl<string>;
appeal: FormControl<string>;
}
@@ -0,0 +1,5 @@
export enum FormType {
APPEAL = 'appeal',
STAFF_APPLICATION = 'staff_application',
CONTACT = 'contact'
}
@@ -0,0 +1,18 @@
<ng-container>
<app-header [current_page]="currentPage" height="200px" background_image="/public/img/backgrounds/staff.png"
[overlay_gradient]="0.5">
<div class="title" header-content>
<h1>{{ formTitle }}</h1>
</div>
</app-header>
<ng-container *ngIf="!type">
<ng-container *ngFor="let formType of FormType | keyvalue">
<button mat-raised-button (click)="setFormType(formType.value)">
{{ formType }}
</button>
</ng-container>
</ng-container>
<div>
<ng-content select="[form-content]"></ng-content>
</div>
</ng-container>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsComponent } from './forms.component';
describe('FormsComponent', () => {
let component: FormsComponent;
let fixture: ComponentFixture<FormsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FormsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(FormsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,68 @@
import {Component, Input, OnInit} from '@angular/core';
import {HeaderComponent} from '@header/header.component';
import {MatDialog} from '@angular/material/dialog';
import {ActivatedRoute} from '@angular/router';
import {LoginDialogComponent} from '@shared-components/login/login.component';
import {KeyValuePipe, NgForOf, NgIf} from '@angular/common';
import {FormType} from './form_type';
import {MatButton} from '@angular/material/button';
import {AuthService} from '@services/auth.service';
@Component({
selector: 'app-forms',
imports: [
HeaderComponent,
NgIf,
NgForOf,
MatButton,
KeyValuePipe
],
templateUrl: './forms.component.html',
styleUrl: './forms.component.scss'
})
export class FormsComponent implements OnInit {
@Input() formTitle: string = 'Form';
@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;
}
}