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