upgrade and migrate to angular 20

This commit is contained in:
_Bastler
2025-11-08 19:24:11 +01:00
parent 9213cf6b12
commit ff94ca05ce
51 changed files with 5772 additions and 6650 deletions
+3342 -4626
View File
File diff suppressed because it is too large Load Diff
+22 -23
View File
@@ -11,45 +11,44 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^19.2.10", "@angular/animations": "^20.3.10",
"@angular/cdk": "^19.2.15", "@angular/cdk": "^20.2.12",
"@angular/common": "^19.2.10", "@angular/common": "^20.3.10",
"@angular/compiler": "^19.2.10", "@angular/compiler": "^20.3.10",
"@angular/core": "^19.2.10", "@angular/core": "^20.3.10",
"@angular/forms": "^19.2.10", "@angular/forms": "^20.3.10",
"@angular/material": "^19.2.15", "@angular/material": "^20.2.12",
"@angular/material-moment-adapter": "^19.2.15", "@angular/material-moment-adapter": "^20.2.12",
"@angular/platform-browser": "^19.2.10", "@angular/platform-browser": "^20.3.10",
"@angular/platform-browser-dynamic": "^19.2.10", "@angular/platform-browser-dynamic": "^20.3.10",
"@angular/router": "^19.2.10", "@angular/router": "^20.3.10",
"moment": "^2.30.1", "moment": "^2.30.1",
"ng-qrcode": "^19.0.1", "ng-qrcode": "^20.0.1",
"openpgp": "^6.1.0", "openpgp": "^6.2.2",
"qr-scanner": "^1.4.2", "qr-scanner": "^1.4.2",
"rxjs": "~7.8.2", "rxjs": "~7.8.2",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"zone.js": "~0.15.0" "zone.js": "~0.15.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^19.2.11", "@angular-devkit/build-angular": "^20.3.9",
"@angular/cli": "^19.2.11", "@angular/cli": "^20.3.9",
"@angular/compiler-cli": "^19.2.10", "@angular/compiler-cli": "^20.3.10",
"@angular/localize": "^19.2.10", "@angular/localize": "^20.3.10",
"@types/jasmine": "^5.1.8", "@types/jasmine": "^5.1.12",
"@types/jasminewd2": "^2.0.13", "@types/jasminewd2": "^2.0.13",
"@types/node": "^22.15.17", "@types/node": "^24.10.0",
"@types/openpgp": "^5.0.0", "@types/openpgp": "^5.0.0",
"jasmine-core": "~5.7.1", "jasmine-core": "~5.12.1",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"karma": "^6.4.4", "karma": "^6.4.4",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage-istanbul-reporter": "~3.0.3", "karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "^2.1.0", "karma-jasmine-html-reporter": "^2.1.0",
"protractor": "~7.0.0",
"ts-node": "~10.9.2", "ts-node": "~10.9.2",
"tslint": "~6.1.3", "tslint": "~6.1.3",
"typescript": "~5.8.3" "typescript": "~5.9.3"
} }
} }
+12 -4
View File
@@ -10,15 +10,23 @@
[active]="rlasecurity.isActive">{{'security' | i18n}}</a> [active]="rlasecurity.isActive">{{'security' | i18n}}</a>
<a mat-tab-link routerLink="voucher" routerLinkActive #rlavoucher="routerLinkActive" <a mat-tab-link routerLink="voucher" routerLinkActive #rlavoucher="routerLinkActive"
[active]="rlavoucher.isActive">{{'vouchers' | i18n}}</a> [active]="rlavoucher.isActive">{{'vouchers' | i18n}}</a>
<a *ngIf="advancedView" mat-tab-link routerLink="aliases" #rlaaliases="routerLinkActive" routerLinkActive @if (advancedView) {
<a mat-tab-link routerLink="aliases" #rlaaliases="routerLinkActive" routerLinkActive
[active]="rlaaliases.isActive">{{'user.aliases' | i18n}}</a> [active]="rlaaliases.isActive">{{'user.aliases' | i18n}}</a>
<a *ngIf="advancedView" mat-tab-link routerLink="domains" #rladomains="routerLinkActive" routerLinkActive }
@if (advancedView) {
<a mat-tab-link routerLink="domains" #rladomains="routerLinkActive" routerLinkActive
[active]="rladomains.isActive">{{'user.domains' | i18n}}</a> [active]="rladomains.isActive">{{'user.domains' | i18n}}</a>
<a *ngIf="advancedView" mat-tab-link routerLink="dyndns" #rladyndns="routerLinkActive" routerLinkActive }
@if (advancedView) {
<a mat-tab-link routerLink="dyndns" #rladyndns="routerLinkActive" routerLinkActive
[active]="rladyndns.isActive">{{'user.dyndns' | i18n}}</a> [active]="rladyndns.isActive">{{'user.dyndns' | i18n}}</a>
}
<a style="align-self: center;"> <a style="align-self: center;">
<mat-slide-toggle [(ngModel)]="advancedView"> <mat-slide-toggle [(ngModel)]="advancedView">
<span *ngIf="!advancedView">{{'account.advanced' | i18n}}</span> @if (!advancedView) {
<span>{{'account.advanced' | i18n}}</span>
}
</mat-slide-toggle> </mat-slide-toggle>
</a> </a>
</nav> </nav>
@@ -11,10 +11,12 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
<td mat-cell *matCellDef="let alias"> <td mat-cell *matCellDef="let alias">
<mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)"> <mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility"> @for (visibility of visibilities; track visibility) {
<mat-option [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}} visibility | i18n}}
</mat-option> </mat-option>
}
<mat-select-trigger> <mat-select-trigger>
<mat-icon inline="true">{{'visibility.' + alias.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + alias.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
@@ -41,8 +43,11 @@
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'user.aliases.info' | i18n}}</p> <p>{{'user.aliases.info' | i18n}}</p>
<p *ngIf="!aliasCreation">{{'user.aliases.noQuota' | i18n}}</p> @if (!aliasCreation) {
<div *ngIf="aliasCreation"> <p>{{'user.aliases.noQuota' | i18n}}</p>
}
@if (aliasCreation) {
<div>
<p>{{'user.aliases.left' | i18n:aliasCreation}}</p> <p>{{'user.aliases.left' | i18n:aliasCreation}}</p>
<mat-form-field> <mat-form-field>
<mat-label>{{'user.aliases.alias' | i18n}}</mat-label> <mat-label>{{'user.aliases.alias' | i18n}}</mat-label>
@@ -54,11 +59,12 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'visibility' | i18n}}</mat-label> <mat-label>{{'visibility' | i18n}}</mat-label>
<mat-select [(ngModel)]="alias.visibility" formControlName="visibility"> <mat-select [(ngModel)]="alias.visibility" formControlName="visibility">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility"> @for (visibility of visibilities; track visibility) {
<mat-option [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}} visibility | i18n}}
</mat-option> </mat-option>
}
<mat-select-trigger> <mat-select-trigger>
<mat-icon inline="true">{{'visibility.' + alias.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + alias.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
alias.visibility | i18n}} alias.visibility | i18n}}
@@ -66,12 +72,15 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="aliasCreation && !working" mat-raised-button color="primary" @if (aliasCreation && !working) {
<button type="submit" mat-raised-button color="primary"
[disabled]="form.invalid"> [disabled]="form.invalid">
{{'user.aliases.create' | i18n}} {{'user.aliases.create' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#aliases" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#aliases" class="help-button" matTooltip="{{'help-button' | i18n}}"
@@ -50,9 +50,11 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid"> @if (!working) {
<button type="submit" mat-raised-button color="primary" [disabled]="form.invalid">
{{'user.domains.create' | i18n}} {{'user.domains.create' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#domains" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#domains" class="help-button" matTooltip="{{'help-button' | i18n}}"
@@ -3,15 +3,23 @@
<form (ngSubmit)="create()" #formDirective="ngForm" #dyndnsForm> <form (ngSubmit)="create()" #formDirective="ngForm" #dyndnsForm>
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p *ngIf="dyndnsToken && !dyndnsToken.token">{{'user.dyndns.token.exists' | i18n}}</p> @if (dyndnsToken && !dyndnsToken.token) {
<a *ngIf="dyndnsToken && dyndnsToken.token" mat-button (click)="copySecret()" <p>{{'user.dyndns.token.exists' | i18n}}</p>
}
@if (dyndnsToken && dyndnsToken.token) {
<a mat-button (click)="copySecret()"
matTooltip="{{'user.dyndns.token.copy' | i18n}}" matTooltipPosition="above">{{ dyndnsToken.token}}</a> matTooltip="{{'user.dyndns.token.copy' | i18n}}" matTooltipPosition="above">{{ dyndnsToken.token}}</a>
<p *ngIf="dyndnsToken && dyndnsToken.token">{{'user.dyndns.token.store' | i18n}}</p> }
@if (dyndnsToken && dyndnsToken.token) {
<p>{{'user.dyndns.token.store' | i18n}}</p>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="!working" mat-raised-button color="primary"> @if (!working) {
<button type="submit" mat-raised-button color="primary">
{{(dyndnsToken ? 'user.dyndns.new' : 'user.dyndns.create') | i18n}} {{(dyndnsToken ? 'user.dyndns.new' : 'user.dyndns.create') | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#dyndns" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#dyndns" class="help-button" matTooltip="{{'help-button' | i18n}}"
@@ -3,8 +3,10 @@
{{'security.2fa.totp.hint' | i18n}} {{'security.2fa.totp.hint' | i18n}}
<qr-code *ngIf="data.qrData" [value]="data.qrData" size="400" errorCorrectionLevel="'M'" @if (data.qrData) {
<qr-code [value]="data.qrData" size="400" errorCorrectionLevel="'M'"
title="{{data.qrData}}"></qr-code> title="{{data.qrData}}"></qr-code>
}
{{'security.2fa.totp.activate' | i18n}} {{'security.2fa.totp.activate' | i18n}}
<mat-form-field> <mat-form-field>
@@ -5,19 +5,25 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'password.current' | i18n}}</mat-label> <mat-label>{{'password.current' | i18n}}</mat-label>
<input matInput type="password" formControlName="oldPassword" [(ngModel)]="model.old"> <input matInput type="password" formControlName="oldPassword" [(ngModel)]="model.old">
<mat-error *ngFor="let error of passwordForm.get('oldPassword').errors | keyvalue"> @for (error of passwordForm.get('oldPassword').errors | keyvalue; track error) {
<mat-error>
{{error.key}} {{error.key}}
</mat-error> </mat-error>
<mat-hint *ngIf="success"> }
@if (success) {
<mat-hint>
{{'password.changed' | i18n}} {{'password.changed' | i18n}}
</mat-hint> </mat-hint>
}
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'password' | i18n}}</mat-label> <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password" [(ngModel)]="model.password"> <input matInput type="password" formControlName="password" [(ngModel)]="model.password">
<mat-error *ngFor="let error of passwordForm.get('password').errors | keyvalue"> @for (error of passwordForm.get('password').errors | keyvalue; track error) {
<mat-error>
{{error.key}} {{error.key}}
</mat-error> </mat-error>
}
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'password.confirm' | i18n}}</mat-label> <mat-label>{{'password.confirm' | i18n}}</mat-label>
@@ -28,10 +34,14 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> @if (working) {
<button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="passwordForm.invalid"> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
@if (!working) {
<button type="submit" mat-raised-button color="primary" [disabled]="passwordForm.invalid">
{{'password.change' | i18n}} {{'password.change' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</form> </form>
@@ -44,21 +54,29 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'security.status' | i18n}}</mat-label> <mat-label>{{'security.status' | i18n}}</mat-label>
<mat-select [(ngModel)]="model.status" formControlName="status"> <mat-select [(ngModel)]="model.status" formControlName="status">
<mat-option *ngFor="let status of statuses" [value]="status"> @for (status of statuses; track status) {
<mat-option [value]="status">
{{'security.status.' + status | i18n}} {{'security.status.' + status | i18n}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
<mat-hint *ngIf="successStatus"> @if (successStatus) {
<mat-hint>
{{'security.status.success' | i18n}} {{'security.status.success' | i18n}}
</mat-hint> </mat-hint>
}
</mat-form-field> </mat-form-field>
<mat-label>{{'security.status.' + model.status + '.hint' | i18n}}</mat-label> <mat-label>{{'security.status.' + model.status + '.hint' | i18n}}</mat-label>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> @if (working) {
<button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="statusForm.invalid"> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
@if (!working) {
<button type="submit" mat-raised-button color="primary" [disabled]="statusForm.invalid">
{{'security.status.change' | i18n}} {{'security.status.change' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#status" class="help-button" <a href="https://wiki.bstly.de/services/webstly#status" class="help-button"
@@ -75,10 +93,14 @@
<p>{{'security.2fa.info' | i18n}}</p> <p>{{'security.2fa.info' | i18n}}</p>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a *ngIf="!totp" (click)="createTotp()" mat-raised-button color="accent">{{'security.2fa.totp.create' | @if (!totp) {
<a (click)="createTotp()" mat-raised-button color="accent">{{'security.2fa.totp.create' |
i18n}}</a> i18n}}</a>
<a *ngIf="totp" (click)="removeTotp()" mat-raised-button color="warn">{{'security.2fa.totp.remove' | }
@if (totp) {
<a (click)="removeTotp()" mat-raised-button color="warn">{{'security.2fa.totp.remove' |
i18n}}</a> i18n}}</a>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#2fa" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#2fa" class="help-button" matTooltip="{{'help-button' | i18n}}"
@@ -3,9 +3,11 @@
<p>{{'vouchers.info' | i18n}}</p> <p>{{'vouchers.info' | i18n}}</p>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a *ngFor="let name of available" mat-raised-button (click)="create(name)" matTooltip="{{'vouchers.' + name + '.text' | i18n}}"> @for (name of available; track name) {
<a mat-raised-button (click)="create(name)" matTooltip="{{'vouchers.' + name + '.text' | i18n}}">
{{'vouchers.' + name | i18n}} {{'vouchers.' + name | i18n}}
</a> </a>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#voucher" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#voucher" class="help-button" matTooltip="{{'help-button' | i18n}}"
@@ -15,7 +17,8 @@
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>
<div *ngIf="vouchers && vouchers[0]"> @if (vouchers && vouchers[0]) {
<div>
<h3>{{'vouchers.temp' | i18n}}</h3> <h3>{{'vouchers.temp' | i18n}}</h3>
<p>{{'vouchers.temp.info' | i18n}}</p> <p>{{'vouchers.temp.info' | i18n}}</p>
<table mat-table [dataSource]="voucherSource"> <table mat-table [dataSource]="voucherSource">
@@ -23,13 +26,12 @@
<th mat-header-cell *matHeaderCellDef> {{'voucher.type' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'voucher.type' | i18n}} </th>
<td mat-cell *matCellDef="let voucher">{{voucher.type}}</td> <td mat-cell *matCellDef="let voucher">{{voucher.type}}</td>
</ng-container> </ng-container>
<ng-container matColumnDef="code"> <ng-container matColumnDef="code">
<th mat-header-cell *matHeaderCellDef> {{'voucher.code' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'voucher.code' | i18n}} </th>
<td mat-cell *matCellDef="let voucher">{{voucher.code}}</td> <td mat-cell *matCellDef="let voucher">{{voucher.code}}</td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="voucherColumns"></tr> <tr mat-header-row *matHeaderRowDef="voucherColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: voucherColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: voucherColumns"></tr>
</table> </table>
</div> </div>
}
+20 -18
View File
@@ -71,22 +71,24 @@
{{'borrow.items.emailNotification' | i18n}} {{'borrow.items.emailNotification' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field *ngIf="form.get('emailNotification').value" fxFlex="1 1 0%"> @if (form.get('emailNotification').value) {
<mat-form-field fxFlex="1 1 0%">
<mat-label>{{'borrow.items.email' | i18n}}</mat-label> <mat-label>{{'borrow.items.email' | i18n}}</mat-label>
<input matInput type="email" formControlName="email"> <input matInput type="email" formControlName="email">
<mat-error> <mat-error>
{{'borrow.items.error.email' | i18n}} {{'borrow.items.error.email' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
}
</div> </div>
<ng-container *ngIf="form.get('availability').value == 'MANUAL'"> @if (form.get('availability').value == 'MANUAL') {
<mat-divider></mat-divider><br> <mat-divider></mat-divider><br>
<label>{{'borrow.items.slot.MANUAL' | i18n}}</label> <label>{{'borrow.items.slot.MANUAL' | i18n}}</label>
<a mat-icon-button (click)="addManualSlot('','','')" title="{{'borrow.items.slot.add' | i18n}}"> <a mat-icon-button (click)="addManualSlot('','','')" title="{{'borrow.items.slot.add' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
</a> </a>
<ng-container *ngFor="let slotForm of slots.controls; let i = index"> @for (slotForm of slots.controls; track slotForm; let i = $index) {
<div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid"> <div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid">
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.start' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.start' | i18n}}</mat-label>
@@ -95,7 +97,6 @@
{{'borrow.items.error.slot.start' | i18n}} {{'borrow.items.error.slot.start' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.end' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.end' | i18n}}</mat-label>
<input matInput type="datetime-local" formControlName="end"> <input matInput type="datetime-local" formControlName="end">
@@ -103,7 +104,6 @@
{{'borrow.items.error.slot.end' | i18n}} {{'borrow.items.error.slot.end' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div> <div>
<a mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}"> <a mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
@@ -113,29 +113,30 @@
</a> </a>
</div> </div>
</div> </div>
</ng-container> }
</ng-container> }
<ng-container *ngIf="form.get('availability').value == 'PERIOD'"> @if (form.get('availability').value == 'PERIOD') {
<mat-divider></mat-divider><br> <mat-divider></mat-divider><br>
<label>{{'borrow.items.slot.PERIOD' | i18n}}</label> <label>{{'borrow.items.slot.PERIOD' | i18n}}</label>
<a mat-icon-button (click)="addPeriodSlot('','','','','')" title="{{'borrow.items.slot.add' | i18n}}"> <a mat-icon-button (click)="addPeriodSlot('','','','','')" title="{{'borrow.items.slot.add' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
</a> </a>
<ng-container *ngFor="let slotForm of slots.controls; let i = index"> @for (slotForm of slots.controls; track slotForm; let i = $index) {
<div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="12px grid"> <div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="12px grid">
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.startDay' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.startDay' | i18n}}</mat-label>
<mat-select formControlName="startDay"> <mat-select formControlName="startDay">
<mat-option *ngFor="let day of weekdays" [value]="day"> @for (day of weekdays; track day) {
<mat-option [value]="day">
{{'borrow.items.slot.day.' + day | i18n}} {{'borrow.items.slot.day.' + day | i18n}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
<mat-error> <mat-error>
{{'borrow.items.error.slot.startDay' | i18n}} {{'borrow.items.error.slot.startDay' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.startTime' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.startTime' | i18n}}</mat-label>
<input matInput format="24" [ngxMatTimepicker]="startTimePicker" formControlName="startTime"> <input matInput format="24" [ngxMatTimepicker]="startTimePicker" formControlName="startTime">
@@ -147,19 +148,19 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<ngx-mat-timepicker #startTimePicker></ngx-mat-timepicker> <ngx-mat-timepicker #startTimePicker></ngx-mat-timepicker>
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.endDay' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.endDay' | i18n}}</mat-label>
<mat-select formControlName="endDay"> <mat-select formControlName="endDay">
<mat-option *ngFor="let day of weekdays" [value]="day"> @for (day of weekdays; track day) {
<mat-option [value]="day">
{{'borrow.items.slot.day.' + day | i18n}} {{'borrow.items.slot.day.' + day | i18n}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
<mat-error> <mat-error>
{{'borrow.items.error.slot.endDay' | i18n}} {{'borrow.items.error.slot.endDay' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.endTime' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.endTime' | i18n}}</mat-label>
<input matInput format="24" [ngxMatTimepicker]="endTimePicker" formControlName="endTime"> <input matInput format="24" [ngxMatTimepicker]="endTimePicker" formControlName="endTime">
@@ -171,7 +172,6 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<ngx-mat-timepicker #endTimePicker></ngx-mat-timepicker> <ngx-mat-timepicker #endTimePicker></ngx-mat-timepicker>
<div> <div>
<a mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}"> <a mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
@@ -181,8 +181,8 @@
</a> </a>
</div> </div>
</div> </div>
</ng-container> }
</ng-container> }
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
@@ -193,9 +193,11 @@
<mat-icon>save</mat-icon>{{(create ? 'borrow.items.create' : 'borrow.items.save') | i18n}} <mat-icon>save</mat-icon>{{(create ? 'borrow.items.create' : 'borrow.items.save') | i18n}}
</a> </a>
</div> </div>
<a *ngIf="borrowItemId" mat-button color="warn" (click)="confirmDelete()"> @if (borrowItemId) {
<a mat-button color="warn" (click)="confirmDelete()">
<mat-icon>delete</mat-icon> {{ 'borrow.items.delete' | <mat-icon>delete</mat-icon> {{ 'borrow.items.delete' |
i18n}} i18n}}
</a> </a>
}
</div> </div>
</mat-dialog-actions> </mat-dialog-actions>
+18 -11
View File
@@ -1,5 +1,6 @@
<h3>{{'borrow.items' | i18n}}</h3> <h3>{{'borrow.items' | i18n}}</h3>
<div *ngIf="borrowItems"> @if (borrowItems) {
<div>
<div class="flex justify-between"> <div class="flex justify-between">
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.search' | i18n}}</mat-label> <mat-label>{{'borrow.items.search' | i18n}}</mat-label>
@@ -13,49 +14,55 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'borrow.items.name' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'borrow.items.name' | i18n}} </th>
<td mat-cell *matCellDef="let borrowItem"> {{ borrowItem.name}} </td> <td mat-cell *matCellDef="let borrowItem"> {{ borrowItem.name}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="description"> <ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef> {{'borrow.items.description' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'borrow.items.description' | i18n}} </th>
<td mat-cell *matCellDef="let borrowItem"> {{ borrowItem.description}} </td> <td mat-cell *matCellDef="let borrowItem"> {{ borrowItem.description}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="availability"> <ng-container matColumnDef="availability">
<th mat-header-cell *matHeaderCellDef> {{'borrow.items.availability' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'borrow.items.availability' | i18n}} </th>
<td mat-cell *matCellDef="let borrowItem"> {{ 'borrow.items.availability.' + borrowItem.availability | i18n}} <td mat-cell *matCellDef="let borrowItem"> {{ 'borrow.items.availability.' + borrowItem.availability | i18n}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="url"> <ng-container matColumnDef="url">
<th mat-header-cell *matHeaderCellDef> {{'borrow.items.url' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'borrow.items.url' | i18n}} </th>
<td mat-cell *matCellDef="let borrowItem"> <td mat-cell *matCellDef="let borrowItem">
<a *ngIf="borrowItem.url" mat-button color="accent" href="{{ borrowItem.url }}" target="_blank"> @if (borrowItem.url) {
<a mat-button color="accent" href="{{ borrowItem.url }}" target="_blank">
{{ borrowItem.url }} {{ borrowItem.url }}
<mat-icon style="font-size: 1em;">open_in_new</mat-icon> <mat-icon style="font-size: 1em;">open_in_new</mat-icon>
</a> </a>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.items.actions' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.items.actions' | i18n}} </th>
<td mat-cell *matCellDef="let borrowItem" class="text-right"> <td mat-cell *matCellDef="let borrowItem" class="text-right">
<a mat-icon-button *ngIf="borrowItem.owner == userId" (click)="request(borrowItem)"> @if (borrowItem.owner == userId) {
<a mat-icon-button (click)="request(borrowItem)">
<mat-icon>pending_actions</mat-icon> <mat-icon>pending_actions</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowItem.owner == userId" (click)="edit(borrowItem)"> }
@if (borrowItem.owner == userId) {
<a mat-icon-button (click)="edit(borrowItem)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowItem.owner == userId" (click)="confirmDelete(borrowItem)"> }
@if (borrowItem.owner == userId) {
<a mat-icon-button (click)="confirmDelete(borrowItem)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowItem.owner != userId" (click)="request(borrowItem)"> }
@if (borrowItem.owner != userId) {
<a mat-icon-button (click)="request(borrowItem)">
<mat-icon>pending_actions</mat-icon> <mat-icon>pending_actions</mat-icon>
</a> </a>
}
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="borrowItemColumns"></tr> <tr mat-header-row *matHeaderRowDef="borrowItemColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: borrowItemColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: borrowItemColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="borrowItems.totalElements" [pageSize]="borrowItems.size" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="borrowItems.totalElements" [pageSize]="borrowItems.size"
(page)="updatePages($event)" showFirstLastButtons></mat-paginator> (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
@@ -4,9 +4,11 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.proving.camera' | i18n}}</mat-label> <mat-label>{{'borrow.proving.camera' | i18n}}</mat-label>
<mat-select [formControl]="camera" name="camera"> <mat-select [formControl]="camera" name="camera">
<mat-option *ngFor="let camera of cameras" [value]="camera.id"> @for (camera of cameras; track camera) {
<mat-option [value]="camera.id">
{{camera.label || camera.id}} {{camera.label || camera.id}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@@ -16,7 +18,8 @@
</mat-form-field> </mat-form-field>
</form> </form>
<mat-card class="warn" *ngIf="initialized && noCamera"> @if (initialized && noCamera) {
<mat-card class="warn">
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
<mat-icon>no_photography</mat-icon> <mat-icon>no_photography</mat-icon>
@@ -29,3 +32,4 @@
</p> </p>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
}
@@ -2,9 +2,15 @@
<mat-card [ngClass]="result.success ? 'success' : (result.response.status == 412 ? 'accent' : 'warn')"> <mat-card [ngClass]="result.success ? 'success' : (result.response.status == 412 ? 'accent' : 'warn')">
<mat-card-header> <mat-card-header>
<mat-card-title> <mat-card-title>
<mat-icon *ngIf="!result.success && result.response.status != 412">block</mat-icon> @if (!result.success && result.response.status != 412) {
<mat-icon *ngIf="!result.success && result.response.status == 412">help_outline</mat-icon> <mat-icon>block</mat-icon>
<mat-icon *ngIf="result.success">check</mat-icon> }
@if (!result.success && result.response.status == 412) {
<mat-icon>help_outline</mat-icon>
}
@if (result.success) {
<mat-icon>check</mat-icon>
}
</mat-card-title> </mat-card-title>
<mat-card-subtitle>{{'borrow.proving.' + (result.success ? 'valid' : 'invalid') | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'borrow.proving.' + (result.success ? 'valid' : 'invalid') | i18n}}</mat-card-subtitle>
</mat-card-header> </mat-card-header>
@@ -12,20 +18,29 @@
<p> <p>
{{'borrow.proving.' + (result.success ? 'valid' : 'invalid') + '.text' | i18n}} {{'borrow.proving.' + (result.success ? 'valid' : 'invalid') + '.text' | i18n}}
</p> </p>
<p *ngFor="let error of result.response.error"> @for (error of result.response.error; track error) {
<ng-container [ngSwitch]="error.field"> <p>
<span *ngSwitchCase="'nbf'"> @switch (error.field) {
@case ('nbf') {
<span>
{{'borrow.proving.error.' + error.code | i18n:(error.defaultMessage | datef)}} {{'borrow.proving.error.' + error.code | i18n:(error.defaultMessage | datef)}}
</span> </span>
<span *ngSwitchCase="'exp'"> }
@case ('exp') {
<span>
{{'borrow.proving.error.' + error.code | i18n:(error.defaultMessage | datef)}} {{'borrow.proving.error.' + error.code | i18n:(error.defaultMessage | datef)}}
</span> </span>
<span *ngSwitchDefault> }
@default {
<span>
{{'borrow.proving.error.default.' + error.code | i18n:error.defaultMessage}} {{'borrow.proving.error.default.' + error.code | i18n:error.defaultMessage}}
</span> </span>
</ng-container> }
}
</p> </p>
<table *ngIf="result.success"> }
@if (result.success) {
<table>
<tr> <tr>
<th>{{'borrow.request.user' | i18n}}</th> <th>{{'borrow.request.user' | i18n}}</th>
<td>{{result.response.user}}</td> <td>{{result.response.user}}</td>
@@ -47,6 +62,7 @@
<td>{{result.response.exp | datef}}</td> <td>{{result.response.exp | datef}}</td>
</tr> </tr>
</table> </table>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a mat-raised-button [mat-dialog-close]="false">{{'ok' | i18n}}</a> <a mat-raised-button [mat-dialog-close]="false">{{'ok' | i18n}}</a>
@@ -4,22 +4,25 @@
<h3>{{borrowRequest.borrowItem.name}}</h3> <h3>{{borrowRequest.borrowItem.name}}</h3>
<p>{{borrowRequest.borrowItem.description}}</p> <p>{{borrowRequest.borrowItem.description}}</p>
<a *ngIf="borrowRequest.borrowItem.url" mat-button color="accent" href="{{ borrowRequest.borrowItem.url }}" @if (borrowRequest.borrowItem.url) {
<a mat-button color="accent" href="{{ borrowRequest.borrowItem.url }}"
target="_blank"> target="_blank">
{{ borrowRequest.borrowItem.url }} {{ borrowRequest.borrowItem.url }}
<mat-icon style="font-size: 1em;">open_in_new</mat-icon> <mat-icon style="font-size: 1em;">open_in_new</mat-icon>
</a> </a>
}
<br> <br>
<br> <br>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<ng-container *ngIf="borrowRequest.id"> @if (borrowRequest.id) {
<mat-chip-listbox *ngSwitch="borrowRequest.status"> <mat-chip-listbox >
<mat-chip *ngSwtichCase="'PENDING'" color="primary" selected></mat-chip> @if (borrowRequest.status === 'PENDING') {
<mat-chip color="primary" selected></mat-chip>
}
<mat-chip color="accent" selected>Accent fish</mat-chip> <mat-chip color="accent" selected>Accent fish</mat-chip>
</mat-chip-listbox> </mat-chip-listbox>
}
</ng-container>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
@@ -55,9 +58,11 @@
<mat-icon>save</mat-icon>{{(create ? 'borrow.request.create' : 'borrow.request.save') | i18n}} <mat-icon>save</mat-icon>{{(create ? 'borrow.request.create' : 'borrow.request.save') | i18n}}
</a> </a>
</div> </div>
<a *ngIf="borrowRequest.id" mat-button color="warn" (click)="confirmDelete()"> @if (borrowRequest.id) {
<a mat-button color="warn" (click)="confirmDelete()">
<mat-icon>delete</mat-icon> {{ 'borrow.request.delete' | <mat-icon>delete</mat-icon> {{ 'borrow.request.delete' |
i18n}} i18n}}
</a> </a>
}
</div> </div>
</mat-dialog-actions> </mat-dialog-actions>
@@ -1,5 +1,6 @@
<h3>{{'borrow.requests' | i18n}}</h3> <h3>{{'borrow.requests' | i18n}}</h3>
<div *ngIf="borrowRequests"> @if (borrowRequests) {
<div>
<div class="flex justify-between"> <div class="flex justify-between">
<mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.requests.mine' | i18n}}</mat-slide-toggle> <mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.requests.mine' | i18n}}</mat-slide-toggle>
</div> </div>
@@ -8,43 +9,45 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'borrow.item.name' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'borrow.item.name' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.borrowItem.name}} </td> <td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.borrowItem.name}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="starts"> <ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef> {{'borrow.requests.starts' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'borrow.requests.starts' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.starts | datef}} <td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.starts | datef}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="ends"> <ng-container matColumnDef="ends">
<th mat-header-cell *matHeaderCellDef> {{'borrow.requests.ends' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'borrow.requests.ends' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.ends | datef}} <td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.ends | datef}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="status"> <ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef> {{'borrow.requests.status' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'borrow.requests.status' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest"> {{ 'borrow.requests.status.' + borrowRequest.status | i18n}} <td mat-cell *matCellDef="let borrowRequest"> {{ 'borrow.requests.status.' + borrowRequest.status | i18n}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.requests.actions' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.requests.actions' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest" class="text-right"> <td mat-cell *matCellDef="let borrowRequest" class="text-right">
<a mat-icon-button *ngIf="borrowRequest.user == userId" (click)="edit(borrowRequest)"> @if (borrowRequest.user == userId) {
<a mat-icon-button (click)="edit(borrowRequest)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowRequest.user == userId" (click)="confirmDelete(borrowRequest)"> }
@if (borrowRequest.user == userId) {
<a mat-icon-button (click)="confirmDelete(borrowRequest)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowRequest.item.owner != userId" (click)="updateStatus(borrowRequest)"> }
@if (borrowRequest.item.owner != userId) {
<a mat-icon-button (click)="updateStatus(borrowRequest)">
<mat-icon>pending_actions</mat-icon> <mat-icon>pending_actions</mat-icon>
</a> </a>
}
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="borrowRequestColumns"></tr> <tr mat-header-row *matHeaderRowDef="borrowRequestColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: borrowRequestColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: borrowRequestColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="borrowRequests.totalElements" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="borrowRequests.totalElements"
[pageSize]="borrowRequests.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator> [pageSize]="borrowRequests.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
@@ -4,22 +4,28 @@
<h2>{{'security.2fa.external' | i18n}}<mat-icon style="font-size: 1em;">open_in_new <h2>{{'security.2fa.external' | i18n}}<mat-icon style="font-size: 1em;">open_in_new
</mat-icon> </mat-icon>
</h2> </h2>
<mat-error *ngIf="loginInvalid"> @if (loginInvalid) {
<mat-error>
{{'security.2fa.invalid' | i18n}} {{'security.2fa.invalid' | i18n}}
</mat-error> </mat-error>
}
<input id="provider" name="provider" matInput hidden [value]="selectedProvider.id"> <input id="provider" name="provider" matInput hidden [value]="selectedProvider.id">
<mat-form-field> <mat-form-field>
<mat-label>{{'security.2fa.provider' | i18n}}</mat-label> <mat-label>{{'security.2fa.provider' | i18n}}</mat-label>
<mat-select [(ngModel)]="selectedProvider" [ngModelOptions]="{standalone: true}"> <mat-select [(ngModel)]="selectedProvider" [ngModelOptions]="{standalone: true}">
<mat-option *ngFor="let provider of providers" [value]="provider"> @for (provider of providers; track provider) {
<mat-option [value]="provider">
{{'security.2fa.' + provider.id | i18n}} {{'security.2fa.' + provider.id | i18n}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@if (selectedProvider && selectedProvider.request) {
<a mat-raised-button (click)="request()" <a mat-raised-button (click)="request()"
*ngIf="selectedProvider && selectedProvider.request">{{'security.2fa.' + selectedProvider.id + >{{'security.2fa.' + selectedProvider.id +
'.request' '.request'
| i18n}}</a> | i18n}}</a>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'security.2fa.code' | i18n}}</mat-label> <mat-label>{{'security.2fa.code' | i18n}}</mat-label>
<input id="code" name="code" matInput required matAutofocus> <input id="code" name="code" matInput required matAutofocus>
@@ -27,9 +33,11 @@
{{'security.2fa.missing' | i18n}} {{'security.2fa.missing' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle #toggle *ngIf="keep" [checked]="true" [disabled]="true"> @if (keep) {
<mat-slide-toggle #toggle [checked]="true" [disabled]="true">
{{'security.2fa.keepSession' | i18n}} {{'security.2fa.keepSession' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
}
<input class="hidden" type="checkbox" id="keep" name="keep" [checked]="keep"> <input class="hidden" type="checkbox" id="keep" name="keep" [checked]="keep">
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
@@ -2,9 +2,11 @@
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<h2>{{'security.oidc' | i18n}}</h2> <h2>{{'security.oidc' | i18n}}</h2>
<mat-error *ngIf="loginInvalid"> @if (loginInvalid) {
<mat-error>
{{'security.oidc.login.invalid' | i18n}} {{'security.oidc.login.invalid' | i18n}}
</mat-error> </mat-error>
}
<input id="code" name="code" type="hidden" formControlName="code" required> <input id="code" name="code" type="hidden" formControlName="code" required>
<input id="state" name="state" type="hidden" formControlName="state" required> <input id="state" name="state" type="hidden" formControlName="state" required>
<input id="client_id" name="client_id" type="hidden" formControlName="client_id" required> <input id="client_id" name="client_id" type="hidden" formControlName="client_id" required>
@@ -14,19 +16,21 @@
<input id="nonce" name="nonce" type="hidden" formControlName="nonce"> <input id="nonce" name="nonce" type="hidden" formControlName="nonce">
<input id="prompt" name="prompt" type="hidden" formControlName="prompt"> <input id="prompt" name="prompt" type="hidden" formControlName="prompt">
<input id="login_hint" name="login_hint" type="hidden" formControlName="login_hint"> <input id="login_hint" name="login_hint" type="hidden" formControlName="login_hint">
<ng-container *ngIf="authorize"> @if (authorize) {
<h3>{{'security.oidc.authorize' | i18n}}</h3> <h3>{{'security.oidc.authorize' | i18n}}</h3>
<p>{{'security.oidc.authorize.hint' | i18n}}</p> <p>{{'security.oidc.authorize.hint' | i18n}}</p>
</ng-container> }
<ng-container *ngIf="alias"> @if (alias) {
<h3>{{'security.oidc.alias' | i18n}}</h3> <h3>{{'security.oidc.alias' | i18n}}</h3>
<mat-nav-list> <mat-nav-list>
<mat-list-item (click)="setAlias('')" <mat-list-item (click)="setAlias('')"
[ngClass]="{'active' : !selectedAlias || selectedAlias == ''}">{{auth.name}}</mat-list-item> [ngClass]="{'active' : !selectedAlias || selectedAlias == ''}">{{auth.name}}</mat-list-item>
<mat-list-item *ngFor="let userAlias of aliases" (click)="setAlias(userAlias.alias)" @for (userAlias of aliases; track userAlias) {
<mat-list-item (click)="setAlias(userAlias.alias)"
[ngClass]="{'active' : selectedAlias == userAlias.alias}">{{userAlias.alias}}</mat-list-item> [ngClass]="{'active' : selectedAlias == userAlias.alias}">{{userAlias.alias}}</mat-list-item>
}
</mat-nav-list> </mat-nav-list>
</ng-container> }
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
@@ -4,9 +4,11 @@
<h2>{{'login.external' | i18n}}<mat-icon style="font-size: 1em;">open_in_new <h2>{{'login.external' | i18n}}<mat-icon style="font-size: 1em;">open_in_new
</mat-icon> </mat-icon>
</h2> </h2>
<mat-error *ngIf="loginInvalid"> @if (loginInvalid) {
<mat-error>
{{'login.invalid' | i18n}} {{'login.invalid' | i18n}}
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'username' | i18n}}</mat-label> <mat-label>{{'username' | i18n}}</mat-label>
<input id="username" name="username" matInput required matAutofocus> <input id="username" name="username" matInput required matAutofocus>
+46 -29
View File
@@ -3,7 +3,7 @@
<mat-card-title>{{'invites.register' | i18n}}</mat-card-title> <mat-card-title>{{'invites.register' | i18n}}</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<ng-container *ngIf="invite"> @if (invite) {
<h3> <h3>
<mat-icon inline="true">{{'invites.quota.' + invite.quota + ".icon" | i18n}}</mat-icon> <mat-icon inline="true">{{'invites.quota.' + invite.quota + ".icon" | i18n}}</mat-icon>
{{'invites.quota.' {{'invites.quota.'
@@ -11,21 +11,26 @@
invite.quota invite.quota
| i18n}} | i18n}}
</h3> </h3>
<p>{{'invites.quota.' + invite.quota + <p>{{'invites.quota.' + invite.quota +
".text" | i18n}}</p> ".text" | i18n}}</p>
<p *ngIf="invite.url"> @if (invite.url) {
<p>
<a mat-button href="{{invite.url}}" target="_blank" color="accent">{{'invites.register.url' | <a mat-button href="{{invite.url}}" target="_blank" color="accent">{{'invites.register.url' |
i18n}}</a> i18n}}</a>
</p> </p>
}
<ng-container *ngIf="!success"> @if (!success) {
<blockquote *ngIf="invite.message && auth.principal.userId != invite.owner" class="message"> @if (invite.message && auth.principal.userId != invite.owner) {
<blockquote class="message">
{{invite.message}}</blockquote> {{invite.message}}</blockquote>
<blockquote *ngIf="invite.note && auth.principal.userId != invite.owner" class="note">{{invite.note}} }
@if (invite.note && auth.principal.userId != invite.owner) {
<blockquote class="note">{{invite.note}}
</blockquote> </blockquote>
<form [formGroup]="form" *ngIf="!error"> }
<ng-container *ngIf="!auth.authenticated"> @if (!error) {
<form [formGroup]="form">
@if (!auth.authenticated) {
<mat-form-field> <mat-form-field>
<mat-label>{{'username' | i18n}}</mat-label> <mat-label>{{'username' | i18n}}</mat-label>
<input matInput formControlName="username" matAutofocus tabindex="1"> <input matInput formControlName="username" matAutofocus tabindex="1">
@@ -40,9 +45,11 @@
<mat-label>{{'password' | i18n}}</mat-label> <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password" tabindex="2"> <input matInput type="password" formControlName="password" tabindex="2">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> @for (error of form.get('password').errors | keyvalue; track error) {
<div>
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
</div> </div>
}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
@@ -52,8 +59,8 @@
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</ng-container> }
<ng-container *ngIf="auth.principal.userId == invite.owner"> @if (auth.principal.userId == invite.owner) {
<mat-form-field> <mat-form-field>
<mat-label>{{'invite.message' | i18n}}</mat-label> <mat-label>{{'invite.message' | i18n}}</mat-label>
<textarea matInput formControlName="message"></textarea> <textarea matInput formControlName="message"></textarea>
@@ -68,38 +75,44 @@
{{'invites.error.note' | i18n}} {{'invites.error.note' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</ng-container> }
</form> </form>
</ng-container> }
<ng-container *ngIf="success"> }
@if (success) {
<h2>{{'invites.register.success' | i18n}}</h2> <h2>{{'invites.register.success' | i18n}}</h2>
<p>{{'invites.register.success.text' | i18n}}</p> <p>{{'invites.register.success.text' | i18n}}</p>
</ng-container> }
</ng-container> }
<ng-container *ngIf="error"> @if (error) {
<mat-error> <mat-error>
{{'invites.register.error.' + error | i18n}} {{'invites.register.error.' + error | i18n}}
</mat-error> </mat-error>
</ng-container> }
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<ng-container *ngIf="!success && invite"> @if (!success && invite) {
<a *ngIf="!working && !auth.authenticated && !error" mat-raised-button color="primary" @if (!working && !auth.authenticated && !error) {
<a mat-raised-button color="primary"
[disabled]="form.invalid" (click)="register()" tabindex="4"> [disabled]="form.invalid" (click)="register()" tabindex="4">
{{'invites.register' | i18n}} {{'invites.register' | i18n}}
</a> </a>
}
<a *ngIf="auth.principal.userId == invite.owner && !error" mat-raised-button color="primary" @if (auth.principal.userId == invite.owner && !error) {
<a mat-raised-button color="primary"
(click)="save()" tabindex="4"> (click)="save()" tabindex="4">
{{'invites.edit.save' | i18n}} {{'invites.edit.save' | i18n}}
</a> </a>
</ng-container> }
<ng-container *ngIf="success"> }
@if (success) {
<a routerLink="/login" mat-raised-button color="primary"> <a routerLink="/login" mat-raised-button color="primary">
{{'invites.register.login' | i18n}} {{'invites.register.login' | i18n}}
</a> </a>
</ng-container> }
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> @if (working) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#invite" class="help-button" <a href="https://wiki.bstly.de/services/webstly#invite" class="help-button"
@@ -109,11 +122,15 @@
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>
<div *ngIf="!success && permissions && permissions[0] && !error"> @if (!success && permissions && permissions[0] && !error) {
<div>
<h3>{{'permissions' | i18n}}</h3> <h3>{{'permissions' | i18n}}</h3>
<app-permissions [permissions]="permissions"></app-permissions> <app-permissions [permissions]="permissions"></app-permissions>
</div> </div>
<div *ngIf="!success && quotas && quotas[0] && !error"> }
@if (!success && quotas && quotas[0] && !error) {
<div>
<h3>{{'quotas' | i18n}}</h3> <h3>{{'quotas' | i18n}}</h3>
<app-quotas [quotas]="quotas"></app-quotas> <app-quotas [quotas]="quotas"></app-quotas>
</div> </div>
}
@@ -1,4 +1,4 @@
@import '../../../../variables.scss'; @use '../../../../variables.scss' as vars;
mat-form-field { mat-form-field {
@@ -14,10 +14,10 @@ blockquote {
padding: 5px; padding: 5px;
&.message { &.message {
border-left: 2px solid $accent; border-left: 2px solid vars.$accent;
} }
&.note { &.note {
border-left: 2px solid $primary; border-left: 2px solid vars.$primary;
} }
} }
+3 -1
View File
@@ -1,9 +1,11 @@
<h2 mat-dialog-title>{{'invites.edit' | i18n}}</h2> <h2 mat-dialog-title>{{'invites.edit' | i18n}}</h2>
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-error *ngIf="error"> @if (error) {
<mat-error>
{{'invites.register.error.' + error | i18n}} {{'invites.register.error.' + error | i18n}}
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'invite.message' | i18n}}</mat-label> <mat-label>{{'invite.message' | i18n}}</mat-label>
<textarea matInput formControlName="message"></textarea> <textarea matInput formControlName="message"></textarea>
+63 -32
View File
@@ -1,6 +1,7 @@
<h3>{{'invites' | i18n}}</h3> <h3>{{'invites' | i18n}}</h3>
<div *ngIf="invites"> @if (invites) {
<div>
<div class="flex"> <div class="flex">
<mat-form-field> <mat-form-field>
<mat-label>{{'invites.search' | i18n}}</mat-label> <mat-label>{{'invites.search' | i18n}}</mat-label>
@@ -16,58 +17,69 @@
<mat-option value="false"> <mat-option value="false">
<mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} <mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
</mat-option> </mat-option>
<mat-select-trigger [ngSwitch]="redeemedFormControl.value"> <mat-select-trigger>
<span *ngSwitchCase="'true'"> @switch (redeemedFormControl.value) {
@case ('true') {
<span>
<mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}} <mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}}
</span> </span>
<span *ngSwitchCase="'false'"> }
@case ('false') {
<span>
<mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} <mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
</span> </span>
<span *ngSwitchDefault>{{'invites.redeemed.filter.none' | i18n}}</span> }
@default {
<span>{{'invites.redeemed.filter.none' | i18n}}</span>
}
}
</mat-select-trigger> </mat-select-trigger>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<table mat-table matSort [dataSource]="invites.content"> <table mat-table matSort [dataSource]="invites.content">
<ng-container matColumnDef="starts"> <ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef> {{'invite.starts' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.starts' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> {{ invite.starts | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let invite"> {{ invite.starts | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="expires"> <ng-container matColumnDef="expires">
<th mat-header-cell *matHeaderCellDef> {{'invite.expires' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.expires' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> {{ invite.expires | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let invite"> {{ invite.expires | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="link"> <ng-container matColumnDef="link">
<th mat-header-cell *matHeaderCellDef> {{'invite.link' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.link' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <td mat-cell *matCellDef="let invite">
<a href="{{ invite.codeLink }}" target="_blank" mat-button color="accent">{{invite.code}}</a> <a href="{{ invite.codeLink }}" target="_blank" mat-button color="accent">{{invite.code}}</a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="note"> <ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <span *ngIf="invite.note">{{ invite.note}}</span> <i <td mat-cell *matCellDef="let invite"> @if (invite.note) {
*ngIf="!invite.note">{{ 'invite.noNote' | i18n}}</i> <span>{{ invite.note}}</span>
} @if (!invite.note) {
<i
>{{ 'invite.noNote' | i18n}}</i>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="message"> <ng-container matColumnDef="message">
<th mat-header-cell *matHeaderCellDef> {{'invite.message' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.message' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <i *ngIf="!invite.message">{{ 'invite.noMessage' | i18n}}</i> <td mat-cell *matCellDef="let invite"> @if (!invite.message) {
<mat-icon matTooltip="{{ invite.message}}" inline="true" *ngIf="invite.message">article</mat-icon> <i>{{ 'invite.noMessage' | i18n}}</i>
}
@if (invite.message) {
<mat-icon matTooltip="{{ invite.message}}" inline="true">article</mat-icon>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="redeemed"> <ng-container matColumnDef="redeemed">
<th mat-header-cell *matHeaderCellDef> {{'invite.redeemed' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.redeemed' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <td mat-cell *matCellDef="let invite">
<mat-icon *ngIf="invite.redeemed">how_to_reg</mat-icon> @if (invite.redeemed) {
<mat-icon>how_to_reg</mat-icon>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> {{'invite.actions' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.actions' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <td mat-cell *matCellDef="let invite">
@@ -76,32 +88,38 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="inviteColumns"></tr> <tr mat-header-row *matHeaderRowDef="inviteColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: inviteColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: inviteColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="invites.totalElements" [pageSize]="invites.size" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="invites.totalElements" [pageSize]="invites.size"
(page)="updatePages($event)" showFirstLastButtons></mat-paginator> (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'invites.info' | i18n}}</p> <p>{{'invites.info' | i18n}}</p>
<p *ngIf="!inviteQuota">{{'invites.noQuota' | i18n}}</p> @if (!inviteQuota) {
<div *ngIf="inviteQuota"> <p>{{'invites.noQuota' | i18n}}</p>
}
@if (inviteQuota) {
<div>
<p>{{'invites.left' | i18n:inviteQuota}}</p> <p>{{'invites.left' | i18n:inviteQuota}}</p>
</div> </div>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a *ngIf="inviteQuota && !working" mat-raised-button color="primary" (click)="create()"> @if (inviteQuota && !working) {
<a mat-raised-button color="primary" (click)="create()">
{{'invite.create' | i18n}} {{'invite.create' | i18n}}
</a> </a>
}
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
<div *ngIf="others && others.content"> @if (others && others.content) {
<div>
<h4>{{'invites.others' | i18n}}</h4> <h4>{{'invites.others' | i18n}}</h4>
<div class="flex"> <div class="flex">
<mat-form-field> <mat-form-field>
<mat-label>{{'invites.search' | i18n}}</mat-label> <mat-label>{{'invites.search' | i18n}}</mat-label>
@@ -117,14 +135,22 @@
<mat-option value="false"> <mat-option value="false">
<mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} <mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
</mat-option> </mat-option>
<mat-select-trigger [ngSwitch]="redeemedOthersFormControl.value"> <mat-select-trigger>
<span *ngSwitchCase="'true'"> @switch (redeemedOthersFormControl.value) {
@case ('true') {
<span>
<mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}} <mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}}
</span> </span>
<span *ngSwitchCase="'false'"> }
@case ('false') {
<span>
<mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} <mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
</span> </span>
<span *ngSwitchDefault>{{'invites.redeemed.filter.none' | i18n}}</span> }
@default {
<span>{{'invites.redeemed.filter.none' | i18n}}</span>
}
}
</mat-select-trigger> </mat-select-trigger>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@@ -132,20 +158,25 @@
<table mat-table matSort [dataSource]="others.content"> <table mat-table matSort [dataSource]="others.content">
<ng-container matColumnDef="note"> <ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <span *ngIf="invite.note">{{ invite.note}}</span> <i <td mat-cell *matCellDef="let invite"> @if (invite.note) {
*ngIf="!invite.note">{{ 'invite.noNote' | i18n}}</i> </td> <span>{{ invite.note}}</span>
} @if (!invite.note) {
<i
>{{ 'invite.noNote' | i18n}}</i>
} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="redeemed"> <ng-container matColumnDef="redeemed">
<th mat-header-cell *matHeaderCellDef> {{'invite.redeemed' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.redeemed' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <td mat-cell *matCellDef="let invite">
<mat-icon *ngIf="invite.redeemed">how_to_reg</mat-icon> @if (invite.redeemed) {
<mat-icon>how_to_reg</mat-icon>
}
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="otherColumns"></tr> <tr mat-header-row *matHeaderRowDef="otherColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: otherColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: otherColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="others.totalElements" [pageSize]="others.size" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="others.totalElements" [pageSize]="others.size"
(page)="updateOthers($event)" showFirstLastButtons></mat-paginator> (page)="updateOthers($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
+30 -28
View File
@@ -1,8 +1,8 @@
<h3>{{'jitsi.rooms' | i18n}}</h3> <h3>{{'jitsi.rooms' | i18n}}</h3>
<div *ngIf="jitsiRooms"> @if (jitsiRooms) {
<div>
<table mat-table matSort [dataSource]="jitsiRooms.content" (matSortChange)="updateSort($event)"> <table mat-table matSort [dataSource]="jitsiRooms.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="share"> <ng-container matColumnDef="share">
<th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom"> <td mat-cell *matCellDef="let jitsiRoom">
@@ -11,62 +11,62 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="room"> <ng-container matColumnDef="room">
<th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom"> <td mat-cell *matCellDef="let jitsiRoom">
<div class="flex align-center"> <div class="flex align-center">
<a *ngIf="open(jitsiRoom, false)" mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank"> @if (open(jitsiRoom, false)) {
<a mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank">
{{ jitsiRoom.room }} {{ jitsiRoom.room }}
<mat-icon style="font-size: 1em;">open_in_new</mat-icon> <mat-icon style="font-size: 1em;">open_in_new</mat-icon>
</a> </a>
}
<a *ngIf="!open(jitsiRoom, false)" mat-button matTooltip="{{'jitsi.rooms.notStarted' | i18n}}" @if (!open(jitsiRoom, false)) {
<a mat-button matTooltip="{{'jitsi.rooms.notStarted' | i18n}}"
(click)="copyRoomUrlToClipboard(jitsiRoom)"> (click)="copyRoomUrlToClipboard(jitsiRoom)">
{{ jitsiRoom.room }} {{ jitsiRoom.room }}
</a> </a>
}
<a *ngIf="!jitsiRoom.code && shortenedUrlQuota" mat-icon-button (click)="createShortenedUrl(jitsiRoom)" inline @if (!jitsiRoom.code && shortenedUrlQuota) {
<a mat-icon-button (click)="createShortenedUrl(jitsiRoom)" inline
matTooltip="{{'urlshortener.create' | i18n}}"> matTooltip="{{'urlshortener.create' | i18n}}">
<mat-icon>add_link</mat-icon> <mat-icon>add_link</mat-icon>
</a> </a>
}
</div> </div>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="starts"> <ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'jitsi.rooms.starts' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'jitsi.rooms.starts' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.starts | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.starts | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="moderationStarts"> <ng-container matColumnDef="moderationStarts">
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationStarts"> {{'jitsi.rooms.moderationStarts' | <th mat-header-cell *matHeaderCellDef mat-sort-header="moderationStarts"> {{'jitsi.rooms.moderationStarts' |
i18n}} </th> i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.moderationStarts | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.moderationStarts | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="expires"> <ng-container matColumnDef="expires">
<th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'jitsi.rooms.expires' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'jitsi.rooms.expires' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom"> {{ jitsiRoom.expires | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let jitsiRoom"> {{ jitsiRoom.expires | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="moderationUrl"> <ng-container matColumnDef="moderationUrl">
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationUrl"> {{'jitsi.rooms.moderationUrl' | i18n}} <th mat-header-cell *matHeaderCellDef mat-sort-header="moderationUrl"> {{'jitsi.rooms.moderationUrl' | i18n}}
</th> </th>
<td mat-cell *matCellDef="let jitsiRoom"> <td mat-cell *matCellDef="let jitsiRoom">
<a *ngIf="open(jitsiRoom, true)" mat-button color="primary" href="{{ jitsiRoom.moderationUrl}}" target="_blank"> @if (open(jitsiRoom, true)) {
<a mat-button color="primary" href="{{ jitsiRoom.moderationUrl}}" target="_blank">
<span class="url">{{ jitsiRoom.moderationUrl }}</span> <span class="url">{{ jitsiRoom.moderationUrl }}</span>
<mat-icon style="font-size: 1em;">open_in_new</mat-icon> <mat-icon style="font-size: 1em;">open_in_new</mat-icon>
</a> </a>
}
<a *ngIf="!open(jitsiRoom, true)" mat-button color="primary" disabled @if (!open(jitsiRoom, true)) {
<a mat-button color="primary" disabled
matTooltip="{{'jitsi.rooms.notStarted' | i18n}}" matTooltipPosition="above"> matTooltip="{{'jitsi.rooms.notStarted' | i18n}}" matTooltipPosition="above">
<span class="url">{{ jitsiRoom.moderationUrl }}</span> <span class="url">{{ jitsiRoom.moderationUrl }}</span>
</a> </a>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="edit"> <ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'jitsi.rooms.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'jitsi.rooms.edit' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right"> <td mat-cell *matCellDef="let jitsiRoom" class="text-right">
@@ -75,7 +75,6 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right"> <td mat-cell *matCellDef="let jitsiRoom" class="text-right">
@@ -84,21 +83,23 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="jitsiRoomsColumns"></tr> <tr mat-header-row *matHeaderRowDef="jitsiRoomsColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: jitsiRoomsColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: jitsiRoomsColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="jitsiRooms.totalElements" [pageSize]="jitsiRooms.size" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="jitsiRooms.totalElements" [pageSize]="jitsiRooms.size"
(page)="updatePages($event)" showFirstLastButtons></mat-paginator> (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm"> <form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'jitsi.rooms.info' | i18n}}</p> <p>{{'jitsi.rooms.info' | i18n}}</p>
<p *ngIf="!jitsiRoomsQuota">{{'jitsi.rooms.noQuota' | i18n}}</p> @if (!jitsiRoomsQuota) {
<div *ngIf="jitsiRoomsQuota"> <p>{{'jitsi.rooms.noQuota' | i18n}}</p>
}
@if (jitsiRoomsQuota) {
<div>
<p>{{'jitsi.rooms.left' | i18n:jitsiRoomsQuota}}</p> <p>{{'jitsi.rooms.left' | i18n:jitsiRoomsQuota}}</p>
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.room' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.room' | i18n}}</mat-label>
@@ -107,7 +108,6 @@
{{'jitsi.rooms.error.room' | i18n}} {{'jitsi.rooms.error.room' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="jitsiRoom.starts" formControlName="starts"> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.starts" formControlName="starts">
@@ -115,15 +115,15 @@
{{'jitsi.rooms.error.starts' | i18n}} {{'jitsi.rooms.error.starts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
@if (jitsiRoom.starts) {
<mat-form-field *ngIf="jitsiRoom.starts"> <mat-form-field>
<mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts"> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts">
<mat-error> <mat-error>
{{'jitsi.rooms.error.moderationStarts' | i18n}} {{'jitsi.rooms.error.moderationStarts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="jitsiRoom.expires" formControlName="expires"> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.expires" formControlName="expires">
@@ -131,14 +131,16 @@
{{'jitsi.rooms.error.expires' | i18n}} {{'jitsi.rooms.error.expires' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="jitsiRoomsQuota && !working" mat-raised-button color="primary" @if (jitsiRoomsQuota && !working) {
<button type="submit" mat-raised-button color="primary"
[disabled]="form.invalid"> [disabled]="form.invalid">
{{'jitsi.rooms.create' | i18n}} {{'jitsi.rooms.create' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/jitsi#rooms" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/jitsi#rooms" class="help-button" matTooltip="{{'help-button' | i18n}}"
+3 -1
View File
@@ -19,13 +19,15 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="jitsiRoom.starts"> @if (jitsiRoom.starts) {
<mat-form-field>
<mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts"> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts">
<mat-error> <mat-error>
{{'jitsi.rooms.error.moderationStarts' | i18n}} {{'jitsi.rooms.error.moderationStarts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label>
+64 -30
View File
@@ -1,20 +1,29 @@
<div *ngIf="active"> @if (active) {
<div>
<form (submit)="search()"> <form (submit)="search()">
<mat-card> <mat-card>
<mat-card-header> <mat-card-header>
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title> <mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
</mat-card-header> </mat-card-header>
<ng-container *ngIf="!checking"> @if (!checking) {
<mat-card-title-group *ngIf="currentTrack"> @if (currentTrack) {
<img *ngIf="getImageUrl(currentTrack)" mat-card-sm-image [src]="getImageUrl(currentTrack)"> <mat-card-title-group>
@if (getImageUrl(currentTrack)) {
<img mat-card-sm-image [src]="getImageUrl(currentTrack)">
}
<mat-card-title><small>{{'jukebox.current' | i18n}}</small></mat-card-title> <mat-card-title><small>{{'jukebox.current' | i18n}}</small></mat-card-title>
<mat-card-title>{{currentTrack.name}}</mat-card-title> <mat-card-title>{{currentTrack.name}}</mat-card-title>
<mat-card-subtitle> <mat-card-subtitle>
<span *ngFor="let artist of currentTrack.artists; let i = index"> @for (artist of currentTrack.artists; track artist; let i = $index) {
{{artist.name}}<span *ngIf="(i+1) < currentTrack.artists.length">, </span> <span>
{{artist.name}}@if ((i+1) < currentTrack.artists.length) {
<span>, </span>
}
</span> </span>
}
</mat-card-subtitle> </mat-card-subtitle>
</mat-card-title-group> </mat-card-title-group>
}
<mat-card-content> <mat-card-content>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<br /> <br />
@@ -35,64 +44,86 @@
<mat-icon>refresh</mat-icon> <mat-icon>refresh</mat-icon>
</a> </a>
</mat-card-footer> </mat-card-footer>
</ng-container> }
@if (checking) {
<ng-container *ngIf="checking">
<mat-card-actions> <mat-card-actions>
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</mat-card-actions> </mat-card-actions>
</ng-container> }
</mat-card> </mat-card>
</form> </form>
@if (timeout) {
<p *ngIf="timeout">{{'jukebox.timeout' | i18n:timeout}}</p> <p>{{'jukebox.timeout' | i18n:timeout}}</p>
}
<div *ngIf="searchResult && searchResult.items"> @if (searchResult && searchResult.items) {
<mat-card class="track" *ngFor="let track of searchResult.items; let i = index" (click)="queue(track)"> <div>
@for (track of searchResult.items; track track; let i = $index) {
<mat-card class="track" (click)="queue(track)">
<mat-card-title-group> <mat-card-title-group>
<img *ngIf="getImageUrl(track)" mat-card-sm-image [src]="getImageUrl(track)"> @if (getImageUrl(track)) {
<img mat-card-sm-image [src]="getImageUrl(track)">
}
<mat-card-title>{{track.name}}</mat-card-title> <mat-card-title>{{track.name}}</mat-card-title>
<mat-card-subtitle> <mat-card-subtitle>
<span *ngFor="let artist of track.artists; let i = index"> @for (artist of track.artists; track artist; let i = $index) {
{{artist.name}}<span *ngIf="(i+1) < track.artists.length">, </span> <span>
{{artist.name}}@if ((i+1) < track.artists.length) {
<span>, </span>
}
</span> </span>
}
</mat-card-subtitle> </mat-card-subtitle>
</mat-card-title-group> </mat-card-title-group>
</mat-card> </mat-card>
}
<mat-card *ngIf="searchResult && ((searchResult.offset + searchResult.limit) < searchResult.total)"> @if (searchResult && ((searchResult.offset + searchResult.limit) < searchResult.total)) {
<mat-card>
<mat-card-actions> <mat-card-actions>
<a mat-button (click)="searchMore()"> {{'jukebox.search.more' | i18n}}</a> <a mat-button (click)="searchMore()"> {{'jukebox.search.more' | i18n}}</a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
}
</div> </div>
}
</div> </div>
}
<mat-card class="accent" *ngIf="wait"> @if (wait) {
<mat-card class="accent">
<mat-card-header> <mat-card-header>
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title> <mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
<mat-card-subtitle>{{'jukebox.wait' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'jukebox.wait' | i18n}}</mat-card-subtitle>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<mat-card-title-group *ngIf="currentTrack"> @if (currentTrack) {
<img *ngIf="getImageUrl(currentTrack)" mat-card-sm-image [src]="getImageUrl(currentTrack)"> <mat-card-title-group>
@if (getImageUrl(currentTrack)) {
<img mat-card-sm-image [src]="getImageUrl(currentTrack)">
}
<mat-card-title><small>{{'jukebox.current' | i18n}}</small></mat-card-title> <mat-card-title><small>{{'jukebox.current' | i18n}}</small></mat-card-title>
<mat-card-title>{{currentTrack.name}}</mat-card-title> <mat-card-title>{{currentTrack.name}}</mat-card-title>
<mat-card-subtitle> <mat-card-subtitle>
<span *ngFor="let artist of currentTrack.artists; let i = index"> @for (artist of currentTrack.artists; track artist; let i = $index) {
{{artist.name}}<span *ngIf="(i+1) < currentTrack.artists.length">, </span> <span>
{{artist.name}}@if ((i+1) < currentTrack.artists.length) {
<span>, </span>
}
</span> </span>
}
</mat-card-subtitle> </mat-card-subtitle>
</mat-card-title-group> </mat-card-title-group>
<p *ngIf="timeout">{{'jukebox.timeout' | i18n:timeout}}</p> }
@if (timeout) {
<p>{{'jukebox.timeout' | i18n:timeout}}</p>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
}
<mat-card class="warn" *ngIf="unavailable"> @if (unavailable) {
<mat-card class="warn">
<mat-card-header> <mat-card-header>
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title> <mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
<mat-card-subtitle>{{'jukebox.unavailable' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'jukebox.unavailable' | i18n}}</mat-card-subtitle>
@@ -103,8 +134,10 @@
<mat-card-actions> <mat-card-actions>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
}
<mat-card *ngIf="forbidden"> @if (forbidden) {
<mat-card>
<mat-card-header> <mat-card-header>
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title> <mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
<mat-card-subtitle>{{'jukebox.forbidden' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'jukebox.forbidden' | i18n}}</mat-card-subtitle>
@@ -115,3 +148,4 @@
<mat-card-actions> <mat-card-actions>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
}
@@ -2,9 +2,11 @@
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<h2>{{'security.2fa.totp' | i18n}}</h2> <h2>{{'security.2fa.totp' | i18n}}</h2>
<mat-error *ngIf="loginInvalid"> @if (loginInvalid) {
<mat-error>
{{'security.2fa.totp.invalid' | i18n}} {{'security.2fa.totp.invalid' | i18n}}
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'security.2fa.totp.code' | i18n}}</mat-label> <mat-label>{{'security.2fa.totp.code' | i18n}}</mat-label>
<input id="code" name="code" matInput formControlName="code" <input id="code" name="code" matInput formControlName="code"
+3 -1
View File
@@ -2,9 +2,11 @@
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<h2>{{'login' | i18n}}</h2> <h2>{{'login' | i18n}}</h2>
<mat-error *ngIf="loginInvalid"> @if (loginInvalid) {
<mat-error>
{{'login.invalid' | i18n}} {{'login.invalid' | i18n}}
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'username' | i18n}}</mat-label> <mat-label>{{'username' | i18n}}</mat-label>
<input id="username" name="username" matInput <input id="username" name="username" matInput
@@ -29,8 +29,11 @@
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'minetest.accounts.info' | i18n}}</p> <p>{{'minetest.accounts.info' | i18n}}</p>
<p *ngIf="!minetestAccountsQuota">{{'minetest.accounts.noQuota' | i18n}}</p> @if (!minetestAccountsQuota) {
<div *ngIf="minetestAccountsQuota"> <p>{{'minetest.accounts.noQuota' | i18n}}</p>
}
@if (minetestAccountsQuota) {
<div>
<p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p> <p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p>
<mat-form-field> <mat-form-field>
<mat-label>{{'minetest.accounts.name' | i18n}}</mat-label> <mat-label>{{'minetest.accounts.name' | i18n}}</mat-label>
@@ -41,11 +44,14 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="minetestAccountsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid"> @if (minetestAccountsQuota && !working) {
<button type="submit" mat-raised-button color="primary" [disabled]="form.invalid">
{{'minetest.accounts.create' | i18n}} {{'minetest.accounts.create' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/minetest" class="help-button" <a href="https://wiki.bstly.de/services/minetest" class="help-button"
+14 -6
View File
@@ -1,17 +1,25 @@
<h3>{{'partey.tags' | i18n}}</h3> <h3>{{'partey.tags' | i18n}}</h3>
<p *ngIf="!tags || tags.length == 0">{{'partey.tags.none' | i18n}}</p> @if (!tags || tags.length == 0) {
<p>{{'partey.tags.none' | i18n}}</p>
}
<mat-chip-listbox> <mat-chip-listbox>
<ng-container *ngFor="let tag of tags"> @for (tag of tags; track tag) {
<mat-chip *ngIf="activeTag(tag)" color="accent" selected matTooltip="{{ 'partey.tag.' + tag.tag + '.hint' | i18nEmpty}}"> @if (activeTag(tag)) {
<mat-chip color="accent" selected matTooltip="{{ 'partey.tag.' + tag.tag + '.hint' | i18nEmpty}}">
{{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip> {{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip>
<mat-chip *ngIf="upcomingTag(tag)" disabled matTooltip="{{'partey.tags.upcoming' | i18n:(tag.starts | datef)}}" }
@if (upcomingTag(tag)) {
<mat-chip disabled matTooltip="{{'partey.tags.upcoming' | i18n:(tag.starts | datef)}}"
selected>{{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip> selected>{{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip>
<mat-chip *ngIf="expiringTag(tag)" color="primary" }
@if (expiringTag(tag)) {
<mat-chip color="primary"
matTooltip="{{'partey.tags.expires' | i18n:(tag.expires | datef)}}" selected> matTooltip="{{'partey.tags.expires' | i18n:(tag.expires | datef)}}" selected>
{{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip> {{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip>
</ng-container> }
}
</mat-chip-listbox> </mat-chip-listbox>
@@ -4,20 +4,28 @@
timeslot.type) | i18n}} timeslot.type) | i18n}}
</h1> </h1>
<mat-dialog-content> <mat-dialog-content>
<h3 *ngIf="timeslot.id">{{'partey.timeslots.type.' + timeslot.type | i18n}}</h3> @if (timeslot.id) {
<h3>{{'partey.timeslots.type.' + timeslot.type | i18n}}</h3>
}
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field *ngIf="timeslot.type == 'VIDEO' || timeslot.type == 'AUDIO'"> @if (timeslot.type == 'VIDEO' || timeslot.type == 'AUDIO') {
<mat-form-field>
<mat-label>{{'partey.timeslots.share.placeholder' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.share.placeholder' | i18n}}</mat-label>
<input matInput [(ngModel)]="timeslot.share" formControlName="share"> <input matInput [(ngModel)]="timeslot.share" formControlName="share">
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="timeslot.type == 'VIDEO_STREAM'"> }
@if (timeslot.type == 'VIDEO_STREAM') {
<mat-form-field>
<mat-label>{{'partey.timeslots.stream' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.stream' | i18n}}</mat-label>
<input matInput [(ngModel)]="timeslot.stream" formControlName="stream"> <input matInput [(ngModel)]="timeslot.stream" formControlName="stream">
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="timeslot.type == 'AUDIO_STREAM' && timeslot.secret"> }
@if (timeslot.type == 'AUDIO_STREAM' && timeslot.secret) {
<mat-form-field>
<mat-label>{{'partey.timeslots.secret' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.secret' | i18n}}</mat-label>
<textarea [mat-autosize] matInput [value]="timeslot.secret" readonly></textarea> <textarea [mat-autosize] matInput [value]="timeslot.secret" readonly></textarea>
</mat-form-field> </mat-form-field>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'partey.timeslots.starts' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.starts' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="timeslot.starts" formControlName="starts"> <input matInput type="datetime-local" [(ngModel)]="timeslot.starts" formControlName="starts">
@@ -1,6 +1,7 @@
<h3>{{'partey.timeslots' | i18n}}</h3> <h3>{{'partey.timeslots' | i18n}}</h3>
<div *ngIf="timeslots"> @if (timeslots) {
<div>
<div class="flex"> <div class="flex">
<mat-form-field> <mat-form-field>
<mat-label>{{'partey.timeslots.filter.search' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.filter.search' | i18n}}</mat-label>
@@ -18,13 +19,17 @@
<mat-label>{{'partey.timeslots.filter.type' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.filter.type' | i18n}}</mat-label>
<mat-select [formControl]="typeFormControl"> <mat-select [formControl]="typeFormControl">
<mat-option value="">{{'partey.timeslots.filter.type.all' | i18n}}</mat-option> <mat-option value="">{{'partey.timeslots.filter.type.all' | i18n}}</mat-option>
<mat-option *ngFor="let type of types" [value]="type"> @for (type of types; track type) {
<mat-option [value]="type">
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
{{'partey.timeslots.filter.type.' + type | i18n}} {{'partey.timeslots.filter.type.' + type | i18n}}
</mat-option> </mat-option>
}
<mat-select-trigger> <mat-select-trigger>
<mat-icon inline="true" *ngIf="typeFormControl.value ">{{'partey.timeslots.type.' + typeFormControl.value + @if (typeFormControl.value ) {
'.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ? <mat-icon inline="true">{{'partey.timeslots.type.' + typeFormControl.value +
'.icon' | i18n}}</mat-icon>
} {{'partey.timeslots.filter.type.' + (typeFormControl.value ?
typeFormControl.value typeFormControl.value
: 'all') | i18n}} : 'all') | i18n}}
</mat-select-trigger> </mat-select-trigger>
@@ -40,23 +45,19 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot">{{ timeslot.starts | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let timeslot">{{ timeslot.starts | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="ends"> <ng-container matColumnDef="ends">
<th mat-header-cell *matHeaderCellDef mat-sort-header="ends"> {{'partey.timeslots.ends' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="ends"> {{'partey.timeslots.ends' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.ends | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let timeslot"> {{ timeslot.ends | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="title"> <ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header="title"> {{'partey.timeslots.title' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="title"> {{'partey.timeslots.title' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.title }} </td> <td mat-cell *matCellDef="let timeslot"> {{ timeslot.title }} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="description"> <ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef mat-sort-header="description"> {{'partey.timeslots.description' | i18n}} <th mat-header-cell *matHeaderCellDef mat-sort-header="description"> {{'partey.timeslots.description' | i18n}}
</th> </th>
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.description }} </td> <td mat-cell *matCellDef="let timeslot"> {{ timeslot.description }} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="type"> <ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef mat-sort-header="type"> {{'partey.timeslots.type' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="type"> {{'partey.timeslots.type' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot"> <td mat-cell *matCellDef="let timeslot">
@@ -64,65 +65,78 @@
{{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon> {{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="share"> <ng-container matColumnDef="share">
<th mat-header-cell *matHeaderCellDef mat-sort-header="share"> {{'partey.timeslots.share' | i18n}} <th mat-header-cell *matHeaderCellDef mat-sort-header="share"> {{'partey.timeslots.share' | i18n}}
</th> </th>
<td mat-cell *matCellDef="let timeslot"> <td mat-cell *matCellDef="let timeslot">
<div *ngIf="timeslot.owner == userId"> @if (timeslot.owner == userId) {
<div>
<span> {{ timeslot.share }} </span> <span> {{ timeslot.share }} </span>
</div> </div>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="stream"> <ng-container matColumnDef="stream">
<th mat-header-cell *matHeaderCellDef mat-sort-header="stream"> {{'partey.timeslots.stream' | i18n}} <th mat-header-cell *matHeaderCellDef mat-sort-header="stream"> {{'partey.timeslots.stream' | i18n}}
</th> </th>
<td mat-cell *matCellDef="let timeslot"> <td mat-cell *matCellDef="let timeslot">
<div *ngIf="timeslot.owner == userId"> @if (timeslot.owner == userId) {
<a *ngIf="timeslot.type == 'AUDIO_STREAM'" mat-raised-button <div>
@if (timeslot.type == 'AUDIO_STREAM') {
<a mat-raised-button
(click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</a> (click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</a>
<span *ngIf="timeslot.type == 'VIDEO_STREAM'"> {{ timeslot.stream }} </span> }
@if (timeslot.type == 'VIDEO_STREAM') {
<span> {{ timeslot.stream }} </span>
}
</div> </div>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="edit"> <ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right"> <td mat-cell *matCellDef="let timeslot" class="text-right">
<a *ngIf="timeslot.owner == userId" mat-icon-button (click)="openEdit(timeslot)"> @if (timeslot.owner == userId) {
<a mat-icon-button (click)="openEdit(timeslot)">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right"> <td mat-cell *matCellDef="let timeslot" class="text-right">
<a *ngIf="timeslot.owner == userId" mat-icon-button (click)="confirmDelete(timeslot)"> @if (timeslot.owner == userId) {
<a mat-icon-button (click)="confirmDelete(timeslot)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
}
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="timeslotsColumns"></tr> <tr mat-header-row *matHeaderRowDef="timeslotsColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: timeslotsColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: timeslotsColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="timeslots.totalElements" [pageSize]="timeslots.size" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="timeslots.totalElements" [pageSize]="timeslots.size"
(page)="updatePages($event)" showFirstLastButtons></mat-paginator> (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'partey.timeslots.info' | i18n}}</p> <p>{{'partey.timeslots.info' | i18n}}</p>
<p *ngIf="!timeslotsQuota">{{'partey.timeslots.noQuota' | i18n}}</p> @if (!timeslotsQuota) {
<div *ngIf="timeslotsQuota"> <p>{{'partey.timeslots.noQuota' | i18n}}</p>
}
@if (timeslotsQuota) {
<div>
<p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p> <p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p>
</div> </div>
}
</mat-card-content> </mat-card-content>
<mat-card-actions *ngIf="timeslotsQuota" class="flex wrap"> @if (timeslotsQuota) {
<div *ngFor="let type of types"> <mat-card-actions class="flex wrap">
@for (type of types; track type) {
<div>
<a mat-raised-button (click)="openCreate(type)"> <a mat-raised-button (click)="openCreate(type)">
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
{{'partey.timeslots.create.' + type | i18n}} {{'partey.timeslots.create.' + type | i18n}}
@@ -130,7 +144,9 @@
<br> <br>
<br> <br>
</div> </div>
}
</mat-card-actions> </mat-card-actions>
}
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/partey/stream" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/partey/stream" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltipPosition="above" target="_blank" mat-fab color="accent"> matTooltipPosition="above" target="_blank" mat-fab color="accent">
@@ -1,17 +1,22 @@
<form [formGroup]="form" (ngSubmit)="passwordReset()" *ngIf="!success"> @if (!success) {
<form [formGroup]="form" (ngSubmit)="passwordReset()">
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<h2>{{'password.reset' | i18n}}</h2> <h2>{{'password.reset' | i18n}}</h2>
<mat-error *ngIf="tokenInvalid"> @if (tokenInvalid) {
<mat-error>
{{'password.reset.tokenInvalid' | i18n}} {{'password.reset.tokenInvalid' | i18n}}
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'password' | i18n}}</mat-label> <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password" <input matInput type="password" formControlName="password"
[(ngModel)]="model.password"> [(ngModel)]="model.password">
<mat-error *ngFor="let error of form.get('password').errors | keyvalue"> @for (error of form.get('password').errors | keyvalue; track error) {
<mat-error>
{{error.key}} {{error.key}}
</mat-error> </mat-error>
}
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'password.confirm' | i18n}}</mat-label> <mat-label>{{'password.confirm' | i18n}}</mat-label>
@@ -23,16 +28,21 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid"> @if (!working) {
<button type="submit" mat-raised-button color="primary" [disabled]="form.invalid">
{{'password.reset' | i18n}} {{'password.reset' | i18n}}
</button> </button>
}
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> @if (working) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</form> </form>
}
<mat-card *ngIf="success"> @if (success) {
<mat-card>
<mat-card-content> <mat-card-content>
<h2>{{'password.reset.success.title' | i18n}}</h2> <h2>{{'password.reset.success.title' | i18n}}</h2>
<p>{{'password.reset.success.text' | i18n}}</p> <p>{{'password.reset.success.text' | i18n}}</p>
@@ -43,3 +53,4 @@
</a> </a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
}
@@ -18,11 +18,15 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid"> @if (!working) {
<button type="submit" mat-raised-button color="primary" [disabled]="form.invalid">
{{'password.request' | i18n}} {{'password.request' | i18n}}
</button> </button>
}
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> @if (working) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</form> </form>
+36 -20
View File
@@ -1,22 +1,24 @@
<form [formGroup]="form" (ngSubmit)="register()" *ngIf="!success"> @if (!success) {
<form [formGroup]="form" (ngSubmit)="register()">
<mat-card> <mat-card>
<mat-card-header> <mat-card-header>
<mat-card-title>{{'register' | i18n}}</mat-card-title> <mat-card-title>{{'register' | i18n}}</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<mat-error *ngIf="missingToken"> @if (missingToken) {
<mat-error>
{{'register.token.missing' | i18n}} {{'register.token.missing' | i18n}}
<a routerLink="/tokens" mat-raised-button color="warn">{{'register.token.missing.action' | <a routerLink="/tokens" mat-raised-button color="warn">{{'register.token.missing.action' |
i18n}}</a> i18n}}</a>
</mat-error> </mat-error>
<mat-error *ngIf="lockedToken"> }
@if (lockedToken) {
<mat-error>
{{'register.token.locked' | i18n}} {{'register.token.locked' | i18n}}
<a routerLink="/login" [queryParams]="{ target:'/tokens' }" mat-raised-button <a routerLink="/login" [queryParams]="{ target:'/tokens' }" mat-raised-button
color="warn">{{'register.token.locked.action' | i18n}}</a> color="warn">{{'register.token.locked.action' | i18n}}</a>
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'username' | i18n}}</mat-label> <mat-label>{{'username' | i18n}}</mat-label>
<input matInput formControlName="username" <input matInput formControlName="username"
@@ -33,9 +35,11 @@
<input matInput type="password" formControlName="password" <input matInput type="password" formControlName="password"
[(ngModel)]="model.password" required tabindex="2"> [(ngModel)]="model.password" required tabindex="2">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> @for (error of form.get('password').errors | keyvalue; track error) {
<div>
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
</div> </div>
}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
@@ -46,15 +50,14 @@
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle formControlName="primaryEmail" [(ngModel)]="model.primaryEmail" <mat-slide-toggle formControlName="primaryEmail" [(ngModel)]="model.primaryEmail"
(change)="onPrimaryChange()" tabindex="4"> (change)="onPrimaryChange()" tabindex="4">
{{'email.primary' | i18n}} {{'email.primary' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<mat-icon #primaryHint="matTooltip" (click)="primaryHint.toggle()" inline="true" <mat-icon #primaryHint="matTooltip" (click)="primaryHint.toggle()" inline="true"
matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon> matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon>
@if (model.primaryEmail) {
<mat-form-field *ngIf="model.primaryEmail"> <mat-form-field>
<mat-label>{{'email' | i18n}}</mat-label> <mat-label>{{'email' | i18n}}</mat-label>
<input matInput type="email" formControlName="email" <input matInput type="email" formControlName="email"
[(ngModel)]="model.email" required tabindex="5"> [(ngModel)]="model.email" required tabindex="5">
@@ -62,22 +65,28 @@
{{'email.invalid' | i18n}} {{'email.invalid' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
}
<mat-list *ngIf="items && items[0]"> @if (items && items[0]) {
<mat-list-item *ngFor="let item of items"> <mat-list>
@for (item of items; track item) {
<mat-list-item>
<mat-icon mat-list-icon>plus_one</mat-icon> <mat-icon mat-list-icon>plus_one</mat-icon>
{{ item.name[currentLocale] || 'missing' }} {{ item.name[currentLocale] || 'missing' }}
</mat-list-item> </mat-list-item>
}
</mat-list> </mat-list>
}
<mat-divider></mat-divider> <mat-divider></mat-divider>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid" tabindex="6"> @if (!working) {
<button type="submit" mat-raised-button color="primary" [disabled]="form.invalid" tabindex="6">
{{'register' | i18n}} {{'register' | i18n}}
</button> </button>
}
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> @if (working) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#registration" class="help-button" <a href="https://wiki.bstly.de/services/webstly#registration" class="help-button"
@@ -88,18 +97,24 @@
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>
</form> </form>
}
<div *ngIf="!success && permissions && permissions[0]"> @if (!success && permissions && permissions[0]) {
<div>
<h3>{{'permissions' | i18n}}</h3> <h3>{{'permissions' | i18n}}</h3>
<app-permissions [permissions]="permissions"></app-permissions> <app-permissions [permissions]="permissions"></app-permissions>
</div> </div>
<div *ngIf="!success && quotas && quotas[0]"> }
@if (!success && quotas && quotas[0]) {
<div>
<h3>{{'quotas' | i18n}}</h3> <h3>{{'quotas' | i18n}}</h3>
<app-quotas [quotas]="quotas"></app-quotas> <app-quotas [quotas]="quotas"></app-quotas>
</div> </div>
}
<mat-card *ngIf="success"> @if (success) {
<mat-card>
<mat-card-content> <mat-card-content>
<h2>{{'register.success.title' | i18n}}</h2> <h2>{{'register.success.title' | i18n}}</h2>
<p>{{'register.success.text' | i18n}}</p> <p>{{'register.success.text' | i18n}}</p>
@@ -110,3 +125,4 @@
</a> </a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
}
@@ -5,9 +5,11 @@
<mat-form-field> <mat-form-field>
<mat-chip-listbox #chipList [multiple]="true" [selectable]="true" cdkDropList cdkDropListOrientation="horizontal" <mat-chip-listbox #chipList [multiple]="true" [selectable]="true" cdkDropList cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"> (cdkDropListDropped)="drop($event)">
<mat-chip *ngFor="let dict of dicts" cdkDrag [selected]="dict.selected" (click)="toggle(dict)"> @for (dict of dicts; track dict) {
<mat-chip cdkDrag [selected]="dict.selected" (click)="toggle(dict)">
{{dict.name}} {{dict.name}}
</mat-chip> </mat-chip>
}
</mat-chip-listbox> </mat-chip-listbox>
</mat-form-field> </mat-form-field>
+24 -10
View File
@@ -12,28 +12,42 @@
</header> </header>
<mat-progress-bar *ngIf="!baseServices" mode="indeterminate"></mat-progress-bar> @if (!baseServices) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
<p *ngIf="baseServices && baseServices.length == 0">{{'services.empty' | i18n}}</p> @if (baseServices && baseServices.length == 0) {
<p>{{'services.empty' | i18n}}</p>
}
<div *ngIf="view=='grid'"> @if (view=='grid') {
<app-services-grid *ngIf="baseServices" [services]="baseServices"></app-services-grid> <div>
@if (baseServices) {
<app-services-grid [services]="baseServices"></app-services-grid>
}
<br> <br>
<div *ngFor="let item of serviceCategory | keyvalue"> @for (item of serviceCategory | keyvalue; track item) {
<div>
<h4>{{'services.category.' + item.key | i18n}}</h4> <h4>{{'services.category.' + item.key | i18n}}</h4>
<app-services-grid [services]="item.value"></app-services-grid> <app-services-grid [services]="item.value"></app-services-grid>
<br> <br>
</div> </div>
}
</div> </div>
}
<div *ngIf="view=='table'"> @if (view=='table') {
<app-services-table *ngIf="baseServices" [services]="baseServices"></app-services-table> <div>
@if (baseServices) {
<app-services-table [services]="baseServices"></app-services-table>
}
<br> <br>
<div *ngFor="let item of serviceCategory | keyvalue"> @for (item of serviceCategory | keyvalue; track item) {
<div>
<h4>{{'services.category.' + item.key | i18n}}</h4> <h4>{{'services.category.' + item.key | i18n}}</h4>
<app-services-table [services]="item.value"></app-services-table> <app-services-table [services]="item.value"></app-services-table>
<br> <br>
</div> </div>
}
</div> </div>
}
+36 -20
View File
@@ -1,40 +1,48 @@
<div *ngIf="items && items[0]"> @if (items && items[0]) {
<div>
<h2>{{'tokens.active' | i18n}}</h2> <h2>{{'tokens.active' | i18n}}</h2>
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<mat-list> <mat-list>
<mat-list-item *ngFor="let item of items"> @for (item of items; track item) {
<mat-list-item>
<mat-icon mat-list-icon>plus_one</mat-icon> <mat-icon mat-list-icon>plus_one</mat-icon>
{{ item.name[currentLocale] || 'missing' }} {{ item.name[currentLocale] || 'missing' }}
<a mat-icon-button (click)="removeSecret(item.secret)"> <a mat-icon-button (click)="removeSecret(item.secret)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</mat-list-item> </mat-list-item>
}
</mat-list> </mat-list>
<mat-divider></mat-divider> <mat-divider></mat-divider>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a *ngIf="auth.authenticated" mat-raised-button color="accent" (click)="redeem()"> @if (auth.authenticated) {
<a mat-raised-button color="accent" (click)="redeem()">
<mat-icon>redeem</mat-icon> {{'tokens.redeem' | i18n}} <mat-icon>redeem</mat-icon> {{'tokens.redeem' | i18n}}
</a> </a>
}
<div class="flex column" *ngIf="!auth.authenticated"> @if (!auth.authenticated) {
<p *ngIf="canRegister()">{{'tokens.register' | i18n}}</p> <div class="flex column">
@if (canRegister()) {
<a routerLink="/register" mat-raised-button color="accent" *ngIf="canRegister()"> <p>{{'tokens.register' | i18n}}</p>
}
@if (canRegister()) {
<a routerLink="/register" mat-raised-button color="accent">
<mat-icon>how_to_reg</mat-icon> {{'register' | i18n}} <mat-icon>how_to_reg</mat-icon> {{'register' | i18n}}
</a> </a>
}
<p *ngIf="canRegister()">{{'tokens.login' | i18n}}</p> @if (canRegister()) {
<p>{{'tokens.login' | i18n}}</p>
<p *ngIf="!canRegister()">{{'tokens.loginRequired' | i18n}}</p> }
@if (!canRegister()) {
<p>{{'tokens.loginRequired' | i18n}}</p>
}
<a routerLink="/login" [queryParams]="{ target:'/tokens' }" mat-raised-button color="primary"> <a routerLink="/login" [queryParams]="{ target:'/tokens' }" mat-raised-button color="primary">
<mat-icon>login</mat-icon> {{'login' | i18n}} <mat-icon>login</mat-icon> {{'login' | i18n}}
</a> </a>
</div> </div>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#registration" class="help-button" <a href="https://wiki.bstly.de/services/webstly#registration" class="help-button"
@@ -43,16 +51,20 @@
</a> </a>
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>
@if (permissions && permissions[0]) {
<div *ngIf="permissions && permissions[0]"> <div>
<h3>{{'permissions' | i18n}}</h3> <h3>{{'permissions' | i18n}}</h3>
<app-permissions [permissions]="permissions" [startsVisible]="canRegister() || auth.authenticated" [expiresVisible]="canRegister() || auth.authenticated"></app-permissions> <app-permissions [permissions]="permissions" [startsVisible]="canRegister() || auth.authenticated" [expiresVisible]="canRegister() || auth.authenticated"></app-permissions>
</div> </div>
<div *ngIf="quotas && quotas[0]"> }
@if (quotas && quotas[0]) {
<div>
<h3>{{'quotas' | i18n}}</h3> <h3>{{'quotas' | i18n}}</h3>
<app-quotas [quotas]="quotas"></app-quotas> <app-quotas [quotas]="quotas"></app-quotas>
</div> </div>
}
</div> </div>
}
<form [formGroup]="form" (ngSubmit)="redeemSecret()" #formDirective="ngForm"> <form [formGroup]="form" (ngSubmit)="redeemSecret()" #formDirective="ngForm">
<mat-card> <mat-card>
@@ -60,12 +72,16 @@
<mat-card-title>{{'tokens.enter' | i18n}}</mat-card-title> <mat-card-title>{{'tokens.enter' | i18n}}</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<mat-error *ngIf="tokenInvalid"> @if (tokenInvalid) {
<mat-error>
{{'tokens.invalid' | i18n}} {{'tokens.invalid' | i18n}}
</mat-error> </mat-error>
<mat-error *ngIf="tokenRedeemed"> }
@if (tokenRedeemed) {
<mat-error>
{{'tokens.redeemed' | i18n}} {{'tokens.redeemed' | i18n}}
</mat-error> </mat-error>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'token' | i18n}}</mat-label> <mat-label>{{'token' | i18n}}</mat-label>
<input matInput formControlName="token" matAutofocus> <input matInput formControlName="token" matAutofocus>
@@ -1,6 +1,7 @@
<h3>{{'urlshortener' | i18n}}</h3> <h3>{{'urlshortener' | i18n}}</h3>
<div *ngIf="shortenedUrls"> @if (shortenedUrls) {
<div>
<div class="flex"> <div class="flex">
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.search' | i18n}}</mat-label> <mat-label>{{'urlshortener.search' | i18n}}</mat-label>
@@ -8,7 +9,6 @@
</mat-form-field> </mat-form-field>
</div> </div>
<table mat-table matSort [dataSource]="shortenedUrls.content" (matSortChange)="updateSort($event)"> <table mat-table matSort [dataSource]="shortenedUrls.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="share"> <ng-container matColumnDef="share">
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.share' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'urlshortener.share' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl"> <td mat-cell *matCellDef="let shortenedUrl">
@@ -17,25 +17,24 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="link"> <ng-container matColumnDef="link">
<th mat-header-cell *matHeaderCellDef mat-sort-header="link"> {{'urlshortener.link' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="link"> {{'urlshortener.link' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl"> <td mat-cell *matCellDef="let shortenedUrl">
<a mat-button color="accent" href="{{ shortenedUrl.link }}" target="_blank"> <a mat-button color="accent" href="{{ shortenedUrl.link }}" target="_blank">
<mat-icon *ngIf="shortenedUrl.password">vpn_key</mat-icon> {{ shortenedUrl.link }} @if (shortenedUrl.password) {
<mat-icon>vpn_key</mat-icon>
} {{ shortenedUrl.link }}
<mat-icon style="font-size: 1em;">open_in_new <mat-icon style="font-size: 1em;">open_in_new
</mat-icon> </mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="note"> <ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.note' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'urlshortener.note' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl"> <td mat-cell *matCellDef="let shortenedUrl">
{{shortenedUrl.note}} {{shortenedUrl.note}}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="url"> <ng-container matColumnDef="url">
<th mat-header-cell *matHeaderCellDef mat-sort-header="url"> {{'urlshortener.url' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="url"> {{'urlshortener.url' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl"> <td mat-cell *matCellDef="let shortenedUrl">
@@ -46,12 +45,10 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="expires"> <ng-container matColumnDef="expires">
<th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'urlshortener.expires' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'urlshortener.expires' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl"> {{ shortenedUrl.expires | datef:datetimeformat}} </td> <td mat-cell *matCellDef="let shortenedUrl"> {{ shortenedUrl.expires | datef:datetimeformat}} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="edit"> <ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'urlshortener.edit' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl" class="text-right"> <td mat-cell *matCellDef="let shortenedUrl" class="text-right">
@@ -60,7 +57,6 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'urlshortener.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'urlshortener.delete' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl" class="text-right"> <td mat-cell *matCellDef="let shortenedUrl" class="text-right">
@@ -69,23 +65,24 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="shortenedUrlColumns"></tr> <tr mat-header-row *matHeaderRowDef="shortenedUrlColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: shortenedUrlColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: shortenedUrlColumns"></tr>
</table> </table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="shortenedUrls.totalElements" <mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="shortenedUrls.totalElements"
[pageSize]="shortenedUrls.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator> [pageSize]="shortenedUrls.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div> </div>
}
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm"> <form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'urlshortener.info' | i18n}}</p> <p>{{'urlshortener.info' | i18n}}</p>
<p *ngIf="!shortenedUrlQuota">{{'urlshortener.noQuota' | i18n}}</p> @if (!shortenedUrlQuota) {
<div *ngIf="shortenedUrlQuota"> <p>{{'urlshortener.noQuota' | i18n}}</p>
}
@if (shortenedUrlQuota) {
<div>
<p>{{'urlshortener.left' | i18n:shortenedUrlQuota}}</p> <p>{{'urlshortener.left' | i18n:shortenedUrlQuota}}</p>
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.url' | i18n}}</mat-label> <mat-label>{{'urlshortener.url' | i18n}}</mat-label>
<input matInput formControlName="url" [(ngModel)]="shortenedUrl.url" type="url"> <input matInput formControlName="url" [(ngModel)]="shortenedUrl.url" type="url">
@@ -93,7 +90,6 @@
{{'urlshortener.error.url' | i18n}} {{'urlshortener.error.url' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.note' | i18n}}</mat-label> <mat-label>{{'urlshortener.note' | i18n}}</mat-label>
<textarea matInput type="note" formControlName="note" [(ngModel)]="shortenedUrl.note"></textarea> <textarea matInput type="note" formControlName="note" [(ngModel)]="shortenedUrl.note"></textarea>
@@ -101,7 +97,6 @@
{{'urlshortener.error.note' | i18n}} {{'urlshortener.error.note' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
@@ -112,12 +107,13 @@
<mat-label>{{'password' | i18n}}</mat-label> <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password" [(ngModel)]="shortenedUrl.password"> <input matInput type="password" formControlName="password" [(ngModel)]="shortenedUrl.password">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> @for (error of form.get('password').errors | keyvalue; track error) {
<div>
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
</div> </div>
}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'password.confirm' | i18n}}</mat-label> <mat-label>{{'password.confirm' | i18n}}</mat-label>
<input matInput type="password" formControlName="password2" [(ngModel)]="shortenedUrl.password2"> <input matInput type="password" formControlName="password2" [(ngModel)]="shortenedUrl.password2">
@@ -125,7 +121,6 @@
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.expires' | i18n}}</mat-label> <mat-label>{{'urlshortener.expires' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="shortenedUrl.expires" formControlName="expires"> <input matInput type="datetime-local" [(ngModel)]="shortenedUrl.expires" formControlName="expires">
@@ -133,12 +128,10 @@
{{'urlshortener.error.expires' | i18n}} {{'urlshortener.error.expires' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle [(ngModel)]="shortenedUrl.queryParameters" formControlName="queryParameters"> <mat-slide-toggle [(ngModel)]="shortenedUrl.queryParameters" formControlName="queryParameters">
{{'urlshortener.queryParameters' | i18n}} {{'urlshortener.queryParameters' | i18n}}
<mat-icon inline="true" matTooltip="{{'urlshortener.queryParameters.info' | i18n}}">info</mat-icon> <mat-icon inline="true" matTooltip="{{'urlshortener.queryParameters.info' | i18n}}">info</mat-icon>
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.code' | i18n}}</mat-label> <mat-label>{{'urlshortener.code' | i18n}}</mat-label>
<input matInput formControlName="code" [(ngModel)]="shortenedUrl.code"> <input matInput formControlName="code" [(ngModel)]="shortenedUrl.code">
@@ -148,12 +141,15 @@
</mat-form-field> </mat-form-field>
</mat-expansion-panel> </mat-expansion-panel>
</div> </div>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" *ngIf="shortenedUrlQuota && !working" mat-raised-button color="primary" @if (shortenedUrlQuota && !working) {
<button type="submit" mat-raised-button color="primary"
[disabled]="form.invalid"> [disabled]="form.invalid">
{{'urlshortener.create' | i18n}} {{'urlshortener.create' | i18n}}
</button> </button>
}
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/urlshortener" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/urlshortener" class="help-button" matTooltip="{{'help-button' | i18n}}"
@@ -30,23 +30,29 @@
{{'urlshortener.newPassword' | i18n}} {{'urlshortener.newPassword' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field *ngIf="shortenedUrlModel.newPassword"> @if (shortenedUrlModel.newPassword) {
<mat-form-field>
<mat-label>{{'password' | i18n}}</mat-label> <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password" [(ngModel)]="shortenedUrlModel.password"> <input matInput type="password" formControlName="password" [(ngModel)]="shortenedUrlModel.password">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> @for (error of form.get('password').errors | keyvalue; track error) {
<div>
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
</div> </div>
}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
}
<mat-form-field *ngIf="shortenedUrlModel.newPassword"> @if (shortenedUrlModel.newPassword) {
<mat-form-field>
<mat-label>{{'password.confirm' | i18n}}</mat-label> <mat-label>{{'password.confirm' | i18n}}</mat-label>
<input matInput type="password" formControlName="password2" [(ngModel)]="shortenedUrlModel.password2"> <input matInput type="password" formControlName="password2" [(ngModel)]="shortenedUrlModel.password2">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
}
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.expires' | i18n}}</mat-label> <mat-label>{{'urlshortener.expires' | i18n}}</mat-label>
@@ -7,9 +7,11 @@
<mat-label>{{'password' | i18n}}</mat-label> <mat-label>{{'password' | i18n}}</mat-label>
<input id="password" name="password" matInput type="password" required matAutofocus> <input id="password" name="password" matInput type="password" required matAutofocus>
</mat-form-field> </mat-form-field>
<mat-error *ngIf="invalidPassword"> @if (invalidPassword) {
<mat-error>
{{'urlshortener.password.invalid' | i18n}} {{'urlshortener.password.invalid' | i18n}}
</mat-error> </mat-error>
}
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a type="submit" (click)="loginForm.submit()" mat-raised-button color="primary" <a type="submit" (click)="loginForm.submit()" mat-raised-button color="primary"
+18 -6
View File
@@ -1,22 +1,33 @@
<br> <br>
<mat-progress-bar *ngIf="!success && !error" mode="indeterminate"></mat-progress-bar> @if (!success && !error) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
}
<div *ngIf="success"> @if (success) {
<div>
<h3>{{model.username}} <h3>{{model.username}}
<a *ngIf="isMe" mat-icon-button color="accent"> @if (isMe) {
<a mat-icon-button color="accent">
<mat-icon>star</mat-icon> <mat-icon>star</mat-icon>
</a> </a>
}
</h3> </h3>
<app-profilefields [username]="model.username"></app-profilefields> <app-profilefields [username]="model.username"></app-profilefields>
<div *ngIf="model.aliases && model.aliases.length > 0"> @if (model.aliases && model.aliases.length > 0) {
<div>
<h4>{{'user.aliases' | i18n}}</h4> <h4>{{'user.aliases' | i18n}}</h4>
<mat-list> <mat-list>
<mat-list-item *ngFor="let alias of model.aliases">{{alias}}</mat-list-item> @for (alias of model.aliases; track alias) {
<mat-list-item>{{alias}}</mat-list-item>
}
</mat-list> </mat-list>
</div> </div>
}
</div> </div>
}
<mat-card class="warn" *ngIf="error"> @if (error) {
<mat-card class="warn">
<mat-card-header> <mat-card-header>
<mat-card-title>{{error.status}}</mat-card-title> <mat-card-title>{{error.status}}</mat-card-title>
<mat-card-subtitle>{{'user.unavailable' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'user.unavailable' | i18n}}</mat-card-subtitle>
@@ -27,3 +38,4 @@
</p> </p>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
}
+6 -2
View File
@@ -2,12 +2,16 @@
<mat-select [(ngModel)]="unit"> <mat-select [(ngModel)]="unit">
<mat-option *ngFor="let item of dividerModel.units" [value]="item">{{item.name}}</mat-option> @for (item of dividerModel.units; track item) {
<mat-option [value]="item">{{item.name}}</mat-option>
}
</mat-select> </mat-select>
<div *ngFor="let item of dividerModel.items"> @for (item of dividerModel.items; track item) {
<div>
<label>{{item.name}}: {{toCurrentUnit(item.value)}}{{unit.name}}</label> <label>{{item.name}}: {{toCurrentUnit(item.value)}}{{unit.name}}</label>
<mat-slider [max]="toCurrentUnit(dividerModel.free + item.value)" min=0 [step]="unit.steps" [value]="toCurrentUnit(item.value)" thumbLabel <mat-slider [max]="toCurrentUnit(dividerModel.free + item.value)" min=0 [step]="unit.steps" [value]="toCurrentUnit(item.value)" thumbLabel
tickInterval="5" (change)="updateValue($event, item)"> tickInterval="5" (change)="updateValue($event, item)">
</mat-slider> </mat-slider>
</div> </div>
}
+30 -10
View File
@@ -14,21 +14,33 @@
<mat-icon>arrow_drop_down</mat-icon> <mat-icon>arrow_drop_down</mat-icon>
</a> </a>
<mat-menu #menu="matMenu"> <mat-menu #menu="matMenu">
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-menu-item> @if (!auth || auth && !auth.authenticated) {
<a routerLink="/login" routerLinkActive="active" mat-menu-item>
<mat-icon>login</mat-icon> {{'login' | i18n}} <mat-icon>login</mat-icon> {{'login' | i18n}}
</a> </a>
<mat-divider *ngIf="!auth || auth && !auth.authenticated"></mat-divider> }
<a *ngFor="let locale of locales" mat-menu-item (click)="setLocale(locale)">{{'locale.' + locale + '.long' | @if (!auth || auth && !auth.authenticated) {
i18n}} <mat-icon *ngIf="locale == currentLocale">done</mat-icon></a> <mat-divider></mat-divider>
}
@for (locale of locales; track locale) {
<a mat-menu-item (click)="setLocale(locale)">{{'locale.' + locale + '.long' |
i18n}} @if (locale == currentLocale) {
<mat-icon>done</mat-icon>
}</a>
}
<a mat-menu-item> <a mat-menu-item>
<mat-slide-toggle (change)="darkThemeChange($event)" [checked]="darkTheme == 'true'"> <mat-slide-toggle (change)="darkThemeChange($event)" [checked]="darkTheme == 'true'">
{{'profileField.name.darkTheme' | i18n}} {{'profileField.name.darkTheme' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
</a> </a>
<mat-divider *ngIf="auth && auth.authenticated"></mat-divider> @if (auth && auth.authenticated) {
<a *ngIf="auth && auth.authenticated" (click)="logout()" mat-menu-item> <mat-divider></mat-divider>
}
@if (auth && auth.authenticated) {
<a (click)="logout()" mat-menu-item>
<mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}} <mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}}
</a> </a>
}
</mat-menu> </mat-menu>
</ng-container> </ng-container>
</mat-toolbar> </mat-toolbar>
@@ -37,15 +49,21 @@
<mat-sidenav #sidenav [mode]="isBiggerScreen() ? 'side' : 'over'" [(opened)]="opened" <mat-sidenav #sidenav [mode]="isBiggerScreen() ? 'side' : 'over'" [(opened)]="opened"
(click)="!isBiggerScreen() && opened=false"> (click)="!isBiggerScreen() && opened=false">
<mat-nav-list> <mat-nav-list>
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-list-item> @if (!auth || auth && !auth.authenticated) {
<a routerLink="/login" routerLinkActive="active" mat-list-item>
<mat-icon>login</mat-icon> {{'login' | i18n}} <mat-icon>login</mat-icon> {{'login' | i18n}}
</a> </a>
<a *ngIf="auth && auth.authenticated" routerLink="/account/info" routerLinkActive="active" mat-list-item> }
@if (auth && auth.authenticated) {
<a routerLink="/account/info" routerLinkActive="active" mat-list-item>
<mat-icon>account_circle</mat-icon> {{'account' | i18n}} <mat-icon>account_circle</mat-icon> {{'account' | i18n}}
</a> </a>
<a *ngIf="auth && auth.authenticated" routerLink="/services" routerLinkActive="active" mat-list-item> }
@if (auth && auth.authenticated) {
<a routerLink="/services" routerLinkActive="active" mat-list-item>
<mat-icon>widgets</mat-icon> {{'services' | i18n}} <mat-icon>widgets</mat-icon> {{'services' | i18n}}
</a> </a>
}
<a routerLink="/tokens" mat-list-item> <a routerLink="/tokens" mat-list-item>
<mat-icon>card_giftcard</mat-icon> {{'tokens.redeem' | i18n}} <mat-icon>card_giftcard</mat-icon> {{'tokens.redeem' | i18n}}
</a> </a>
@@ -57,9 +75,11 @@
<mat-icon>shopping_cart</mat-icon> {{'tokens.get' | i18n}}<mat-icon style="font-size: 1em;">open_in_new <mat-icon>shopping_cart</mat-icon> {{'tokens.get' | i18n}}<mat-icon style="font-size: 1em;">open_in_new
</mat-icon> </mat-icon>
</a> </a>
<a *ngIf="auth && auth.authenticated" (click)="logout()" mat-list-item> @if (auth && auth.authenticated) {
<a (click)="logout()" mat-list-item>
<mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}} <mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}}
</a> </a>
}
</mat-nav-list> </mat-nav-list>
<span class="spacer"></span> <span class="spacer"></span>
@@ -5,7 +5,9 @@
<td mat-cell *matCellDef="let permission"> <td mat-cell *matCellDef="let permission">
<mat-icon inline="true">{{'services.' + permission.name + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'services.' + permission.name + '.icon' | i18n}}</mat-icon>
{{'services.' + permission.name + '.title' | i18n}} {{'services.' + permission.name + '.title' | i18n}}
<mat-icon inline="true" *ngIf="permission.addon">add_circle</mat-icon> @if (permission.addon) {
<mat-icon inline="true">add_circle</mat-icon>
}
<br> <br>
<small>{{'services.' + permission.name + '.subtitle' | i18n}}</small> <small>{{'services.' + permission.name + '.subtitle' | i18n}}</small>
</td> </td>
@@ -12,24 +12,30 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'profileField.type' | i18n}}</mat-label> <mat-label>{{'profileField.type' | i18n}}</mat-label>
<mat-select [(ngModel)]="profileField.type" formControlName="type"> <mat-select [(ngModel)]="profileField.type" formControlName="type">
<mat-option *ngFor="let type of types" [value]="type"> @for (type of types; track type) {
<mat-option [value]="type">
{{'profileField.type.' + type | i18n}} {{'profileField.type.' + type | i18n}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
<mat-error> <mat-error>
{{'profileField.error.type' | i18n}} {{'profileField.error.type' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div [ngSwitch]="profileField.type"> <div>
<mat-form-field *ngSwitchCase="'TEXT'"> @switch (profileField.type) {
@case ('TEXT') {
<mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput type="text" max="255" [(ngModel)]="profileField.value" formControlName="value"> <input matInput type="text" max="255" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.TEXT' | i18n}} {{'profileField.error.TEXT' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'DATE'"> }
@case ('DATE') {
<mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput [matDatepicker]="datePicker" [(ngModel)]="profileField.value" formControlName="value"> <input matInput [matDatepicker]="datePicker" [(ngModel)]="profileField.value" formControlName="value">
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
@@ -38,39 +44,51 @@
{{'profileField.error.DATE' | i18n}} {{'profileField.error.DATE' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'DATETIME'"> }
@case ('DATETIME') {
<mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput type="datetime-local" [(ngModel)]="profileField.value" formControlName="value"> <input matInput type="datetime-local" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.DATETIME' | i18n}} {{'profileField.error.DATETIME' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'URL'"> }
@case ('URL') {
<mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput type="url" [(ngModel)]="profileField.value" formControlName="value"> <input matInput type="url" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.URL' | i18n}} {{'profileField.error.URL' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'EMAIL'"> }
@case ('EMAIL') {
<mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput type="email" [(ngModel)]="profileField.value" formControlName="value"> <input matInput type="email" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.EMAIL' | i18n}} {{'profileField.error.EMAIL' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle *ngSwitchCase="'BOOL'" (change)="booleanChange(profileField)" }
@case ('BOOL') {
<mat-slide-toggle (change)="booleanChange(profileField)"
[checked]="profileField.value == 'true'"> [checked]="profileField.value == 'true'">
{{'profileField.value' | i18n}} {{'profileField.value' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field *ngSwitchCase="'NUMBER'"> }
@case ('NUMBER') {
<mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput type="number" [(ngModel)]="profileField.value" formControlName="value"> <input matInput type="number" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.NUMBER' | i18n}} {{'profileField.error.NUMBER' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div *ngSwitchCase="'BLOB'"> }
@case ('BLOB') {
<div>
<mat-form-field> <mat-form-field>
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<textarea matInput [(ngModel)]="profileField.blob" formControlName="blob"></textarea> <textarea matInput [(ngModel)]="profileField.blob" formControlName="blob"></textarea>
@@ -80,16 +98,20 @@
</mat-form-field> </mat-form-field>
<a mat-button (click)="pgpBlob(profileField)">{{'profileField.blob.pgp' | i18n}}</a> <a mat-button (click)="pgpBlob(profileField)">{{'profileField.blob.pgp' | i18n}}</a>
</div> </div>
}
}
</div> </div>
<mat-form-field> <mat-form-field>
<mat-label>{{'visibility' | i18n}}</mat-label> <mat-label>{{'visibility' | i18n}}</mat-label>
<mat-select [(ngModel)]="profileField.visibility" formControlName="visibility"> <mat-select [(ngModel)]="profileField.visibility" formControlName="visibility">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility"> @for (visibility of visibilities; track visibility) {
<mat-option [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}} visibility | i18n}}
</mat-option> </mat-option>
}
<mat-select-trigger> <mat-select-trigger>
<mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon>
@@ -8,30 +8,53 @@
<ng-container matColumnDef="value"> <ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header="value"> {{'profileField.value' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="value"> {{'profileField.value' | i18n}} </th>
<td mat-cell *matCellDef="let profileField"> <td mat-cell *matCellDef="let profileField">
<div [ngSwitch]="profileField.type"> <div>
<span *ngSwitchCase="'TEXT'">{{profileField.value}}</span> @switch (profileField.type) {
<span *ngSwitchCase="'DATE'">{{profileField.value | datef:dateformat}}</span> @case ('TEXT') {
<span *ngSwitchCase="'DATETIME'">{{profileField.value | datef:datetimeformat}}</span> <span>{{profileField.value}}</span>
<span *ngSwitchCase="'TIME'">{{profileField.value | datef:timeformat}}</span> }
<a *ngSwitchCase="'URL'" class="accent" href="{{profileField.value}}">{{profileField.value}}</a> @case ('DATE') {
<a *ngSwitchCase="'EMAIL'" class="accent" href="mailto:{{profileField.value}}">{{profileField.value}}</a> <span>{{profileField.value | datef:dateformat}}</span>
<span *ngSwitchCase="'NUMBER'">{{profileField.value}}</span> }
<a *ngSwitchCase="'BLOB'" mat-raised-button (click)="openBlob(profileField)">{{'profileField.openBlob' | i18n}}</a> @case ('DATETIME') {
<mat-slide-toggle *ngSwitchCase="'BOOL'" [checked]="profileField.value == 'true'" disabled> <span>{{profileField.value | datef:datetimeformat}}</span>
}
@case ('TIME') {
<span>{{profileField.value | datef:timeformat}}</span>
}
@case ('URL') {
<a class="accent" href="{{profileField.value}}">{{profileField.value}}</a>
}
@case ('EMAIL') {
<a class="accent" href="mailto:{{profileField.value}}">{{profileField.value}}</a>
}
@case ('NUMBER') {
<span>{{profileField.value}}</span>
}
@case ('BLOB') {
<a mat-raised-button (click)="openBlob(profileField)">{{'profileField.openBlob' | i18n}}</a>
}
@case ('BOOL') {
<mat-slide-toggle [checked]="profileField.value == 'true'" disabled>
</mat-slide-toggle> </mat-slide-toggle>
}
}
</div> </div>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="visibility" *ngIf="edit"> @if (edit) {
<ng-container matColumnDef="visibility">
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
<td mat-cell *matCellDef="let profileField"> <td mat-cell *matCellDef="let profileField">
<mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon>
{{'visibility.' + profileField.visibility | i18n}} {{'visibility.' + profileField.visibility | i18n}}
</td> </td>
</ng-container> </ng-container>
}
<ng-container matColumnDef="edit" *ngIf="edit"> @if (edit) {
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef class="text-right"> {{'profileField.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="text-right"> {{'profileField.edit' | i18n}} </th>
<td mat-cell *matCellDef="let profileField" class="text-right"> <td mat-cell *matCellDef="let profileField" class="text-right">
<a mat-icon-button (click)="openEdit(profileField)"> <a mat-icon-button (click)="openEdit(profileField)">
@@ -39,8 +62,10 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
}
<ng-container matColumnDef="delete" *ngIf="edit"> @if (edit) {
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'profileField.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'profileField.delete' | i18n}} </th>
<td mat-cell *matCellDef="let profileField" class="text-right"> <td mat-cell *matCellDef="let profileField" class="text-right">
<a mat-icon-button (click)="confirmDelete(profileField)"> <a mat-icon-button (click)="confirmDelete(profileField)">
@@ -48,12 +73,15 @@
</a> </a>
</td> </td>
</ng-container> </ng-container>
}
<tr mat-header-row *matHeaderRowDef="profileFieldColumns"></tr> <tr mat-header-row *matHeaderRowDef="profileFieldColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: profileFieldColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: profileFieldColumns"></tr>
</table> </table>
<br> <br>
<div *ngIf="edit" class="text-center"> @if (edit) {
<div class="text-center">
<a mat-raised-button color="primary" (click)="openCreate()">{{'profileField.create' | <a mat-raised-button color="primary" (click)="openCreate()">{{'profileField.create' |
i18n}}</a> i18n}}</a>
</div> </div>
}
+12 -4
View File
@@ -10,21 +10,29 @@
<ng-container matColumnDef="value"> <ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header="value"> {{'quotas.value' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="value"> {{'quotas.value' | i18n}} </th>
<td mat-cell *matCellDef="let quota"><span *ngIf="quota.disposable">{{quota.value}}</span> </td> <td mat-cell *matCellDef="let quota">@if (quota.disposable) {
<span>{{quota.value}}</span>
} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="fixed_value"> <ng-container matColumnDef="fixed_value">
<th mat-header-cell *matHeaderCellDef> {{'quotas.fixed_value' | i18n}} <mat-icon #primaryHint="matTooltip" inline <th mat-header-cell *matHeaderCellDef> {{'quotas.fixed_value' | i18n}} <mat-icon #primaryHint="matTooltip" inline
matTooltip="{{'quotas.fixed_value.hint' | i18n}}">info</mat-icon> matTooltip="{{'quotas.fixed_value.hint' | i18n}}">info</mat-icon>
</th> </th>
<td mat-cell *matCellDef="let quota"><span *ngIf="!quota.disposable">{{quota.value}}</span> </td> <td mat-cell *matCellDef="let quota">@if (!quota.disposable) {
<span>{{quota.value}}</span>
} </td>
</ng-container> </ng-container>
<ng-container matColumnDef="quotaUnit"> <ng-container matColumnDef="quotaUnit">
<th mat-header-cell *matHeaderCellDef> {{'quotas.unit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'quotas.unit' | i18n}} </th>
<td mat-cell *matCellDef="let quota"> <td mat-cell *matCellDef="let quota">
<span *ngIf="quota.unit">{{'quotas.unit.' + quota.unit | i18n}}</span> @if (quota.unit) {
<span *ngIf="!quota.unit">#</span> <span>{{'quotas.unit.' + quota.unit | i18n}}</span>
}
@if (!quota.unit) {
<span>#</span>
}
</td> </td>
</ng-container> </ng-container>
@@ -1,21 +1,26 @@
<div class="service-grid" fxLayoutGap="24px grid"> <div class="service-grid" fxLayoutGap="24px grid">
<div *ngFor="let service of services"> @for (service of services; track service) {
<div>
<mat-card> <mat-card>
<a *ngIf="service.url" href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" @if (service.url) {
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'"
color="accent"> color="accent">
<mat-card-header> <mat-card-header>
<div class="icon" mat-card-avatar> <div class="icon" mat-card-avatar>
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon> <mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
</div> </div>
<mat-card-title> <strong>{{'services.' + service.name + '.title' | i18n}}</strong> <mat-card-title> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
<mat-icon *ngIf="!service.sameSite" inline="true"> @if (!service.sameSite) {
<mat-icon inline="true">
open_in_new</mat-icon> open_in_new</mat-icon>
}
</mat-card-title> </mat-card-title>
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>
</mat-card-header> </mat-card-header>
</a> </a>
}
<mat-card-header *ngIf="!service.url"> @if (!service.url) {
<mat-card-header>
<div class="icon" mat-card-avatar> <div class="icon" mat-card-avatar>
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon> <mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
</div> </div>
@@ -23,6 +28,7 @@
</mat-card-title> </mat-card-title>
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>
</mat-card-header> </mat-card-header>
}
<mat-card-content> <mat-card-content>
<p> <p>
{{ 'services.' + service.name + '.text' | i18n}} {{ 'services.' + service.name + '.text' | i18n}}
@@ -32,4 +38,5 @@
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </div>
}
</div> </div>
@@ -1,4 +1,4 @@
@import '../../../variables.scss'; @use '../../../variables.scss' as vars;
.service-grid { .service-grid {
@@ -28,7 +28,7 @@
color: inherit; color: inherit;
mat-card-title { mat-card-title {
color: $accent; color: vars.$accent;
} }
} }
@@ -1,34 +1,38 @@
<table *ngIf="services && services.length > 0" mat-table matSort [dataSource]="services" @if (services && services.length > 0) {
<table mat-table matSort [dataSource]="services"
(matSortChange)="sortData($event)" matSortActive="name" matSortDirection="asc"> (matSortChange)="sortData($event)" matSortActive="name" matSortDirection="asc">
<ng-container matColumnDef="icon"> <ng-container matColumnDef="icon">
<th mat-header-cell *matHeaderCellDef> </th> <th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let service"> <td mat-cell *matCellDef="let service">
<mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon> <mat-icon>{{'services.' + service.name + '.icon' | i18n}}</mat-icon>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'service.name' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'service.name' | i18n}} </th>
<td mat-cell *matCellDef="let service" class="text-center"> <td mat-cell *matCellDef="let service" class="text-center">
<a *ngIf="service.url" href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" mat-button @if (service.url) {
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" mat-button
color="accent"> color="accent">
<strong>{{'services.' + service.name + '.title' | i18n}}</strong> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
<mat-icon *ngIf="!service.sameSite" inline="true"> @if (!service.sameSite) {
<mat-icon inline="true">
open_in_new</mat-icon> open_in_new</mat-icon>
}
</a> </a>
<a *ngIf="!service.url" mat-button disabled> }
@if (!service.url) {
<a mat-button disabled>
<strong>{{'services.' + service.name + '.title' | i18n}}</strong> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
</a> </a>
}
<div><small>{{'services.' + service.name + '.subtitle' | i18n}}</small></div> <div><small>{{'services.' + service.name + '.subtitle' | i18n}}</small></div>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="text"> <ng-container matColumnDef="text">
<th mat-header-cell *matHeaderCellDef> {{'service.text' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'service.text' | i18n}} </th>
<td mat-cell *matCellDef="let service">{{ 'services.' + service.name + '.text' | i18n}}</td> <td mat-cell *matCellDef="let service">{{ 'services.' + service.name + '.text' | i18n}}</td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="serviceColumns"></tr> <tr mat-header-row *matHeaderRowDef="serviceColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: serviceColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: serviceColumns"></tr>
</table> </table>
}
+7 -8
View File
@@ -2,21 +2,20 @@
@use '@angular/material' as mat; @use '@angular/material' as mat;
// For more information: https://material.angular.io/guide/theming // For more information: https://material.angular.io/guide/theming
// Plus imports for other components in your app. // Plus imports for other components in your app.
@use './variables.scss' as vars;
// Include the common styles for Angular Material. We include this here so that you only // Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app. // have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once! // Be sure that you only ever include this mixin once!
@include mat.core(); @include mat.core();
@import './variables.scss';
// Include theme styles for core and each component used in your app. // Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component // Alternatively, you can import and @include the theme mixins for each component
// that you are using. // that you are using.
@include mat.all-component-themes($light-theme); @include mat.all-component-themes(vars.$light-theme);
.dark-theme { .dark-theme {
@include mat.all-component-colors($dark-theme); @include mat.all-component-colors(vars.$dark-theme);
} }
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@@ -31,7 +30,7 @@
a.accent { a.accent {
color: $accent; color: vars.$accent;
} }
.material-icons { .material-icons {
@@ -232,7 +231,7 @@ mat-sidenav-container {
} }
.text-warning { .text-warning {
color: $warn; color: vars.$warn;
} }
.align-right { .align-right {
@@ -268,11 +267,11 @@ mat-card.accent {
} }
mat-card.warn mat-card-header { mat-card.warn mat-card-header {
background-color: $warn !important; background-color: vars.$warn !important;
} }
mat-card.accent mat-card-header { mat-card.accent mat-card-header {
background-color: $accent !important; background-color: vars.$accent !important;
} }
.mat-mdc-list-item-unscoped-content { .mat-mdc-list-item-unscoped-content {