add i18n management, improvements on admin handling, fix service grid
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "we-bstly-angular",
|
||||
"version": "3.5.0",
|
||||
"version": "3.5.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
|
||||
@@ -55,6 +55,7 @@ import { AdminVoucherMappingsComponent } from './pages/admin/voucher-mappings/vo
|
||||
import { AdminSystemProfileFieldsComponent } from './pages/admin/system-profile-fields/system-profile-fields.component';
|
||||
import { AdminUserAliasesComponent } from './pages/admin/user-aliases/user-aliases.component';
|
||||
import { AdminOidcClientsComponent } from './pages/admin/oidc-clients/oidc-clients.component';
|
||||
import { AdminI18nComponent } from './pages/admin/i18n/i18n.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'profile/:username', component: UserComponent, canActivate: [AuthUpdateGuard] },
|
||||
@@ -125,7 +126,8 @@ const routes: Routes = [
|
||||
{ path: 'partey-maps', component: AdminParteyMapsComponent, canActivate: [AdminGuard] },
|
||||
{ path: 'partey-tags', component: AdminParteyTagsComponent, canActivate: [AdminGuard] },
|
||||
{ path: 'partey-reports', component: AdminParteyReportsComponent, canActivate: [AdminGuard] },
|
||||
{ path: 'timeslots', component: AdminTimeslotsComponent, canActivate: [AdminGuard] }
|
||||
{ path: 'timeslots', component: AdminTimeslotsComponent, canActivate: [AdminGuard] },
|
||||
{ path: 'i18n', component: AdminI18nComponent, canActivate: [AdminGuard] }
|
||||
]
|
||||
},
|
||||
{ path: 'unavailable', component: UnavailableComponent },
|
||||
|
||||
@@ -97,6 +97,8 @@ import { AdminSystemProfileFieldsComponent } from './pages/admin/system-profile-
|
||||
import { AdminUserAliasesComponent } from './pages/admin/user-aliases/user-aliases.component';
|
||||
import { AdminOidcClientsComponent } from './pages/admin/oidc-clients/oidc-clients.component';
|
||||
import { AdminOidcClientEditDialog } from './pages/admin/oidc-clients/oidc-client.edit';
|
||||
import { AdminI18nComponent } from './pages/admin/i18n/i18n.component';
|
||||
import { AdminI18nEditDialog } from './pages/admin/i18n/i18n.edit';
|
||||
import { AdminVoucherMappingEditDialog } from './pages/admin/voucher-mappings/voucher-mapping.edit';
|
||||
import { AdminSystemPropertyEditDialog } from './pages/admin/system-properties/system-property.edit';
|
||||
import { AdminSystemProfileFieldEditDialog } from './pages/admin/system-profile-fields/system-profile-field.edit';
|
||||
@@ -176,7 +178,7 @@ export class XhrInterceptor implements HttpInterceptor {
|
||||
AdminParteyTagsComponent, AdminParteyReportsComponent, AdminTimeslotsComponent,
|
||||
AdminSystemPropertiesComponent, AdminPermissionMappingsComponent, AdminQuotaMappingsComponent,
|
||||
AdminVoucherMappingsComponent, AdminSystemProfileFieldsComponent, AdminUserAliasesComponent,
|
||||
AdminOidcClientsComponent
|
||||
AdminOidcClientsComponent, AdminI18nComponent, AdminI18nEditDialog
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
[active]="rlaurls.isActive">
|
||||
{{'admin.shortened_urls' | i18n}}
|
||||
</a>
|
||||
<a mat-tab-link routerLink="i18n" routerLinkActive #rli18n="routerLinkActive"
|
||||
[active]="rli18n.isActive">
|
||||
{{'admin.i18n' | i18n}}
|
||||
</a>
|
||||
<!--
|
||||
<a mat-tab-link routerLink="minetest-accounts" routerLinkActive #rlaminetest="routerLinkActive"
|
||||
[active]="rlaminetest.isActive">
|
||||
|
||||
@@ -3,7 +3,28 @@
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
margin: 0 8px;
|
||||
flex: 0 0 150px;
|
||||
|
||||
&.grow {
|
||||
flex: 1 1 200px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
@@ -33,3 +54,17 @@ mat-checkbox {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Expired items styling */
|
||||
.expired-row {
|
||||
background-color: rgba(244, 67, 54, 0.1) !important;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
td {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.expired-row:hover {
|
||||
background-color: rgba(244, 67, 54, 0.15) !important;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<header>
|
||||
<h3>{{'admin.i18n' | i18n}}</h3>
|
||||
<span class="spacer"></span>
|
||||
|
||||
<mat-form-field subscriptSizing="dynamic">
|
||||
<mat-label>{{ 'admin.i18n.locale' | i18n }}</mat-label>
|
||||
<mat-select [(ngModel)]="selectedLocale" (selectionChange)="onLocaleChange()">
|
||||
@for (locale of locales; track locale) {
|
||||
<mat-option [value]="locale">{{ locale }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@if (!rawJsonMode) {
|
||||
<mat-form-field class="grow" subscriptSizing="dynamic">
|
||||
<mat-label>{{ 'admin.filter' | i18n }}</mat-label>
|
||||
<input matInput [(ngModel)]="searchFilter" (ngModelChange)="onFilterChange()"
|
||||
[placeholder]="'admin.filter_placeholder' | i18n">
|
||||
@if (searchFilter) {
|
||||
<button mat-icon-button matSuffix (click)="searchFilter = ''; onFilterChange()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-raised-button color="accent" (click)="createEntry()">
|
||||
<mat-icon>add</mat-icon>
|
||||
{{ 'admin.i18n.create' | i18n }}
|
||||
</button>
|
||||
}
|
||||
|
||||
<button mat-raised-button [color]="rawJsonMode ? 'warn' : 'primary'" (click)="toggleRawJsonMode()">
|
||||
<mat-icon>{{ rawJsonMode ? 'table_chart' : 'code' }}</mat-icon>
|
||||
{{ (rawJsonMode ? 'admin.i18n.table_mode' : 'admin.i18n.raw_json_mode') | i18n }}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
@if (loading) {
|
||||
<mat-progress-bar mode="indeterminate" class="loading-indicator"></mat-progress-bar>
|
||||
}
|
||||
|
||||
@if (rawJsonMode) {
|
||||
<div class="raw-json-editor">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>{{ 'admin.i18n.raw_json' | i18n }}</mat-label>
|
||||
<textarea matInput [(ngModel)]="rawJsonData" rows="20"
|
||||
[placeholder]="'admin.i18n.raw_json_placeholder' | i18n"></textarea>
|
||||
@if (rawJsonError) {
|
||||
<mat-error>{{ rawJsonError }}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="actions">
|
||||
<button mat-raised-button color="primary" (click)="saveRawJson()" [disabled]="loading">
|
||||
<mat-icon>save</mat-icon>
|
||||
{{ 'admin.save' | i18n }}
|
||||
</button>
|
||||
<button mat-button (click)="toggleRawJsonMode()">
|
||||
{{ 'admin.cancel' | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!rawJsonMode) {
|
||||
@if (!loading && entries.length === 0 && !searchFilter) {
|
||||
<div class="empty-state">
|
||||
<mat-icon>translate</mat-icon>
|
||||
<p>{{ 'admin.i18n.empty' | i18n }}</p>
|
||||
</div>
|
||||
} @else {
|
||||
@if (!loading && dataSource.filteredData.length === 0 && searchFilter) {
|
||||
<div class="empty-state">
|
||||
<mat-icon>search_off</mat-icon>
|
||||
<p>{{ 'admin.no_results' | i18n }}</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="table-container">
|
||||
<table mat-table [dataSource]="dataSource" class="admin-table">
|
||||
|
||||
<ng-container matColumnDef="key">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'admin.i18n.key' | i18n }}</th>
|
||||
<td mat-cell *matCellDef="let entry">
|
||||
<code>{{ entry.key }}</code>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'admin.i18n.value' | i18n }}</th>
|
||||
<td mat-cell *matCellDef="let entry">{{ entry.value }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'admin.actions' | i18n }}</th>
|
||||
<td mat-cell *matCellDef="let entry">
|
||||
<button mat-icon-button (click)="editEntry(entry)" [matTooltip]="'admin.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteEntry(entry)" [matTooltip]="'admin.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" [pageSize]="25" showFirstLastButtons
|
||||
aria-label="Select page of i18n labels">
|
||||
</mat-paginator>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
mat-form-field {
|
||||
margin-bottom: 0;
|
||||
|
||||
&:first-of-type {
|
||||
flex: 0 0 100px;
|
||||
}
|
||||
|
||||
&:nth-of-type(2) {
|
||||
flex: 1 1 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.raw-json-editor {
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 1rem;
|
||||
|
||||
button + button {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { I18nManagementService } from '../../../services/admin/i18n.management.service';
|
||||
import { I18nService } from '../../../services/i18n.service';
|
||||
import { ConfirmDialog } from '../../../ui/confirm/confirm.component';
|
||||
import { AdminI18nEditDialog } from './i18n.edit';
|
||||
|
||||
|
||||
interface I18nEntry {
|
||||
key: string;
|
||||
value: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
standalone: false,
|
||||
selector: 'app-admin-i18n',
|
||||
templateUrl: './i18n.component.html',
|
||||
styleUrls: ['../admin.scss', './i18n.component.scss']
|
||||
})
|
||||
export class AdminI18nComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||
|
||||
dataSource = new MatTableDataSource<I18nEntry>([]);
|
||||
entries: I18nEntry[] = [];
|
||||
loading: boolean = false;
|
||||
selectedLocale: string = '';
|
||||
locales: string[] = [];
|
||||
searchFilter: string = '';
|
||||
rawJsonMode: boolean = false;
|
||||
rawJsonData: string = '';
|
||||
rawJsonError: string = '';
|
||||
|
||||
displayedColumns: string[] = ['key', 'value', 'actions'];
|
||||
|
||||
constructor(
|
||||
private i18nManagementService: I18nManagementService,
|
||||
private i18nService: I18nService,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
// Set custom filter predicate to search both key and value
|
||||
this.dataSource.filterPredicate = (data: I18nEntry, filter: string) => {
|
||||
const searchStr = filter.toLowerCase();
|
||||
return data.key.toLowerCase().includes(searchStr) ||
|
||||
data.value.toLowerCase().includes(searchStr);
|
||||
};
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadLocales();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
}
|
||||
|
||||
loadLocales(): void {
|
||||
this.i18nManagementService.getLocales().subscribe({
|
||||
next: (data: string[]) => {
|
||||
this.locales = data;
|
||||
if (this.locales.length > 0) {
|
||||
this.selectedLocale = this.i18nService.getLocale() || this.locales[0];
|
||||
this.loadLabels();
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error loading locales:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadLabels(): void {
|
||||
if (!this.selectedLocale) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.i18nManagementService.getLabels(this.selectedLocale).subscribe({
|
||||
next: (data: any) => {
|
||||
this.entries = this.flattenObject(data);
|
||||
this.rawJsonData = JSON.stringify(data, null, 2);
|
||||
this.rawJsonError = '';
|
||||
this.dataSource.data = this.entries;
|
||||
// Re-attach paginator after data change
|
||||
if (this.paginator) {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
}
|
||||
this.applyFilter();
|
||||
this.loading = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error loading labels:', error);
|
||||
this.loading = false;
|
||||
this.entries = [];
|
||||
this.dataSource.data = [];
|
||||
this.rawJsonData = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
flattenObject(obj: any, parentPath: string = ''): I18nEntry[] {
|
||||
const result: I18nEntry[] = [];
|
||||
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
const value = obj[key];
|
||||
|
||||
if (key === '.') {
|
||||
// Handle the special "." key - it represents the value at this level
|
||||
result.push({
|
||||
key: parentPath,
|
||||
value: value,
|
||||
path: parentPath.split('.').slice(0, -1).join('.')
|
||||
});
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
// Recursively flatten nested objects
|
||||
const fullKey = parentPath ? `${parentPath}.${key}` : key;
|
||||
result.push(...this.flattenObject(value, fullKey));
|
||||
} else {
|
||||
// Simple string value
|
||||
const fullKey = parentPath ? `${parentPath}.${key}` : key;
|
||||
result.push({
|
||||
key: fullKey,
|
||||
value: value,
|
||||
path: parentPath
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
onLocaleChange(): void {
|
||||
this.loadLabels();
|
||||
}
|
||||
|
||||
applyFilter(): void {
|
||||
this.dataSource.filter = this.searchFilter.trim().toLowerCase();
|
||||
}
|
||||
|
||||
onFilterChange(): void {
|
||||
this.applyFilter();
|
||||
}
|
||||
|
||||
toggleRawJsonMode(): void {
|
||||
this.rawJsonMode = !this.rawJsonMode;
|
||||
if (!this.rawJsonMode && this.rawJsonError) {
|
||||
// If switching back from raw mode with errors, reload
|
||||
this.loadLabels();
|
||||
}
|
||||
}
|
||||
|
||||
saveRawJson(): void {
|
||||
try {
|
||||
const parsed = JSON.parse(this.rawJsonData);
|
||||
this.rawJsonError = '';
|
||||
this.loading = true;
|
||||
|
||||
this.i18nManagementService.setLabels(this.selectedLocale, parsed).subscribe({
|
||||
next: () => {
|
||||
this.loadLabels();
|
||||
this.rawJsonMode = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error saving raw JSON:', error);
|
||||
this.rawJsonError = error.error?.message || 'Error saving JSON data';
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
} catch (e: any) {
|
||||
this.rawJsonError = 'Invalid JSON: ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
createEntry(): void {
|
||||
const dialogRef = this.dialog.open(AdminI18nEditDialog, {
|
||||
data: { locale: this.selectedLocale, entry: null },
|
||||
minWidth: '500px'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.loadLabels();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
editEntry(entry: I18nEntry): void {
|
||||
const dialogRef = this.dialog.open(AdminI18nEditDialog, {
|
||||
data: { locale: this.selectedLocale, entry: entry },
|
||||
minWidth: '500px'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.loadLabels();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteEntry(entry: I18nEntry): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
'label': 'admin.i18n.confirm_delete',
|
||||
'args': [entry.key]
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.removeKeyFromLabels(entry.key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeKeyFromLabels(key: string): void {
|
||||
this.loading = true;
|
||||
|
||||
this.i18nManagementService.getLabels(this.selectedLocale).subscribe({
|
||||
next: (data: any) => {
|
||||
const updated = this.deleteNestedKey(data, key);
|
||||
|
||||
this.i18nManagementService.setLabels(this.selectedLocale, updated).subscribe({
|
||||
next: () => {
|
||||
this.loadLabels();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error deleting label:', error);
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error loading labels for deletion:', error);
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteNestedKey(obj: any, key: string): any {
|
||||
const keys = key.split('.');
|
||||
const newObj = JSON.parse(JSON.stringify(obj)); // Deep clone
|
||||
|
||||
let current = newObj;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
if (!current[keys[i]]) {
|
||||
return newObj;
|
||||
}
|
||||
current = current[keys[i]];
|
||||
}
|
||||
|
||||
delete current[keys[keys.length - 1]];
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
unflattenEntries(): any {
|
||||
const result: any = {};
|
||||
|
||||
for (const entry of this.entries) {
|
||||
this.setNestedValue(result, entry.key, entry.value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
setNestedValue(obj: any, path: string, value: any): void {
|
||||
const keys = path.split('.');
|
||||
let current = obj;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
if (!current[keys[i]]) {
|
||||
current[keys[i]] = {};
|
||||
}
|
||||
current = current[keys[i]];
|
||||
}
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<h2 mat-dialog-title>
|
||||
{{ (data.entry ? 'admin.i18n.edit_label' : 'admin.i18n.create_label') | i18n }}
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
<form>
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ 'admin.i18n.key' | i18n }}</mat-label>
|
||||
<input matInput [(ngModel)]="key" name="key" required
|
||||
[readonly]="data.entry !== null"
|
||||
[placeholder]="'admin.i18n.key_placeholder' | i18n">
|
||||
<mat-hint>{{ 'admin.i18n.key_hint' | i18n }}</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>{{ 'admin.i18n.value' | i18n }}</mat-label>
|
||||
<textarea matInput [(ngModel)]="value" name="value" required
|
||||
rows="4"
|
||||
[placeholder]="'admin.i18n.value_placeholder' | i18n"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
@if (errorMessage) {
|
||||
<mat-error class="error-message">{{ errorMessage }}</mat-error>
|
||||
}
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="onCancel()">{{ 'admin.cancel' | i18n }}</button>
|
||||
<button mat-raised-button color="primary" (click)="onSave()" [disabled]="saving || !isValid()">
|
||||
@if (saving) {
|
||||
<mat-spinner diameter="20"></mat-spinner>
|
||||
}
|
||||
{{ 'admin.save' | i18n }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,18 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
mat-dialog-content {
|
||||
min-width: 500px;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
mat-spinner {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { I18nManagementService } from '../../../services/admin/i18n.management.service';
|
||||
|
||||
interface I18nEntry {
|
||||
key: string;
|
||||
value: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
standalone: false,
|
||||
selector: 'app-admin-i18n-edit',
|
||||
templateUrl: './i18n.edit.html',
|
||||
styleUrls: ['./i18n.edit.scss']
|
||||
})
|
||||
export class AdminI18nEditDialog {
|
||||
|
||||
key: string = '';
|
||||
value: string = '';
|
||||
saving: boolean = false;
|
||||
errorMessage: string = '';
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<AdminI18nEditDialog>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { locale: string; entry: I18nEntry | null },
|
||||
private i18nManagementService: I18nManagementService
|
||||
) {
|
||||
if (data.entry) {
|
||||
this.key = data.entry.key;
|
||||
this.value = data.entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
isValid(): boolean {
|
||||
return this.key.trim().length > 0 && this.value.trim().length > 0;
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
onSave(): void {
|
||||
if (!this.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.saving = true;
|
||||
this.errorMessage = '';
|
||||
|
||||
// Build nested object from key
|
||||
const labels = this.buildNestedObject(this.key, this.value);
|
||||
|
||||
// Use addLabels to merge with existing labels
|
||||
this.i18nManagementService.addLabels(this.data.locale, labels).subscribe({
|
||||
next: () => {
|
||||
this.saving = false;
|
||||
this.dialogRef.close(true);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error saving label:', error);
|
||||
this.errorMessage = error.error?.message || 'admin.i18n.save_error';
|
||||
this.saving = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
buildNestedObject(key: string, value: string): any {
|
||||
const keys = key.split('.');
|
||||
const result: any = {};
|
||||
|
||||
let current = result;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
current[keys[i]] = {};
|
||||
current = current[keys[i]];
|
||||
}
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -9,60 +9,66 @@
|
||||
|
||||
@if (!!jitsiRooms) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="jitsiRooms.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (jitsiRooms.content.length > 0) {
|
||||
<table mat-table [dataSource]="jitsiRooms.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.id}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.owner' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.owner}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.owner' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.owner}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="room">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.room' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.room}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="room">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.room' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.room}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="starts">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.starts | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="starts">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.starts | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="moderationStarts">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.moderation_starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.moderationStarts | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="moderationStarts">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.moderation_starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.moderationStarts | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="expires">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.expires' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.expires | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="expires">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.jitsi_rooms.expires' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room"> {{room.expires | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room">
|
||||
<button mat-icon-button (click)="editRoom(room)" [title]="'admin.jitsi_rooms.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteJitsiRoom(room.id)" [title]="'admin.jitsi_rooms.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let room">
|
||||
<button mat-icon-button (click)="editRoom(room)" [title]="'admin.jitsi_rooms.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteJitsiRoom(room.id)" [title]="'admin.jitsi_rooms.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="jitsiRooms.page.totalElements"
|
||||
[pageSize]="jitsiRooms.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="jitsiRooms.page.totalElements"
|
||||
[pageSize]="jitsiRooms.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (jitsiRooms.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.jitsi_rooms.no_rooms' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,45 +9,51 @@
|
||||
|
||||
@if (!!minetestAccounts) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="minetestAccounts.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (minetestAccounts.content.length > 0) {
|
||||
<table mat-table [dataSource]="minetestAccounts.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.minetest_accounts.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account"> {{account.name}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.minetest_accounts.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account"> {{account.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.minetest_accounts.owner' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account"> {{account.owner}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.minetest_accounts.owner' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account"> {{account.owner}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="created">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.minetest_accounts.created' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account"> {{account.created | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="created">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.minetest_accounts.created' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account"> {{account.created | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account">
|
||||
<button mat-icon-button [title]="'admin.minetest_accounts.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMinetestAccount(account.name)" [title]="'admin.minetest_accounts.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let account">
|
||||
<button mat-icon-button [title]="'admin.minetest_accounts.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMinetestAccount(account.name)" [title]="'admin.minetest_accounts.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="minetestAccounts.page.totalElements"
|
||||
[pageSize]="minetestAccounts.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="minetestAccounts.page.totalElements"
|
||||
[pageSize]="minetestAccounts.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (minetestAccounts.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.minetest_accounts.no_accounts' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,47 +9,53 @@
|
||||
|
||||
@if (!!oidcClients) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="oidcClients.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.oidc_clients.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">{{client.id}}</td>
|
||||
</ng-container>
|
||||
@if (oidcClients.content.length > 0) {
|
||||
<table mat-table [dataSource]="oidcClients.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.oidc_clients.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">{{client.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="clientId">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.oidc_clients.client_id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">{{client.clientId}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="clientId">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.oidc_clients.client_id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">{{client.clientId}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="clientName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.oidc_clients.client_name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">{{client.clientName}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="clientName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.oidc_clients.client_name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">{{client.clientName}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">
|
||||
<button mat-icon-button (click)="editClient(client)" [title]="'admin.oidc_clients.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="createNewSecret(client)" [title]="'admin.oidc_clients.new_secret' | i18n">
|
||||
<mat-icon>vpn_key</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteClient(client)" [title]="'admin.oidc_clients.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let client">
|
||||
<button mat-icon-button (click)="editClient(client)" [title]="'admin.oidc_clients.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="createNewSecret(client)" [title]="'admin.oidc_clients.new_secret' | i18n">
|
||||
<mat-icon>vpn_key</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteClient(client)" [title]="'admin.oidc_clients.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="oidcClients.page.totalElements"
|
||||
[pageSize]="oidcClients.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="oidcClients.page.totalElements"
|
||||
[pageSize]="oidcClients.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (oidcClients.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.oidc_clients.no_clients' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,50 +9,56 @@
|
||||
|
||||
@if (!!parteyMaps) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="parteyMaps.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (parteyMaps.content.length > 0) {
|
||||
<table mat-table [dataSource]="parteyMaps.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.partey_maps.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.id}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.partey_maps.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'admin.partey_maps.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.name}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'admin.partey_maps.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="policyType">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_maps.policy_type' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.policyType}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="policyType">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_maps.policy_type' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.policyType}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="tags">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_maps.tags' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.tags ? map.tags.join(', ') : '-'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="tags">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_maps.tags' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map"> {{map.tags ? map.tags.join(', ') : '-'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map">
|
||||
<button mat-icon-button [title]="'admin.partey_maps.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteParteyMap(map.id)" [title]="'admin.partey_maps.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let map">
|
||||
<button mat-icon-button [title]="'admin.partey_maps.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteParteyMap(map.id)" [title]="'admin.partey_maps.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="parteyMaps.page.totalElements"
|
||||
[pageSize]="parteyMaps.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="parteyMaps.page.totalElements"
|
||||
[pageSize]="parteyMaps.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (parteyMaps.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.partey_maps.no_maps' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,55 +9,61 @@
|
||||
|
||||
@if (!!parteyReports) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="parteyReports.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (parteyReports.content.length > 0) {
|
||||
<table mat-table [dataSource]="parteyReports.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.partey_reports.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.id}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.partey_reports.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="reporterUserUuid">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_reports.reporter' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.reporterUserUuid}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="reporterUserUuid">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_reports.reporter' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.reporterUserUuid}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="reportedUserUuid">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_reports.reported' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.reportedUserUuid}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="reportedUserUuid">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_reports.reported' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.reportedUserUuid}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="reportWorldSlug">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_reports.world' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.reportWorldSlug}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="reportWorldSlug">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_reports.world' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.reportWorldSlug}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="created">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="created"> {{'admin.partey_reports.created' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.created | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="created">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="created"> {{'admin.partey_reports.created' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report"> {{report.created | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report">
|
||||
<button mat-icon-button [title]="'admin.partey_reports.view' | i18n">
|
||||
<mat-icon>visibility</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteParteyReport(report.id)" [title]="'admin.partey_reports.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let report">
|
||||
<button mat-icon-button [title]="'admin.partey_reports.view' | i18n">
|
||||
<mat-icon>visibility</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteParteyReport(report.id)" [title]="'admin.partey_reports.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="parteyReports.page.totalElements"
|
||||
[pageSize]="parteyReports.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="parteyReports.page.totalElements"
|
||||
[pageSize]="parteyReports.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (parteyReports.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.partey_reports.no_reports' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,55 +9,61 @@
|
||||
|
||||
@if (!!parteyTags) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="parteyTags.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (parteyTags.content.length > 0) {
|
||||
<table mat-table [dataSource]="parteyTags.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.partey_tags.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{tag.id}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.partey_tags.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{tag.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="target">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="target"> {{'admin.partey_tags.target' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{tag.target}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="target">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="target"> {{'admin.partey_tags.target' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{tag.target}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'admin.partey_tags.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{tag.name}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'admin.partey_tags.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{tag.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="starts">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_tags.starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{formatDate(tag.starts)}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="starts">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_tags.starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{formatDate(tag.starts)}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="expires">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_tags.expires' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{formatDate(tag.expires)}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="expires">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.partey_tags.expires' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag"> {{formatDate(tag.expires)}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag">
|
||||
<button mat-icon-button [title]="'admin.partey_tags.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteParteyTag(tag.id)" [title]="'admin.partey_tags.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let tag">
|
||||
<button mat-icon-button [title]="'admin.partey_tags.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteParteyTag(tag.id)" [title]="'admin.partey_tags.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="parteyTags.page.totalElements"
|
||||
[pageSize]="parteyTags.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="parteyTags.page.totalElements"
|
||||
[pageSize]="parteyTags.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (parteyTags.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.partey_tags.no_tags' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,54 +9,60 @@
|
||||
|
||||
@if (!!permissionMappings) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="permissionMappings.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.permission_mappings.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.id}}</td>
|
||||
</ng-container>
|
||||
@if (permissionMappings.content.length > 0) {
|
||||
<table mat-table [dataSource]="permissionMappings.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.permission_mappings.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="item">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.permission_mappings.item' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.item}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="item">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.permission_mappings.item' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.item}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="names">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.permission_mappings.names' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.names?.join(', ')}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="names">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.permission_mappings.names' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.names?.join(', ')}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="lifetime">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.permission_mappings.lifetime' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.lifetime}} {{mapping.lifetimeUnit}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="lifetime">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.permission_mappings.lifetime' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.lifetime}} {{mapping.lifetimeUnit}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="product">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.permission_mappings.product' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.product}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="product">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.permission_mappings.product' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.product}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">
|
||||
<button mat-icon-button (click)="editMapping(mapping)" [title]="'admin.permission_mappings.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMapping(mapping)" [title]="'admin.permission_mappings.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">
|
||||
<button mat-icon-button (click)="editMapping(mapping)" [title]="'admin.permission_mappings.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMapping(mapping)" [title]="'admin.permission_mappings.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="permissionMappings.page.totalElements"
|
||||
[pageSize]="permissionMappings.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="permissionMappings.page.totalElements"
|
||||
[pageSize]="permissionMappings.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (permissionMappings.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.permission_mappings.no_mappings' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<mat-icon>add</mat-icon>
|
||||
{{'admin.permissions.create' | i18n}}
|
||||
</button>
|
||||
<button mat-raised-button color="accent" (click)="loadAllPermissions()">
|
||||
<mat-icon>list</mat-icon>
|
||||
{{'admin.permissions.load_all' | i18n}}
|
||||
</button>
|
||||
}
|
||||
</header>
|
||||
|
||||
@@ -62,7 +66,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [class.expired-row]="isExpired(row)"></tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -59,6 +59,23 @@ export class AdminPermissionsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
loadAllPermissions(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.permissionManagementService.getAllPermissionsForUser(this.selectedUsername, 'name')
|
||||
.subscribe({
|
||||
next: (data: any) => {
|
||||
this.permissions = data;
|
||||
this.loading = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error loading all permissions:', error);
|
||||
this.loading = false;
|
||||
this.permissions = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createPermission(): void {
|
||||
const dialogRef = this.dialog.open(AdminPermissionEditDialog, {
|
||||
data: { username: this.selectedUsername, permission: null },
|
||||
@@ -110,4 +127,11 @@ export class AdminPermissionsComponent implements OnInit {
|
||||
formatDate(date: string): string {
|
||||
return date ? new Date(date).toLocaleString() : '-';
|
||||
}
|
||||
|
||||
isExpired(permission: any): boolean {
|
||||
if (!permission.expires) {
|
||||
return false;
|
||||
}
|
||||
return new Date(permission.expires) < new Date();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,54 +9,60 @@
|
||||
|
||||
@if (!!quotaMappings) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="quotaMappings.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.quota_mappings.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.id}}</td>
|
||||
</ng-container>
|
||||
@if (quotaMappings.content.length > 0) {
|
||||
<table mat-table [dataSource]="quotaMappings.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.quota_mappings.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.quota_mappings.name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.name}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.quota_mappings.name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.quota_mappings.value' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.value}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.quota_mappings.value' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.value}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="unit">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.quota_mappings.unit' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.unit}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="unit">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.quota_mappings.unit' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.unit}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="items">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.quota_mappings.items' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.items?.join(', ')}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="items">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.quota_mappings.items' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.items?.join(', ')}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">
|
||||
<button mat-icon-button (click)="editMapping(mapping)" [title]="'admin.quota_mappings.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMapping(mapping)" [title]="'admin.quota_mappings.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">
|
||||
<button mat-icon-button (click)="editMapping(mapping)" [title]="'admin.quota_mappings.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMapping(mapping)" [title]="'admin.quota_mappings.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="quotaMappings.page.totalElements"
|
||||
[pageSize]="quotaMappings.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="quotaMappings.page.totalElements"
|
||||
[pageSize]="quotaMappings.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (quotaMappings.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.quota_mappings.no_mappings' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<mat-icon>add</mat-icon>
|
||||
{{'admin.quotas.create' | i18n}}
|
||||
</button>
|
||||
<button mat-raised-button color="accent" (click)="loadAllQuotas()" style="margin-left: 8px;">
|
||||
<mat-icon>list</mat-icon>
|
||||
{{'admin.quotas.load_all' | i18n}}
|
||||
</button>
|
||||
}
|
||||
</header>
|
||||
|
||||
@@ -62,7 +66,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [class.expired-row]="isExpired(row)"></tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -57,6 +57,23 @@ export class AdminQuotasComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
loadAllQuotas(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.quotaManagementService.getAllQuotasForUser(this.selectedUsername, 'name')
|
||||
.subscribe({
|
||||
next: (data: any) => {
|
||||
this.quotas = data;
|
||||
this.loading = false;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error loading all quotas:', error);
|
||||
this.loading = false;
|
||||
this.quotas = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteQuota(quota: any): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
@@ -112,4 +129,8 @@ export class AdminQuotasComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isExpired(quota: any): boolean {
|
||||
return quota.value <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,50 +9,56 @@
|
||||
|
||||
@if (!!services) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="services.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (services.content.length > 0) {
|
||||
<table mat-table [dataSource]="services.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.name}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="url">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.url' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.url}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="url">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.url' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.url}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="category">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.category' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.category}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="category">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.category' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.category}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="permission">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.permission' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.permission}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="permission">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.services.permission' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service"> {{service.permission}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service">
|
||||
<button mat-icon-button (click)="editService(service)" [title]="'admin.services.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteService(service)" [title]="'admin.services.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let service">
|
||||
<button mat-icon-button (click)="editService(service)" [title]="'admin.services.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteService(service)" [title]="'admin.services.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="services.page.totalElements"
|
||||
[pageSize]="services.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="services.page.totalElements"
|
||||
[pageSize]="services.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (services.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.services.no_services' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
<header>
|
||||
<h3>{{'admin.shortened_urls' | i18n}}</h3>
|
||||
<span class="spacer"></span>
|
||||
<form [formGroup]="searchForm" (ngSubmit)="onSearch()">
|
||||
<mat-form-field class="grow" subscriptSizing="dynamic">
|
||||
<mat-label>{{'admin.shortened_urls.search' | i18n}}</mat-label>
|
||||
<input matInput [placeholder]="'admin.shortened_urls.search_placeholder' | i18n">
|
||||
</mat-form-field>
|
||||
<button mat-raised-button color="primary" type="submit" style="margin-left: 10px;">
|
||||
<mat-icon>search</mat-icon>
|
||||
{{'admin.shortened_urls.search' | i18n}}
|
||||
</button>
|
||||
</form>
|
||||
<button mat-raised-button color="primary" (click)="createShortenedUrl()">
|
||||
<mat-icon>add</mat-icon>
|
||||
{{'admin.shortened_urls.create' | i18n}}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<form [formGroup]="searchForm" (ngSubmit)="onSearch()">
|
||||
<mat-form-field appearance="outline" style="width: 100%; max-width: 400px;">
|
||||
<mat-label>{{'admin.shortened_urls.search' | i18n}}</mat-label>
|
||||
<input matInput formControlName="search" [placeholder]="'admin.shortened_urls.search_placeholder' | i18n">
|
||||
</mat-form-field>
|
||||
<button mat-raised-button color="primary" type="submit" style="margin-left: 10px;">
|
||||
<mat-icon>search</mat-icon>
|
||||
{{'admin.shortened_urls.search' | i18n}}
|
||||
</button>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<br>
|
||||
|
||||
@if (!!shortenedUrls) {
|
||||
<div>
|
||||
@if (shortenedUrls.content.length > 0) {
|
||||
<table mat-table [dataSource]="shortenedUrls.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="code">
|
||||
@@ -64,12 +58,13 @@
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="shortenedUrls.page.totalElements"
|
||||
[pageSize]="shortenedUrls.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="shortenedUrls.page.totalElements"
|
||||
[pageSize]="shortenedUrls.page.size" (page)="updatePages($event)" showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (shortenedUrls.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.shortened_urls.no_urls' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -9,44 +9,50 @@
|
||||
|
||||
@if (!!systemProfileFields) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="systemProfileFields.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_profile_fields.name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">{{field.name}}</td>
|
||||
</ng-container>
|
||||
@if (systemProfileFields.content.length > 0) {
|
||||
<table mat-table [dataSource]="systemProfileFields.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_profile_fields.name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">{{field.name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_profile_fields.type' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">{{field.type}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_profile_fields.type' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">{{field.type}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="uniqueValue">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.system_profile_fields.uniqueValue' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">{{field.uniqueValue ? 'Yes' : 'No'}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="uniqueValue">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.system_profile_fields.uniqueValue' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">{{field.uniqueValue ? 'Yes' : 'No'}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">
|
||||
<button mat-icon-button (click)="editField(field)" [title]="'admin.system_profile_fields.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteField(field)" [title]="'admin.system_profile_fields.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let field">
|
||||
<button mat-icon-button (click)="editField(field)" [title]="'admin.system_profile_fields.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteField(field)" [title]="'admin.system_profile_fields.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="systemProfileFields.page.totalElements"
|
||||
[pageSize]="systemProfileFields.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="systemProfileFields.page.totalElements"
|
||||
[pageSize]="systemProfileFields.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (systemProfileFields.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.system_profile_fields.no_fields' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -12,39 +12,45 @@
|
||||
|
||||
@if (!!systemProperties) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="systemProperties.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="key">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_properties.key' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let property">{{property.key}}</td>
|
||||
</ng-container>
|
||||
@if (systemProperties.content.length > 0) {
|
||||
<table mat-table [dataSource]="systemProperties.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="key">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_properties.key' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let property">{{property.key}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_properties.value' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let property">{{property.value}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.system_properties.value' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let property">{{property.value}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let property">
|
||||
<button mat-icon-button (click)="editProperty(property)" [title]="'admin.system_properties.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteProperty(property)" [title]="'admin.system_properties.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let property">
|
||||
<button mat-icon-button (click)="editProperty(property)" [title]="'admin.system_properties.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteProperty(property)" [title]="'admin.system_properties.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="systemProperties.page.totalElements"
|
||||
[pageSize]="systemProperties.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="systemProperties.page.totalElements"
|
||||
[pageSize]="systemProperties.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (systemProperties.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.system_properties.no_properties' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -55,65 +55,71 @@
|
||||
|
||||
@if (!!timeslots) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="timeslots.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (timeslots.content.length > 0) {
|
||||
<table mat-table [dataSource]="timeslots.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.timeslots.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.id}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.timeslots.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.owner' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.owner}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.owner' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.owner}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.title' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.title}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.title' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.title}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.type' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.type}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.type' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.type}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="visibility">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.visibility' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.visibility}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="visibility">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.visibility' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.visibility}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="start">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.start' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.start | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="start">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.start' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.start | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="end">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.end' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.end | date:'short'}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="end">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'admin.timeslots.end' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot"> {{timeslot.end | date:'short'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot">
|
||||
<button mat-icon-button [title]="'admin.timeslots.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteTimeslot(timeslot.id)" [title]="'admin.timeslots.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let timeslot">
|
||||
<button mat-icon-button [title]="'admin.timeslots.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteTimeslot(timeslot.id)" [title]="'admin.timeslots.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="timeslots.page.totalElements"
|
||||
[pageSize]="timeslots.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="timeslots.page.totalElements"
|
||||
[pageSize]="timeslots.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (timeslots.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.timeslots.no_timeslots' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,49 +9,55 @@
|
||||
|
||||
@if (!!userAliases) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="userAliases.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.id}}</td>
|
||||
</ng-container>
|
||||
@if (userAliases.content.length > 0) {
|
||||
<table mat-table [dataSource]="userAliases.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="alias">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.alias' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.alias}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="alias">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.alias' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.alias}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="target">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.target' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.target}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="target">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.target' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.target}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="visibility">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.visibility' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.visibility}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="visibility">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.user_aliases.visibility' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">{{alias.visibility}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">
|
||||
<button mat-icon-button (click)="editAlias(alias)" [title]="'admin.user_aliases.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteAlias(alias)" [title]="'admin.user_aliases.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let alias">
|
||||
<button mat-icon-button (click)="editAlias(alias)" [title]="'admin.user_aliases.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteAlias(alias)" [title]="'admin.user_aliases.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="userAliases.page.totalElements"
|
||||
[pageSize]="userAliases.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="userAliases.page.totalElements"
|
||||
[pageSize]="userAliases.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (userAliases.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.user_aliases.no_aliases' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -9,49 +9,55 @@
|
||||
|
||||
@if (!!users) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="users.content" matSort (matSortChange)="updateSort($event)">
|
||||
@if (users.content.length > 0) {
|
||||
<table mat-table [dataSource]="users.content" matSort (matSortChange)="updateSort($event)">
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.users.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.id}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="id"> {{'admin.users.id' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.id}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="username">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="username"> {{'admin.users.username' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.username}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="username">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="username"> {{'admin.users.username' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.username}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="status">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="status"> {{'admin.users.status' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.status}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="status">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="status"> {{'admin.users.status' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.status}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user">
|
||||
<button mat-icon-button (click)="editUser(user)" [title]="'admin.users.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [routerLink]="['/admin/permissions']" [queryParams]="{username: user.username}"
|
||||
[title]="'admin.users.view_permissions' | i18n">
|
||||
<mat-icon>security</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [routerLink]="['/admin/quotas']" [queryParams]="{username: user.username}"
|
||||
[title]="'admin.users.view_quotas' | i18n">
|
||||
<mat-icon>data_usage</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteUser(user.username)" [title]="'admin.users.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'admin.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let user">
|
||||
<button mat-icon-button (click)="editUser(user)" [title]="'admin.users.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [routerLink]="['/admin/permissions']" [queryParams]="{username: user.username}"
|
||||
[title]="'admin.users.view_permissions' | i18n">
|
||||
<mat-icon>security</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [routerLink]="['/admin/quotas']" [queryParams]="{username: user.username}"
|
||||
[title]="'admin.users.view_quotas' | i18n">
|
||||
<mat-icon>data_usage</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteUser(user.username)" [title]="'admin.users.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="users.page.totalElements" [pageSize]="users.page.size"
|
||||
(page)="updatePages($event)" showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="users.page.totalElements" [pageSize]="users.page.size"
|
||||
(page)="updatePages($event)" showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (users.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.users.no_users' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -9,54 +9,60 @@
|
||||
|
||||
@if (!!voucherMappings) {
|
||||
<div>
|
||||
<table mat-table [dataSource]="voucherMappings.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.voucher_mappings.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.id}}</td>
|
||||
</ng-container>
|
||||
@if (voucherMappings.content.length > 0) {
|
||||
<table mat-table [dataSource]="voucherMappings.content" matSort (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.voucher_mappings.id' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.voucher_mappings.name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.name}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{'admin.voucher_mappings.name' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="voucher">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.voucher_mappings.voucher' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.voucher}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="voucher">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.voucher_mappings.voucher' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.voucher}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="quota">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.voucher_mappings.quota' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.quota}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="quota">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.voucher_mappings.quota' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.quota}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="free">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.voucher_mappings.free' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.free ? 'Yes' : 'No'}}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="free">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.voucher_mappings.free' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">{{mapping.free ? 'Yes' : 'No'}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">
|
||||
<button mat-icon-button (click)="editMapping(mapping)" [title]="'admin.voucher_mappings.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMapping(mapping)" [title]="'admin.voucher_mappings.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{'admin.actions' | i18n}}</th>
|
||||
<td mat-cell *matCellDef="let mapping">
|
||||
<button mat-icon-button (click)="editMapping(mapping)" [title]="'admin.voucher_mappings.edit' | i18n">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="deleteMapping(mapping)" [title]="'admin.voucher_mappings.delete' | i18n">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="voucherMappings.page.totalElements"
|
||||
[pageSize]="voucherMappings.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
<mat-paginator
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[length]="voucherMappings.page.totalElements"
|
||||
[pageSize]="voucherMappings.page.size"
|
||||
(page)="updatePages($event)"
|
||||
showFirstLastButtons>
|
||||
</mat-paginator>
|
||||
}
|
||||
|
||||
@if (voucherMappings.content.length === 0) {
|
||||
<p style="text-align: center; margin-top: 20px;">{{'admin.voucher_mappings.no_mappings' | i18n}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -13,41 +13,29 @@
|
||||
</header>
|
||||
|
||||
@if (!baseServices) {
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
|
||||
@if (baseServices && baseServices.length == 0) {
|
||||
<p>{{'services.empty' | i18n}}</p>
|
||||
<p>{{'services.empty' | i18n}}</p>
|
||||
}
|
||||
|
||||
@if (view=='grid') {
|
||||
<div>
|
||||
@if (baseServices) {
|
||||
<app-services-grid [services]="baseServices"></app-services-grid>
|
||||
}
|
||||
<br>
|
||||
@for (item of serviceCategory | keyvalue; track item) {
|
||||
<div>
|
||||
<h4>{{'services.category.' + item.key | i18n}}</h4>
|
||||
<app-services-grid [services]="item.value"></app-services-grid>
|
||||
<br>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (baseServices) {
|
||||
<app-services-grid [services]="baseServices"></app-services-grid>
|
||||
}
|
||||
@for (item of serviceCategory | keyvalue; track item) {
|
||||
<h4>{{'services.category.' + item.key | i18n}}</h4>
|
||||
<app-services-grid [services]="item.value"></app-services-grid>
|
||||
}
|
||||
}
|
||||
|
||||
@if (view=='table') {
|
||||
<div>
|
||||
@if (baseServices) {
|
||||
<app-services-table [services]="baseServices"></app-services-table>
|
||||
}
|
||||
<br>
|
||||
@for (item of serviceCategory | keyvalue; track item) {
|
||||
<div>
|
||||
<h4>{{'services.category.' + item.key | i18n}}</h4>
|
||||
<app-services-table [services]="item.value"></app-services-table>
|
||||
<br>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (view=='table') {
|
||||
@if (baseServices) {
|
||||
<app-services-table [services]="baseServices"></app-services-table>
|
||||
}
|
||||
@for (item of serviceCategory | keyvalue; track item) {
|
||||
<h4>{{'services.category.' + item.key | i18n}}</h4>
|
||||
<app-services-table [services]="item.value"></app-services-table>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class I18nManagementService {
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
getLocales(): Observable<string[]> {
|
||||
return this.http.get<string[]>(environment.apiUrl + "/i18n");
|
||||
}
|
||||
|
||||
getLabels(locale: string): Observable<any> {
|
||||
return this.http.get(environment.apiUrl + "/i18n/" + locale);
|
||||
}
|
||||
|
||||
setLabels(locale: string, labels: any): Observable<void> {
|
||||
return this.http.post<void>(environment.apiUrl + "/i18n/" + locale, labels);
|
||||
}
|
||||
|
||||
addLabels(locale: string, labels: any): Observable<void> {
|
||||
return this.http.put<void>(environment.apiUrl + "/i18n/" + locale, labels);
|
||||
}
|
||||
|
||||
deleteLocale(locale: string): Observable<void> {
|
||||
return this.http.delete<void>(environment.apiUrl + "/i18n/" + locale);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,32 @@
|
||||
<div class="service-grid" fxLayoutGap="24px grid">
|
||||
<div class="service-grid">
|
||||
@for (service of services; track service) {
|
||||
<div>
|
||||
<mat-card>
|
||||
@if (service.url) {
|
||||
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'"
|
||||
color="accent">
|
||||
<mat-card-header>
|
||||
<div class="icon" mat-card-avatar>
|
||||
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
|
||||
</div>
|
||||
<mat-card-title> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
|
||||
@if (!service.sameSite) {
|
||||
<mat-icon inline="true">
|
||||
open_in_new</mat-icon>
|
||||
}
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
</a>
|
||||
}
|
||||
@if (!service.url) {
|
||||
<mat-card-header>
|
||||
<div class="icon" mat-card-avatar>
|
||||
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
|
||||
</div>
|
||||
<mat-card-title> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
|
||||
|
||||
<mat-card>
|
||||
@if (service.url) {
|
||||
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" color="accent">
|
||||
<mat-card-header>
|
||||
<div class="icon" mat-card-avatar>
|
||||
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
|
||||
</div>
|
||||
<mat-card-title> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
|
||||
@if (!service.sameSite) {
|
||||
<mat-icon inline="true">
|
||||
open_in_new</mat-icon>
|
||||
}
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
</a>
|
||||
}
|
||||
@if (!service.url) {
|
||||
<mat-card-header>
|
||||
<div class="icon" mat-card-avatar>
|
||||
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
|
||||
</div>
|
||||
<mat-card-title> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
}
|
||||
<mat-card-content>
|
||||
<p>
|
||||
@@ -37,6 +36,5 @@
|
||||
<mat-card-actions>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -5,6 +5,7 @@
|
||||
display: grid;
|
||||
column-gap: 24px;
|
||||
row-gap: 24px;
|
||||
grid-auto-rows: 1fr; /* This makes all cards in each row equal height */
|
||||
|
||||
@media (min-width: 576px) {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -21,7 +22,7 @@
|
||||
mat-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
".": "Administration",
|
||||
"actions": "Aktionen",
|
||||
"cancel": "Abbrechen",
|
||||
"edit": "Bearbeiten",
|
||||
"delete": "Löschen",
|
||||
"filter": "Filtern",
|
||||
"filter_placeholder": "Suchen...",
|
||||
"no_results": "Keine Ergebnisse gefunden",
|
||||
"showing_entries": "{0} von {1} Einträgen angezeigt",
|
||||
"jitsi_rooms": {
|
||||
".": "Jitsi-Räume",
|
||||
"confirm_delete": "Bist du sicher, dass du den Raum '{0}' löschen möchtest?",
|
||||
@@ -20,6 +26,7 @@
|
||||
"moderation_starts": "Moderation beginnt",
|
||||
"moderator": "Moderator",
|
||||
"name": "Name",
|
||||
"no_rooms": "Keine Jitsi-Räume gefunden",
|
||||
"owner": "Besitzer",
|
||||
"owner_hint": "Benutzer-ID des Raumbesitzers",
|
||||
"room": "Raum",
|
||||
@@ -45,6 +52,27 @@
|
||||
"save_config": "Konfiguration speichern",
|
||||
"status": "Status"
|
||||
},
|
||||
"i18n": {
|
||||
".": "Internationalisierung",
|
||||
"title": "Internationalisierungsverwaltung",
|
||||
"locale": "Sprache",
|
||||
"key": "Schlüssel",
|
||||
"value": "Wert",
|
||||
"create": "Label erstellen",
|
||||
"create_label": "I18n-Label erstellen",
|
||||
"edit_label": "I18n-Label bearbeiten",
|
||||
"confirm_delete": "Bist du sicher, dass du das Label '{0}' löschen möchtest?",
|
||||
"key_placeholder": "z.B. admin.users.title",
|
||||
"key_hint": "Verwende Punkt-Notation für verschachtelte Schlüssel",
|
||||
"value_placeholder": "Übersetzungstext eingeben",
|
||||
"empty": "Keine Labels für diese Sprache gefunden",
|
||||
"export": "Labels exportieren",
|
||||
"save_error": "Fehler beim Speichern des Labels. Bitte versuche es erneut.",
|
||||
"raw_json_mode": "Raw-JSON-Modus",
|
||||
"table_mode": "Tabellenmodus",
|
||||
"raw_json": "Raw-JSON-Daten",
|
||||
"raw_json_placeholder": "JSON-Daten hier eingeben..."
|
||||
},
|
||||
"minetest_accounts": {
|
||||
".": "Minetest-Accounts",
|
||||
"confirm_delete": "Bist du sicher, dass du den Minetest-Account '{0}' löschen möchtest?",
|
||||
@@ -53,6 +81,7 @@
|
||||
"delete": "Minetest-Account löschen",
|
||||
"edit": "Minetest-Account bearbeiten",
|
||||
"name": "Name",
|
||||
"no_accounts": "Keine Minetest-Accounts gefunden",
|
||||
"owner": "Besitzer"
|
||||
},
|
||||
"oidc_clients": {
|
||||
@@ -79,6 +108,7 @@
|
||||
"create": "OIDC-Client erstellen",
|
||||
"create_client": "OIDC-Client erstellen",
|
||||
"hide_secret": "Secret verbergen",
|
||||
"no_clients": "Keine OIDC-Clients gefunden",
|
||||
"secret_copied": "Client-Secret in Zwischenablage kopiert",
|
||||
"show_secret": "Secret anzeigen",
|
||||
"delete": "OIDC-Client löschen",
|
||||
@@ -107,6 +137,7 @@
|
||||
"edit": "Partey-Karte bearbeiten",
|
||||
"id": "ID",
|
||||
"name": "Name",
|
||||
"no_maps": "Keine Partey-Karten gefunden",
|
||||
"policy_type": "Richtlinientyp",
|
||||
"tags": "Tags"
|
||||
},
|
||||
@@ -118,6 +149,7 @@
|
||||
"delete": "Meldung löschen",
|
||||
"delete_all": "Alle löschen",
|
||||
"id": "ID",
|
||||
"no_reports": "Keine Partey-Meldungen gefunden",
|
||||
"reported": "Gemeldet",
|
||||
"reporter": "Melder",
|
||||
"view": "Meldung anzeigen",
|
||||
@@ -132,6 +164,7 @@
|
||||
"expires": "Läuft ab",
|
||||
"id": "ID",
|
||||
"name": "Name",
|
||||
"no_tags": "Keine Partey-Tags gefunden",
|
||||
"starts": "Beginnt",
|
||||
"target": "Ziel"
|
||||
},
|
||||
@@ -155,6 +188,7 @@
|
||||
"names": "Namen",
|
||||
"names_hint": "Kommagetrennte Liste von Berechtigungsnamen",
|
||||
"names_required": "Mindestens ein Berechtigungsname ist erforderlich",
|
||||
"no_mappings": "Keine Berechtigungs-Zuordnungen gefunden",
|
||||
"product": "Produkt",
|
||||
"starts": "Beginnt",
|
||||
"starts_question": "Beginnt-Frage",
|
||||
@@ -175,6 +209,7 @@
|
||||
"expires": "Läuft ab",
|
||||
"for_user": "Berechtigungen für {0}",
|
||||
"id": "ID",
|
||||
"load_all": "Alle Berechtigungen laden",
|
||||
"name": "Name",
|
||||
"no_permissions": "Keine Berechtigungen gefunden",
|
||||
"search": "Suchen",
|
||||
@@ -197,6 +232,7 @@
|
||||
"items_required": "Mindestens ein Element ist erforderlich",
|
||||
"name": "Name",
|
||||
"name_required": "Name ist erforderlich",
|
||||
"no_mappings": "Keine Kontingent-Zuordnungen gefunden",
|
||||
"products": "Produkte",
|
||||
"products_hint": "Kommagetrennte Liste von Produktnamen",
|
||||
"title": "Kontingent-Zuordnungen",
|
||||
@@ -216,6 +252,7 @@
|
||||
"edit_quota": "Kontingent bearbeiten",
|
||||
"for_user": "Kontingente für {0}",
|
||||
"id": "ID",
|
||||
"load_all": "Alle Kontingente laden",
|
||||
"name": "Name",
|
||||
"name_required": "Name ist erforderlich",
|
||||
"no_quotas": "Keine Kontingente gefunden",
|
||||
@@ -240,6 +277,7 @@
|
||||
"edit_service": "Dienst bearbeiten",
|
||||
"name": "Name",
|
||||
"name_required": "Name ist erforderlich",
|
||||
"no_services": "Keine Dienste gefunden",
|
||||
"permission": "Berechtigung",
|
||||
"same_site": "Gleiche Seite",
|
||||
"url": "URL",
|
||||
@@ -253,6 +291,7 @@
|
||||
"created": "Erstellt",
|
||||
"delete": "Kurz-URL löschen",
|
||||
"edit": "Kurz-URL bearbeiten",
|
||||
"no_urls": "Keine kurzen URLs gefunden",
|
||||
"owner": "Besitzer",
|
||||
"search": "Suchen",
|
||||
"search_placeholder": "Kurz-URLs durchsuchen...",
|
||||
@@ -268,6 +307,7 @@
|
||||
"name": "Name",
|
||||
"name_readonly": "Name kann nach der Erstellung nicht geändert werden",
|
||||
"name_required": "Name ist erforderlich",
|
||||
"no_fields": "Keine Profilfelder gefunden",
|
||||
"required": "Erforderlich",
|
||||
"title": "System-Profilfelder",
|
||||
"type": "Typ",
|
||||
@@ -284,6 +324,7 @@
|
||||
"key": "Schlüssel",
|
||||
"key_readonly": "Schlüssel kann nach der Erstellung nicht geändert werden",
|
||||
"key_required": "Schlüssel ist erforderlich",
|
||||
"no_properties": "Keine Eigenschaften gefunden",
|
||||
"title": "System-Eigenschaften",
|
||||
"update_pretix": "Pretix-Client aktualisieren",
|
||||
"value": "Wert",
|
||||
@@ -302,6 +343,7 @@
|
||||
"filter_type": "Nach Typ filtern",
|
||||
"filter_visibility": "Nach Sichtbarkeit filtern",
|
||||
"id": "ID",
|
||||
"no_timeslots": "Keine Zeitfenster gefunden",
|
||||
"owner": "Besitzer",
|
||||
"search": "Suchen",
|
||||
"start": "Start",
|
||||
@@ -319,6 +361,7 @@
|
||||
"delete": "Benutzer-Alias löschen",
|
||||
"edit": "Benutzer-Alias bearbeiten",
|
||||
"id": "ID",
|
||||
"no_aliases": "Keine Benutzer-Aliase gefunden",
|
||||
"source": "Quelle",
|
||||
"target": "Ziel",
|
||||
"target_hint": "Benutzer-ID des Zielbenutzers",
|
||||
@@ -344,6 +387,7 @@
|
||||
},
|
||||
"id": "ID",
|
||||
"locked": "Gesperrt",
|
||||
"no_users": "Keine Benutzer gefunden",
|
||||
"password": "Passwort",
|
||||
"password2": "Passwort bestätigen",
|
||||
"status": {
|
||||
@@ -367,6 +411,7 @@
|
||||
"id": "ID",
|
||||
"name": "Name",
|
||||
"name_required": "Name ist erforderlich",
|
||||
"no_mappings": "Keine Gutschein-Zuordnungen gefunden",
|
||||
"quota": "Kontingent",
|
||||
"title": "Gutschein-Zuordnungen",
|
||||
"voucher": "Gutschein",
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
".": "Administration",
|
||||
"actions": "Actions",
|
||||
"cancel": "Cancel",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"filter": "Filter",
|
||||
"filter_placeholder": "Search...",
|
||||
"no_results": "No results found",
|
||||
"showing_entries": "Showing {0} of {1} entries",
|
||||
"jitsi_rooms": {
|
||||
".": "Jitsi Rooms",
|
||||
"confirm_delete": "Are you sure you want to delete room '{0}'?",
|
||||
@@ -20,6 +26,7 @@
|
||||
"moderation_starts": "Moderation Starts",
|
||||
"moderator": "Moderator",
|
||||
"name": "Name",
|
||||
"no_rooms": "No jitsi rooms found",
|
||||
"owner": "Owner",
|
||||
"owner_hint": "User ID of the room owner",
|
||||
"room": "Room",
|
||||
@@ -45,6 +52,27 @@
|
||||
"save_config": "Save Configuration",
|
||||
"status": "Status"
|
||||
},
|
||||
"i18n": {
|
||||
".": "Internationalization",
|
||||
"title": "Internationalization Management",
|
||||
"locale": "Locale",
|
||||
"key": "Key",
|
||||
"value": "Value",
|
||||
"create": "Create Label",
|
||||
"create_label": "Create I18n Label",
|
||||
"edit_label": "Edit I18n Label",
|
||||
"confirm_delete": "Are you sure you want to delete label '{0}'?",
|
||||
"key_placeholder": "e.g., admin.users.title",
|
||||
"key_hint": "Use dot notation for nested keys",
|
||||
"value_placeholder": "Enter translation text",
|
||||
"empty": "No labels found for this locale",
|
||||
"export": "Export Labels",
|
||||
"save_error": "Error saving label. Please try again.",
|
||||
"raw_json_mode": "Raw JSON Mode",
|
||||
"table_mode": "Table Mode",
|
||||
"raw_json": "Raw JSON Data",
|
||||
"raw_json_placeholder": "Enter JSON data here..."
|
||||
},
|
||||
"minetest_accounts": {
|
||||
".": "Minetest Accounts",
|
||||
"confirm_delete": "Are you sure you want to delete minetest account '{0}'?",
|
||||
@@ -53,6 +81,7 @@
|
||||
"delete": "Delete Minetest Account",
|
||||
"edit": "Edit Minetest Account",
|
||||
"name": "Name",
|
||||
"no_accounts": "No minetest accounts found",
|
||||
"owner": "Owner"
|
||||
},
|
||||
"oidc_clients": {
|
||||
@@ -91,6 +120,7 @@
|
||||
"login_url": "Login URL",
|
||||
"logout_settings": "Logout Settings",
|
||||
"new_secret": "Generate New Secret",
|
||||
"no_clients": "No OIDC clients found",
|
||||
"redirect_uris": "Redirect URIs",
|
||||
"redirect_uris_hint": "Comma-separated list of redirect URIs",
|
||||
"redirect_uris_required": "At least one redirect URI is required",
|
||||
@@ -107,6 +137,7 @@
|
||||
"edit": "Edit Partey Map",
|
||||
"id": "ID",
|
||||
"name": "Name",
|
||||
"no_maps": "No partey maps found",
|
||||
"policy_type": "Policy Type",
|
||||
"tags": "Tags"
|
||||
},
|
||||
@@ -118,6 +149,7 @@
|
||||
"delete": "Delete Report",
|
||||
"delete_all": "Delete All",
|
||||
"id": "ID",
|
||||
"no_reports": "No partey reports found",
|
||||
"reported": "Reported",
|
||||
"reporter": "Reporter",
|
||||
"view": "View Report",
|
||||
@@ -132,6 +164,7 @@
|
||||
"expires": "Expires",
|
||||
"id": "ID",
|
||||
"name": "Name",
|
||||
"no_tags": "No partey tags found",
|
||||
"starts": "Starts",
|
||||
"target": "Target"
|
||||
},
|
||||
@@ -155,6 +188,7 @@
|
||||
"names": "Names",
|
||||
"names_hint": "Comma-separated list of permission names",
|
||||
"names_required": "At least one permission name is required",
|
||||
"no_mappings": "No permission mappings found",
|
||||
"product": "Product",
|
||||
"starts": "Starts",
|
||||
"starts_question": "Starts Question",
|
||||
@@ -175,6 +209,7 @@
|
||||
"expires": "Expires",
|
||||
"for_user": "Permissions for {0}",
|
||||
"id": "ID",
|
||||
"load_all": "Load All Permissions",
|
||||
"name": "Name",
|
||||
"no_permissions": "No permissions found",
|
||||
"search": "Search",
|
||||
@@ -197,6 +232,7 @@
|
||||
"items_required": "At least one item is required",
|
||||
"name": "Name",
|
||||
"name_required": "Name is required",
|
||||
"no_mappings": "No quota mappings found",
|
||||
"products": "Products",
|
||||
"products_hint": "Comma-separated list of product names",
|
||||
"title": "Quota Mappings",
|
||||
@@ -216,6 +252,7 @@
|
||||
"edit_quota": "Edit Quota",
|
||||
"for_user": "Quotas for {0}",
|
||||
"id": "ID",
|
||||
"load_all": "Load All Quotas",
|
||||
"name": "Name",
|
||||
"name_required": "Name is required",
|
||||
"no_quotas": "No quotas found",
|
||||
@@ -240,6 +277,7 @@
|
||||
"edit_service": "Edit Service",
|
||||
"name": "Name",
|
||||
"name_required": "Name is required",
|
||||
"no_services": "No services found",
|
||||
"permission": "Permission",
|
||||
"same_site": "Same Site",
|
||||
"url": "URL",
|
||||
@@ -253,6 +291,7 @@
|
||||
"created": "Created",
|
||||
"delete": "Delete Shortened URL",
|
||||
"edit": "Edit Shortened URL",
|
||||
"no_urls": "No shortened URLs found",
|
||||
"owner": "Owner",
|
||||
"search": "Search",
|
||||
"search_placeholder": "Search shortened URLs...",
|
||||
@@ -268,6 +307,7 @@
|
||||
"name": "Name",
|
||||
"name_readonly": "Name cannot be changed after creation",
|
||||
"name_required": "Name is required",
|
||||
"no_fields": "No profile fields found",
|
||||
"required": "Required",
|
||||
"title": "System Profile Fields",
|
||||
"type": "Type",
|
||||
@@ -284,6 +324,7 @@
|
||||
"key": "Key",
|
||||
"key_readonly": "Key cannot be changed after creation",
|
||||
"key_required": "Key is required",
|
||||
"no_properties": "No properties found",
|
||||
"title": "System Properties",
|
||||
"update_pretix": "Update Pretix Client",
|
||||
"value": "Value",
|
||||
@@ -302,6 +343,7 @@
|
||||
"filter_type": "Filter by Type",
|
||||
"filter_visibility": "Filter by Visibility",
|
||||
"id": "ID",
|
||||
"no_timeslots": "No timeslots found",
|
||||
"owner": "Owner",
|
||||
"search": "Search",
|
||||
"start": "Start",
|
||||
@@ -319,6 +361,7 @@
|
||||
"delete": "Delete User Alias",
|
||||
"edit": "Edit User Alias",
|
||||
"id": "ID",
|
||||
"no_aliases": "No user aliases found",
|
||||
"source": "Source",
|
||||
"target": "Target",
|
||||
"target_hint": "User ID of the target user",
|
||||
@@ -344,6 +387,7 @@
|
||||
},
|
||||
"id": "ID",
|
||||
"locked": "Locked",
|
||||
"no_users": "No users found",
|
||||
"password": "Password",
|
||||
"password2": "Confirm Password",
|
||||
"status": {
|
||||
@@ -367,6 +411,7 @@
|
||||
"id": "ID",
|
||||
"name": "Name",
|
||||
"name_required": "Name is required",
|
||||
"no_mappings": "No voucher mappings found",
|
||||
"quota": "Quota",
|
||||
"title": "Voucher Mappings",
|
||||
"voucher": "Voucher",
|
||||
|
||||
Reference in New Issue
Block a user