Refactor AuthenticatedUuid to singleton service and replace static calls across the codebase. Add JWT authority converters, improve punishment expiry handling, and enhance frontend dialog functionality for editing punishments. Extend CORS allowed methods and origins.
This commit is contained in:
+17
-5
@@ -6,13 +6,25 @@
|
||||
<input matInput type="text" [(ngModel)]="reason" placeholder="Enter reason"/>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox [(ngModel)]="isPermanent">Permanent</mat-checkbox>
|
||||
<mat-checkbox [(ngModel)]="isPermanent"><span style="color: black">Permanent</span></mat-checkbox>
|
||||
|
||||
@if (!isPermanent) {
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Expires</mat-label>
|
||||
<input matInput type="datetime-local" [(ngModel)]="expiresAtLocal" [disabled]="isPermanent"/>
|
||||
</mat-form-field>
|
||||
<div class="datetime-container">
|
||||
<mat-form-field appearance="fill" class="date-field">
|
||||
<mat-label>Expiry Date</mat-label>
|
||||
<input matInput [matDatepicker]="dateTimePicker" [(ngModel)]="expiryDate" [disabled]="isPermanent">
|
||||
<mat-hint>MM/DD/YYYY</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="dateTimePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dateTimePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill" class="time-field">
|
||||
<mat-label>Expiry Time</mat-label>
|
||||
<input matInput type="time" [(ngModel)]="expiryTime" [disabled]="isPermanent">
|
||||
<mat-hint>HH:MM (24-hour)</mat-hint>
|
||||
</mat-form-field>
|
||||
<p>Time in timezone: {{ getTimezone() }}</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (errorMessage()) {
|
||||
|
||||
+22
-2
@@ -5,6 +5,26 @@
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #d32f2f; /* Material error color */
|
||||
.datetime-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
|
||||
.date-field {
|
||||
flex: 1;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.time-field {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #beb8b8;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
+42
-27
@@ -13,6 +13,7 @@ import {
|
||||
} from '@angular/material/dialog';
|
||||
import {HistoryService, PunishmentHistory} from '@api';
|
||||
import {firstValueFrom} from 'rxjs';
|
||||
import {MatDatepickerModule} from '@angular/material/datepicker';
|
||||
|
||||
interface EditPunishmentData {
|
||||
punishment: PunishmentHistory;
|
||||
@@ -31,19 +32,22 @@ interface EditPunishmentData {
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatDialogActions,
|
||||
MatDatepickerModule,
|
||||
],
|
||||
templateUrl: './edit-punishment-dialog.component.html',
|
||||
styleUrl: './edit-punishment-dialog.component.scss'
|
||||
})
|
||||
export class EditPunishmentDialogComponent {
|
||||
// Form model
|
||||
reason: string = '';
|
||||
isPermanent: boolean = false;
|
||||
expiresAtLocal: string = '';
|
||||
protected reason: string = '';
|
||||
protected isPermanent: boolean = false;
|
||||
protected expiresAt: Date;
|
||||
protected expiryDate: Date | null = null;
|
||||
protected expiryTime: string = '';
|
||||
|
||||
// UI state
|
||||
isBusy = signal<boolean>(false);
|
||||
errorMessage = signal<string | null>(null);
|
||||
protected isBusy = signal<boolean>(false);
|
||||
protected errorMessage = signal<string | null>(null);
|
||||
|
||||
private historyApi = inject(HistoryService);
|
||||
|
||||
@@ -53,17 +57,15 @@ export class EditPunishmentDialogComponent {
|
||||
) {
|
||||
const punishment = data.punishment;
|
||||
this.reason = punishment.reason ?? '';
|
||||
const permanent = punishment.expiryTime <= 0;
|
||||
this.isPermanent = permanent;
|
||||
if (!permanent) {
|
||||
const date = new Date(punishment.expiryTime);
|
||||
const pad = (n: number) => n.toString().padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1);
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
this.expiresAtLocal = `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
this.isPermanent = punishment.expiryTime <= 0;
|
||||
this.expiresAt = new Date(punishment.expiryTime);
|
||||
|
||||
if (this.expiresAt && !isNaN(this.expiresAt.getTime())) {
|
||||
this.expiryDate = new Date(this.expiresAt);
|
||||
|
||||
const hours = this.expiresAt.getHours().toString().padStart(2, '0');
|
||||
const minutes = this.expiresAt.getMinutes().toString().padStart(2, '0');
|
||||
this.expiryTime = `${hours}:${minutes}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +78,23 @@ export class EditPunishmentDialogComponent {
|
||||
|
||||
private computeUntilMs(): number {
|
||||
if (this.isPermanent) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if (!this.expiresAtLocal) {
|
||||
return -1;
|
||||
|
||||
if (!this.expiryDate) {
|
||||
return 0;
|
||||
}
|
||||
const ms = new Date(this.expiresAtLocal).getTime();
|
||||
|
||||
const combinedDateTime = new Date(this.expiryDate);
|
||||
|
||||
if (this.expiryTime) {
|
||||
const [hours, minutes] = this.expiryTime.split(':').map(Number);
|
||||
combinedDateTime.setHours(hours, minutes, 0, 0);
|
||||
}
|
||||
|
||||
this.expiresAt = combinedDateTime;
|
||||
|
||||
const ms = combinedDateTime.getTime();
|
||||
return isNaN(ms) ? -1 : ms;
|
||||
}
|
||||
|
||||
@@ -95,17 +108,15 @@ export class EditPunishmentDialogComponent {
|
||||
|
||||
const updates: Array<Promise<PunishmentHistory>> = [] as any;
|
||||
|
||||
// Update reason if changed
|
||||
if ((this.reason ?? '') !== (punishment.reason ?? '')) {
|
||||
console.log('Changing reason to ', this.reason, ' from ', punishment.reason, '')
|
||||
updates.push(firstValueFrom(this.historyApi.updatePunishmentReason(punishment.type, punishment.id, this.reason)));
|
||||
}
|
||||
|
||||
// Update expiry for ban/mute only
|
||||
if (punishment.type === 'ban' || punishment.type === 'mute') {
|
||||
const newUntil = this.computeUntilMs();
|
||||
if (newUntil !== punishment.expiryTime) {
|
||||
updates.push(firstValueFrom(this.historyApi.updatePunishmentUntil(punishment.type, punishment.id, newUntil)));
|
||||
}
|
||||
const newUntil = this.computeUntilMs();
|
||||
if (newUntil !== punishment.expiryTime) {
|
||||
console.log('Changing until to ', newUntil, ' from ', punishment.expiryTime, '')
|
||||
updates.push(firstValueFrom(this.historyApi.updatePunishmentUntil(punishment.type, punishment.id, newUntil)));
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
@@ -147,4 +158,8 @@ export class EditPunishmentDialogComponent {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getTimezone() {
|
||||
return Intl.DateTimeFormat().resolvedOptions().timeZone ?? 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export class HistoryFormatService {
|
||||
|
||||
public getAvatarUrl(entry: string, size: string = '25'): string {
|
||||
let uuid = entry.replace('-', '');
|
||||
if (uuid === 'C') {
|
||||
if (uuid === 'C' || uuid === 'Console' || uuid === '[Console]') {
|
||||
uuid = "f78a4d8dd51b4b3998a3230f2de0c670"
|
||||
}
|
||||
return `https://crafatar.com/avatars/${uuid}?size=${size}&overlay`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
|
||||
import {Component, EventEmitter, inject, Input, OnChanges, OnInit, Output} from '@angular/core';
|
||||
import {HistoryService, PunishmentHistory} from '@api';
|
||||
import {catchError, map, Observable, shareReplay} from 'rxjs';
|
||||
import {NgOptimizedImage} from '@angular/common';
|
||||
@@ -38,12 +38,11 @@ export class HistoryComponent implements OnInit, OnChanges {
|
||||
|
||||
public history: PunishmentHistory[] = []
|
||||
|
||||
constructor(private historyApi: HistoryService,
|
||||
public historyFormat: HistoryFormatService,
|
||||
private router: Router,
|
||||
private authService: AuthService,
|
||||
private dialog: MatDialog) {
|
||||
}
|
||||
private historyApi: HistoryService = inject(HistoryService)
|
||||
public historyFormat: HistoryFormatService = inject(HistoryFormatService)
|
||||
private router: Router = inject(Router)
|
||||
private authService: AuthService = inject(AuthService)
|
||||
private dialog: MatDialog = inject(MatDialog)
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.reloadHistory();
|
||||
@@ -116,7 +115,8 @@ export class HistoryComponent implements OnInit, OnChanges {
|
||||
public openEdit(punishment: PunishmentHistory) {
|
||||
if (!this.canEdit()) return;
|
||||
const ref = this.dialog.open(EditPunishmentDialogComponent, {
|
||||
data: {punishment}
|
||||
data: {punishment},
|
||||
width: '500px',
|
||||
});
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {provideRouter} from '@angular/router';
|
||||
import {routes} from './app/app.routes';
|
||||
import {provideHttpClient, withInterceptors} from '@angular/common/http';
|
||||
import {authInterceptor} from '@services/AuthInterceptor';
|
||||
import {provideNativeDateAdapter} from '@angular/material/core';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
@@ -12,6 +13,7 @@ bootstrapApplication(AppComponent, {
|
||||
provideHttpClient(
|
||||
withInterceptors([authInterceptor])
|
||||
),
|
||||
provideNativeDateAdapter()
|
||||
]
|
||||
}).catch(err => console.error(err));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user