Upgrading to Angular 20:

Replaced checked in package-lock.json by regenerated one.
Ran ng update @angular/core@20 @angular/cli@20
Ran npm install @angular-builders/jest@^20.0.0 @fortawesome/angular-fontawesome@^3.0.0 @swimlane/ngx-graph@^11
Ran npm install ngx-mat-select-search@^8.0.2.
Ran ng update @angular/material@20.
Updated all eslint and typescript to the latest versions.
Removed @angular-devkit/schematics.
Updated fontawesome.
Updated rxjs and tslib.
Moved required node version from 16 to 20.
Updated jest-preset-angular.
Set preserveWhitespaces to false.
Corrected two type declarations in two typescript files to make ng serve run without errors.
Removed unused angular-devkit/schematics from view-user.component.ts .
Updated list of supported browsers.
Upgraded prettier.
Ran prettier . --write the first time.
Ran prettier . --write the second time.
Ran ng generate @angular/core:inject with defaults, excepted a handful of files.
Fixed remaining eslint problems.
Added dist directory to ignores in eslint.config.js.
Let prettier fix formatting, almost everything is just line breaks.
Made htmlhint ignore @if and @for Angular constructs.
Updated node version in Github workflow to 22.12.0 (build.yml).
Updated .env.ts.
Updated Angular datetime format strings from YYYY-MM-DD to yyyy-MM-dd.
Merged changes from dev into angular20 branch.
This commit is contained in:
Felix van Hove
2025-12-08 11:01:01 +01:00
parent f1b4ec2cf2
commit 7fd1486c08
1516 changed files with 39367 additions and 36037 deletions
@@ -1,5 +1,5 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
/** rxjs Imports */
import { Observable } from 'rxjs';
@@ -12,10 +12,7 @@ import { SystemService } from '../system.service';
*/
@Injectable()
export class AuditTrailSearchTemplateResolver {
/**
* @param {SystemService} systemService System service.
*/
constructor(private systemService: SystemService) {}
private systemService = inject(SystemService);
/**
* Returns the Audit Trail Search Template data.
@@ -14,12 +14,11 @@
<mat-form-field class="flex-48">
<mat-label>{{ 'labels.inputs.Status' | translate }}</mat-label>
<mat-select [formControl]="processingResult" (selectionChange)="applyFilter($event.value, 'processingResult')">
<mat-option
*ngFor="let processingResult of auditTrailSearchTemplateData.processingResults"
[value]="processingResult.id"
>
{{ processingResult.processingResult }}
</mat-option>
@for (processingResult of auditTrailSearchTemplateData.processingResults; track processingResult) {
<mat-option [value]="processingResult.id">
{{ processingResult.processingResult }}
</mat-option>
}
</mat-select>
</mat-form-field>
@@ -52,7 +51,7 @@
[max]="maxDate"
[matDatepicker]="fromDatePicker"
[formControl]="fromDate"
placeholder="YYYY-MM-DD"
placeholder="yyyy-MM-dd"
/>
<mat-datepicker-toggle matSuffix [for]="fromDatePicker"></mat-datepicker-toggle>
<mat-datepicker #fromDatePicker></mat-datepicker>
@@ -73,7 +72,7 @@
[max]="maxDate"
[matDatepicker]="toDatePicker"
[formControl]="toDate"
placeholder="YYYY-MM-DD"
placeholder="yyyy-MM-dd"
/>
<mat-datepicker-toggle matSuffix [for]="toDatePicker"></mat-datepicker-toggle>
<mat-datepicker #toDatePicker></mat-datepicker>
@@ -94,7 +93,7 @@
[max]="maxDate"
[matDatepicker]="checkedFromDatePicker"
[formControl]="checkedFromDate"
placeholder="YYYY-MM-DD"
placeholder="yyyy-MM-dd"
/>
<mat-datepicker-toggle matSuffix [for]="checkedFromDatePicker"></mat-datepicker-toggle>
<mat-datepicker #checkedFromDatePicker></mat-datepicker>
@@ -115,7 +114,7 @@
[max]="maxDate"
[matDatepicker]="checkedToDatePicker"
[formControl]="checkedToDate"
placeholder="YYYY-MM-DD"
placeholder="yyyy-MM-dd"
/>
<mat-datepicker-toggle matSuffix [for]="checkedToDatePicker"></mat-datepicker-toggle>
<mat-datepicker #checkedToDatePicker></mat-datepicker>
@@ -130,98 +129,100 @@
<!-- Autocomplete data -->
<mat-autocomplete autoActiveFirstOption #userNameAutocomplete="matAutocomplete" [displayWith]="displayUserName">
<mat-option *ngFor="let user of filteredUserData | async" [value]="{ id: user.id, name: user.username }">
{{ user.username }}
</mat-option>
@for (user of filteredUserData | async; track user) {
<mat-option [value]="{ id: user.id, name: user.username }">
{{ user.username }}
</mat-option>
}
</mat-autocomplete>
<mat-autocomplete autoActiveFirstOption #actionNameAutocomplete="matAutocomplete" [displayWith]="displayActionName">
<mat-option *ngFor="let action of filteredActionData | async" [value]="action">
{{ action }}
</mat-option>
@for (action of filteredActionData | async; track action) {
<mat-option [value]="action">
{{ action }}
</mat-option>
}
</mat-autocomplete>
<mat-autocomplete autoActiveFirstOption #entityNameAutocomplete="matAutocomplete" [displayWith]="displayEntityName">
<mat-option *ngFor="let entity of filteredEntityData | async" [value]="entity">
{{ entity }}
</mat-option>
@for (entity of filteredEntityData | async; track entity) {
<mat-option [value]="entity">
{{ entity }}
</mat-option>
}
</mat-autocomplete>
<mat-autocomplete autoActiveFirstOption #checkerAutocomplete="matAutocomplete" [displayWith]="displayUserName">
<mat-option *ngFor="let user of filteredCheckerData | async" [value]="{ id: user.id, name: user.username }">
{{ user.username }}
</mat-option>
@for (user of filteredCheckerData | async; track user) {
<mat-option [value]="{ id: user.id, name: user.username }">
{{ user.username }}
</mat-option>
}
</mat-autocomplete>
<div class="container mat-elevation-z8 table-container">
<div *ngIf="isLoading">
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
<table *ngIf="!isLoading" mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Trail ID' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.id }}</td>
</ng-container>
@if (isLoading) {
<div>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
}
@if (!isLoading) {
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Trail ID' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.id }}</td>
</ng-container>
<ng-container matColumnDef="resourceId">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Resource ID' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.resourceId }}</td>
</ng-container>
<ng-container matColumnDef="processingResult">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Status' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.processingResult }}</td>
</ng-container>
<ng-container matColumnDef="maker">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Made By' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.maker }}</td>
</ng-container>
<ng-container matColumnDef="actionName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Action' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.actionName }}</td>
</ng-container>
<ng-container matColumnDef="entityName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Entity' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.entityName }}</td>
</ng-container>
<ng-container matColumnDef="officeName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Office' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.officeName }}</td>
</ng-container>
<ng-container matColumnDef="madeOnDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Made Date' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.madeOnDate | datetimeFormat }}</td>
</ng-container>
<ng-container matColumnDef="checker">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Checker' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.checker }}</td>
</ng-container>
<ng-container matColumnDef="checkedOnDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Checked Date' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.checkedOnDate | datetimeFormat }}</td>
</ng-container>
<ng-container matColumnDef="clientIp">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Client Ip' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.ip }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns" [routerLink]="[row.id]" class="select-row"></tr>
</table>
}
<ng-container matColumnDef="resourceId">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Resource ID' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.resourceId }}</td>
</ng-container>
<ng-container matColumnDef="processingResult">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Status' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.processingResult }}</td>
</ng-container>
<ng-container matColumnDef="maker">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Made By' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.maker }}</td>
</ng-container>
<ng-container matColumnDef="actionName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Action' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.actionName }}</td>
</ng-container>
<ng-container matColumnDef="entityName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Entity' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.entityName }}</td>
</ng-container>
<ng-container matColumnDef="officeName">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Office' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.officeName }}</td>
</ng-container>
<ng-container matColumnDef="madeOnDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Made Date' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.madeOnDate | datetimeFormat }}</td>
</ng-container>
<ng-container matColumnDef="checker">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Checker' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.checker }}</td>
</ng-container>
<ng-container matColumnDef="checkedOnDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Checked Date' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.checkedOnDate | datetimeFormat }}</td>
</ng-container>
<ng-container matColumnDef="clientIp">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Client Ip' | translate }}</th>
<td mat-cell *matCellDef="let auditTrail">{{ auditTrail.ip }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns" [routerLink]="[row.id]" class="select-row"></tr>
</table>
<mat-paginator
*ngIf="!isLoading"
[length]="dataSource?.records$ | async"
[pageSize]="10"
[pageSizeOptions]="[10, 25, 50, 100]"
showFirstLastButtons
></mat-paginator>
@if (!isLoading) {
<mat-paginator
[length]="dataSource?.records$ | async"
[pageSize]="10"
[pageSizeOptions]="[10, 25, 50, 100]"
showFirstLastButtons
></mat-paginator>
}
</div>
@@ -1,5 +1,5 @@
/** Angular Imports */
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { Component, OnInit, ViewChild, AfterViewInit, inject } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort, MatSortHeader } from '@angular/material/sort';
import { ActivatedRoute, RouterLink } from '@angular/router';
@@ -17,7 +17,7 @@ import { merge } from 'rxjs';
import { tap, debounceTime, distinctUntilChanged, startWith, map } from 'rxjs/operators';
import { Dates } from 'app/core/utils/dates';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { NgFor, NgIf, AsyncPipe } from '@angular/common';
import { AsyncPipe } from '@angular/common';
import { MatOption, MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
import { MatProgressBar } from '@angular/material/progress-bar';
import {
@@ -66,6 +66,11 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
]
})
export class AuditTrailsComponent implements OnInit, AfterViewInit {
private route = inject(ActivatedRoute);
private systemService = inject(SystemService);
private dateUtils = inject(Dates);
private settingsService = inject(SettingsService);
/** Minimum date allowed. */
minDate = new Date(2000, 0, 1);
/** Maximum date allowed. */
@@ -192,12 +197,7 @@ export class AuditTrailsComponent implements OnInit, AfterViewInit {
* @param {Dates} dateUtils Dates utils
* @param {SettingsService} settingsService Settings Service
*/
constructor(
private route: ActivatedRoute,
private systemService: SystemService,
private dateUtils: Dates,
private settingsService: SettingsService
) {
constructor() {
this.route.data.subscribe((data: { auditTrailSearchTemplate: any }) => {
this.auditTrailSearchTemplateData = data.auditTrailSearchTemplate;
});
@@ -556,7 +556,7 @@ export class AuditTrailsComponent implements OnInit, AfterViewInit {
(fieldName === 'madeOnDate' || fieldName === 'checkedOnDate') &&
row[fieldName] != null &&
row[fieldName] !== ''
? JSON.stringify(this.dateUtils.formatDate(row[fieldName], 'YYYY-MM-DDTHH:mm:ssZ'))
? JSON.stringify(this.dateUtils.formatDate(row[fieldName], 'yyyy-MM-ddTHH:mm:ssZ'))
: JSON.stringify(row[fieldName], replacer)
)
);
@@ -598,6 +598,6 @@ export class AuditTrailsComponent implements OnInit, AfterViewInit {
result.setMinutes(minutes || 0);
result.setSeconds(seconds || 0);
}
return this.dateUtils.formatDate(result, 'YYYY-MM-DDTHH:mm:ssZ');
return this.dateUtils.formatDate(result, 'yyyy-MM-ddTHH:mm:ssZ');
}
}
@@ -1,5 +1,5 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
/** rxjs Imports */
@@ -13,10 +13,7 @@ import { SystemService } from '../../system.service';
*/
@Injectable()
export class AuditTrailResolver {
/**
* @param {SystemService} systemService System service.
*/
constructor(private systemService: SystemService) {}
private systemService = inject(SystemService);
/**
* Returns the Audit Trail data.
@@ -60,38 +60,54 @@
{{ auditTrailData.madeOnDate | datetimeFormat }}
</div>
<div class="flex-50 mat-body-strong" *ngIf="auditTrailData.officeName">
{{ 'labels.inputs.Office' | translate }}
</div>
@if (auditTrailData.officeName) {
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Office' | translate }}
</div>
}
<div class="flex-50" *ngIf="auditTrailData.officeName">
{{ auditTrailData.officeName }}
</div>
@if (auditTrailData.officeName) {
<div class="flex-50">
{{ auditTrailData.officeName }}
</div>
}
<div class="flex-50 mat-body-strong" *ngIf="auditTrailData.savingsAccountNo">
{{ 'labels.inputs.Savings Account No' | translate }}
</div>
@if (auditTrailData.savingsAccountNo) {
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Savings Account No' | translate }}
</div>
}
<div class="flex-50" *ngIf="auditTrailData.savingsAccountNo">
{{ auditTrailData.savingsAccountNo }}
</div>
@if (auditTrailData.savingsAccountNo) {
<div class="flex-50">
{{ auditTrailData.savingsAccountNo }}
</div>
}
<div class="flex-50 mat-body-strong" *ngIf="auditTrailData.groupLevelName">
{{ 'labels.inputs.Group Level' | translate }}
</div>
@if (auditTrailData.groupLevelName) {
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Group Level' | translate }}
</div>
}
<div class="flex-50" *ngIf="auditTrailData.groupLevelName">
{{ auditTrailData.groupLevelName }}
</div>
@if (auditTrailData.groupLevelName) {
<div class="flex-50">
{{ auditTrailData.groupLevelName }}
</div>
}
</div>
<div fxFlex="50%" class="mat-body-strong" *ngIf="auditTrailData.ip">
{{ 'labels.inputs.Client Ip' | translate }}
</div>
@if (auditTrailData.ip) {
<div fxFlex="50%" class="mat-body-strong">
{{ 'labels.inputs.Client Ip' | translate }}
</div>
}
<div fxFlex="50%" *ngIf="auditTrailData.ip">
{{ auditTrailData.ip }}
</div>
@if (auditTrailData.ip) {
<div fxFlex="50%">
{{ auditTrailData.ip }}
</div>
}
</div>
<div class="flex-46 command">
@@ -1,5 +1,5 @@
/** Angular Imports */
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, OnInit, ViewChild, inject } from '@angular/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort, MatSortHeader } from '@angular/material/sort';
@@ -44,6 +44,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
]
})
export class ViewAuditComponent implements OnInit {
private route = inject(ActivatedRoute);
/** Audit Trail Data. */
auditTrailData: any;
/** Columns to be displayed in audit trail table. */
@@ -63,7 +65,7 @@ export class ViewAuditComponent implements OnInit {
* Retrieves the audit trail data from `resolve`.
* @param {ActivatedRoute} route Activated Route.
*/
constructor(private route: ActivatedRoute) {
constructor() {
this.route.data.subscribe((data: { auditTrail: any }) => {
this.auditTrailData = data.auditTrail;
});