upgrade and migrate

This commit is contained in:
_Bastler 2022-12-09 22:04:35 +01:00
parent edecc344e7
commit 92a74e72f9
74 changed files with 4624 additions and 4917 deletions

View File

@ -9,5 +9,6 @@
"latex", "latex",
"plaintext", "plaintext",
"json" "json"
] ],
"angular.enable-strict-mode-prompt": false
} }

7943
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "we-bstly-angular", "name": "we-bstly-angular",
"version": "2.0.0", "version": "2.1.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@ -11,39 +11,38 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular-material-components/datetime-picker": "^8.0.0", "@angular-material-components/datetime-picker": "^9.0.0",
"@angular-material-components/moment-adapter": "^8.0.0", "@angular-material-components/moment-adapter": "^9.0.0",
"@angular/animations": "^14.2.9", "@angular/animations": "^15.0.3",
"@angular/cdk": "^14.2.6", "@angular/cdk": "^15.0.2",
"@angular/common": "^14.2.9", "@angular/common": "^15.0.3",
"@angular/compiler": "^14.2.9", "@angular/compiler": "^15.0.3",
"@angular/core": "^14.2.9", "@angular/core": "^15.0.3",
"@angular/forms": "^14.2.9", "@angular/forms": "^15.0.3",
"@angular/material": "^14.2.7", "@angular/material": "^15.0.2",
"@angular/material-moment-adapter": "^14.2.7", "@angular/material-moment-adapter": "^15.0.2",
"@angular/platform-browser": "^14.2.9", "@angular/platform-browser": "^15.0.3",
"@angular/platform-browser-dynamic": "^14.2.9", "@angular/platform-browser-dynamic": "^15.0.3",
"@angular/router": "^14.2.9", "@angular/router": "^15.0.3",
"moment": "^2.29.4", "moment": "^2.29.4",
"ng-qrcode": "^7.0.0", "ng-qrcode": "^8.0.1",
"ngx-mat-timepicker": "^14.0.6", "ngx-mat-timepicker": "^15.1.2",
"openpgp": "^5.5.0", "openpgp": "^5.5.0",
"qr-scanner": "^1.4.2", "qr-scanner": "^1.4.2",
"rxjs": "~7.5.7", "rxjs": "~7.6.0",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"zone.js": "~0.12.0" "zone.js": "~0.12.0"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^14.2.8", "@angular-devkit/build-angular": "^15.0.3",
"@angular/cli": "^14.2.8", "@angular/cli": "^15.0.3",
"@angular/compiler-cli": "^14.2.9", "@angular/compiler-cli": "^15.0.3",
"@angular/localize": "^14.2.9", "@angular/localize": "^15.0.3",
"@types/jasmine": "^4.3.0", "@types/jasmine": "^4.3.1",
"@types/jasminewd2": "^2.0.10", "@types/jasminewd2": "^2.0.10",
"@types/node": "^18.11.9", "@types/node": "^18.11.12",
"@types/openpgp": "^4.4.18", "@types/openpgp": "^4.4.18",
"codelyzer": "^6.0.2",
"jasmine-core": "~4.5.0", "jasmine-core": "~4.5.0",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"karma": "^6.4.1", "karma": "^6.4.1",
@ -54,6 +53,6 @@
"protractor": "~7.0.0", "protractor": "~7.0.0",
"ts-node": "~10.9.1", "ts-node": "~10.9.1",
"tslint": "~6.1.0", "tslint": "~6.1.0",
"typescript": "~4.8.3" "typescript": "~4.8.4"
} }
} }

View File

@ -33,61 +33,63 @@ import { BorrowRequestsComponent } from './pages/borrow/requests/requests.compon
import { BorrowComponent } from './pages/borrow/borrow.component'; import { BorrowComponent } from './pages/borrow/borrow.component';
import { InviteCodeComponent } from './pages/invites/code/code.component'; import { InviteCodeComponent } from './pages/invites/code/code.component';
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent'; import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component';
const routes: Routes = [ const routes: Routes = [
{ path: 'profile/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] }, { path: 'profile/:username', component: UserComponent, canActivate: [AuthUpdateGuard] },
{ path: 'edit-profile', component: ProfileComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'edit-profile', component: ProfileComponent, canActivate: [AuthenticatedGuard] },
{ path: 'jukebox', component: JukeboxComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'jukebox', component: JukeboxComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey/manage', component: ParteyComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'partey/manage', component: ParteyComponent, canActivate: [AuthenticatedGuard] },
{ {
path: '', component: MainComponent, children: [ path: '', component: MainComponent, children: [
{ path: '', redirectTo: "/services", pathMatch: 'full' }, { path: '', redirectTo: "/services", pathMatch: 'full' },
{ path: 'login', component: FormLoginComponent, canActivate: [ AnonymousGuard ] }, { path: 'login', component: FormLoginComponent, canActivate: [AnonymousGuard] },
{ path: 'login/2fa', component: FormLogin2FAComponent, canActivate: [ AnonymousGuard ] }, { path: 'login/2fa', component: FormLogin2FAComponent, canActivate: [AnonymousGuard] },
{ path: 'service-login', component: FormLoginComponent, canActivate: [ AnonymousGuard ] }, { path: 'login/oidc', component: FormLoginOidcComponent, canActivate: [AuthenticatedGuard] },
{ path: 'service-login/2fa', component: FormLogin2FAComponent, canActivate: [ AnonymousGuard ] }, { path: 'service-login', component: FormLoginComponent, canActivate: [AnonymousGuard] },
{ path: 'password', component: PasswordComponent, canActivate: [ AnonymousGuard ] }, { path: 'service-login/2fa', component: FormLogin2FAComponent, canActivate: [AnonymousGuard] },
{ path: 'password-reset', component: PasswordResetComponent, canActivate: [ AnonymousGuard ] }, { path: 'password', component: PasswordComponent, canActivate: [AnonymousGuard] },
{ path: 'services', component: ServicesComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'password-reset', component: PasswordResetComponent, canActivate: [AnonymousGuard] },
{ path: 'services', component: ServicesComponent, canActivate: [AuthenticatedGuard] },
{ {
path: 'account', component: AccountComponent, canActivate: [ AuthenticatedGuard ], children: [ path: 'account', component: AccountComponent, canActivate: [AuthenticatedGuard], children: [
{ path: '', redirectTo: "/account/info", pathMatch: 'full' }, { path: '', redirectTo: "/account/info", pathMatch: 'full' },
{ path: 'info', component: InfoComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'info', component: InfoComponent, canActivate: [AuthenticatedGuard] },
{ path: 'profile', component: ProfileComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'profile', component: ProfileComponent, canActivate: [AuthenticatedGuard] },
{ path: 'security', component: SecurityComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'security', component: SecurityComponent, canActivate: [AuthenticatedGuard] },
{ path: 'voucher', component: VoucherComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'voucher', component: VoucherComponent, canActivate: [AuthenticatedGuard] },
{ path: 'aliases', component: AliasesComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'aliases', component: AliasesComponent, canActivate: [AuthenticatedGuard] },
{ path: 'domains', component: DomainsComponent, canActivate: [ AuthenticatedGuard ] } { path: 'domains', component: DomainsComponent, canActivate: [AuthenticatedGuard] }
] ]
}, },
{ {
path: 'borrow', component: BorrowComponent, canActivate: [ AuthenticatedGuard ], children: [ path: 'borrow', component: BorrowComponent, canActivate: [AuthenticatedGuard], children: [
{ path: '', redirectTo: "/borrow/items", pathMatch: 'full' }, { path: '', redirectTo: "/borrow/items", pathMatch: 'full' },
{ path: 'items', component: BorrowItemsComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'items', component: BorrowItemsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'requests', component: BorrowRequestsComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'requests', component: BorrowRequestsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'proving', component: BorrowProvingComponent, canActivate: [ AuthenticatedGuard ] } { path: 'proving', component: BorrowProvingComponent, canActivate: [AuthenticatedGuard] }
] ]
}, },
{ path: 'register', component: RegisterComponent, canActivate: [ AnonymousGuard ] }, { path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard] },
{ path: 'tokens', component: TokensComponent, canActivate: [ AuthGuard ] }, { path: 'tokens', component: TokensComponent, canActivate: [AuthGuard] },
{ path: 'jitsi', component: JitsiComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey', component: ParteyComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'partey', component: ParteyComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey/timeslots', component: ParteyTimeslotsComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'partey/timeslots', component: ParteyTimeslotsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey/jukebox', component: JukeboxComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'partey/jukebox', component: JukeboxComponent, canActivate: [AuthenticatedGuard] },
{ path: 'minetest/accounts', component: MinetestAccountsComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'minetest/accounts', component: MinetestAccountsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'dividertest', component: DividertestComponent }, { path: 'dividertest', component: DividertestComponent },
{ path: 'urlshortener', component: UrlShortenerComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard] },
{ path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [ AuthUpdateGuard ] }, { path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard] },
{ path: 'invites/:quota', component: InvitesComponent, canActivate: [ AuthenticatedGuard ] }, { path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard] },
{ path: 'invite/:code', component: InviteCodeComponent, canActivate: [ AuthGuard ] }, { path: 'invite/:code', component: InviteCodeComponent, canActivate: [AuthGuard] },
{ path: 'unavailable', component: UnavailableComponent }, { path: 'unavailable', component: UnavailableComponent },
{ path: 'p/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] }, { path: 'p/:username', component: UserComponent, canActivate: [AuthUpdateGuard] },
{ path: '**', component: NotfoundComponent, pathMatch: 'full', canActivate: [ AuthUpdateGuard ] }, ] { path: '**', component: NotfoundComponent, pathMatch: 'full', canActivate: [AuthUpdateGuard] },]
}, },
]; ];
@NgModule({ @NgModule({
imports: [ RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy' }) ], imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
exports: [ RouterModule ] exports: [RouterModule]
}) })
export class AppRoutingModule { } export class AppRoutingModule { }

View File

@ -63,9 +63,9 @@ import { InviteCodeComponent } from './pages/invites/code/code.component';
import { InviteEditComponent } from './pages/invites/edit/invite.edit'; import { InviteEditComponent } from './pages/invites/edit/invite.edit';
import { MatPaginatorIntl } from '@angular/material/paginator'; import { MatPaginatorIntl } from '@angular/material/paginator';
import { NgxMatDateAdapter, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker'; import { NgxMatDateAdapter, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker';
import { MAT_DATE_LOCALE } from '@angular/material/core';
import { NgxMatMomentAdapter } from '@angular-material-components/moment-adapter'; import { NgxMatMomentAdapter } from '@angular-material-components/moment-adapter';
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent'; import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component';
export function init_app(i18n: I18nService) { export function init_app(i18n: I18nService) {
@ -94,6 +94,7 @@ export class XhrInterceptor implements HttpInterceptor {
LoginComponent, LoginComponent,
FormLoginComponent, FormLoginComponent,
FormLogin2FAComponent, FormLogin2FAComponent,
FormLoginOidcComponent,
TokensComponent, TokensComponent,
InvitesComponent, InviteCodeComponent, InviteEditComponent, InvitesComponent, InviteCodeComponent, InviteEditComponent,
ServicesComponent, ServicesComponent,
@ -140,21 +141,21 @@ export class XhrInterceptor implements HttpInterceptor {
QrCodeModule, QrCodeModule,
], ],
exports: [ MaterialModule ], exports: [MaterialModule],
providers: [ { provide: APP_INITIALIZER, useFactory: init_app, deps: [ I18nService ], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }, DatePipe, providers: [{ provide: APP_INITIALIZER, useFactory: init_app, deps: [I18nService], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }, DatePipe,
{ {
provide: MatPaginatorIntl, useFactory: (i18n) => { provide: MatPaginatorIntl, useFactory: (i18n) => {
const service = new I18nPaginatorIntl(); const service = new I18nPaginatorIntl();
service.injectI18n(i18n) service.injectI18n(i18n)
return service; return service;
}, deps: [ I18nService ] }, deps: [I18nService]
}, },
{ {
provide: NgxMatDateAdapter, provide: NgxMatDateAdapter,
useClass: NgxMatMomentAdapter, useClass: NgxMatMomentAdapter,
useFactory: (i18n: I18nService) => { useFactory: (i18n: I18nService) => {
return new NgxMatMomentAdapter(i18n.getLocale(), { strict: true }); return new NgxMatMomentAdapter(i18n.getLocale(), { strict: true });
}, deps: [ I18nService ] }, deps: [I18nService]
}, { }, {
provide: NGX_MAT_DATE_FORMATS, useFactory: (i18n: I18nService) => { provide: NGX_MAT_DATE_FORMATS, useFactory: (i18n: I18nService) => {
const datetimeformat = i18n.get('format.datetime', []); const datetimeformat = i18n.get('format.datetime', []);
@ -169,9 +170,9 @@ export class XhrInterceptor implements HttpInterceptor {
monthYearA11yLabel: "MMMM YYYY" monthYearA11yLabel: "MMMM YYYY"
} }
}; };
}, deps: [ I18nService ] }, deps: [I18nService]
} ], }],
bootstrap: [ AppComponent ], bootstrap: [AppComponent],
}) })
export class AppModule { export class AppModule {
} }

View File

@ -1,7 +1,7 @@
<h2>{{'greet' | i18n:auth.name}} <mat-icon>sentiment_satisfied_alt</mat-icon> <h2>{{'greet' | i18n:auth.name}} <mat-icon>sentiment_satisfied_alt</mat-icon>
</h2> </h2>
<nav mat-tab-nav-bar> <nav mat-tab-nav-bar [tabPanel]="tabPanel">
<a mat-tab-link routerLink="info" routerLinkActive #rlainfo="routerLinkActive" [active]="rlainfo.isActive">{{'info' <a mat-tab-link routerLink="info" routerLinkActive #rlainfo="routerLinkActive" [active]="rlainfo.isActive">{{'info'
| i18n}}</a> | i18n}}</a>
<a mat-tab-link routerLink="profile" routerLinkActive #rlaprofile="routerLinkActive" <a mat-tab-link routerLink="profile" routerLinkActive #rlaprofile="routerLinkActive"
@ -14,11 +14,12 @@
[active]="rlaaliases.isActive">{{'user.aliases' | i18n}}</a> [active]="rlaaliases.isActive">{{'user.aliases' | i18n}}</a>
<a *ngIf="advancedView" mat-tab-link routerLink="domains" #rladomains="routerLinkActive" routerLinkActive <a *ngIf="advancedView" mat-tab-link routerLink="domains" #rladomains="routerLinkActive" routerLinkActive
[active]="rladomains.isActive">{{'user.domains' | i18n}}</a> [active]="rladomains.isActive">{{'user.domains' | i18n}}</a>
<a mat-tab-link> <a style="align-self: center;">
<mat-slide-toggle [(ngModel)]="advancedView"> <mat-slide-toggle [(ngModel)]="advancedView">
<span *ngIf="!advancedView">{{'account.advanced' | i18n}}</span> <span *ngIf="!advancedView">{{'account.advanced' | i18n}}</span>
</mat-slide-toggle> </mat-slide-toggle>
</a> </a>
</nav> </nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@ -10,8 +10,7 @@
<ng-container matColumnDef="visibility"> <ng-container matColumnDef="visibility">
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
<td mat-cell *matCellDef="let alias"> <td mat-cell *matCellDef="let alias">
<mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)" <mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)">
placeholder="{{'visibility' | i18n}}">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility"> <mat-option *ngFor="let visibility of visibilities" [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}} visibility | i18n}}
@ -28,8 +27,8 @@
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.aliases.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.aliases.delete' | i18n}} </th>
<td mat-cell *matCellDef="let alias" class="text-right"> <td mat-cell *matCellDef="let alias" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="confirmDelete(alias)">
<mat-icon (click)="confirmDelete(alias)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -46,14 +45,15 @@
<div *ngIf="aliasCreation"> <div *ngIf="aliasCreation">
<p>{{'user.aliases.left' | i18n:aliasCreation}}</p> <p>{{'user.aliases.left' | i18n:aliasCreation}}</p>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'user.aliases.alias' | i18n}}" formControlName="alias" <mat-label>{{'user.aliases.alias' | i18n}}</mat-label>
[(ngModel)]="alias.alias" required> <input matInput formControlName="alias" [(ngModel)]="alias.alias" required>
<mat-error> <mat-error>
{{'user.aliases.error' | i18n}} {{'user.aliases.error' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-select [(ngModel)]="alias.visibility" formControlName="visibility" placeholder="{{'visibility' | i18n}}"> <mat-label>{{'visibility' | i18n}}</mat-label>
<mat-select [(ngModel)]="alias.visibility" formControlName="visibility">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility"> <mat-option *ngFor="let visibility of visibilities" [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}} visibility | i18n}}
@ -68,15 +68,16 @@
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="aliasCreation && !working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="aliasCreation && !working" mat-raised-button color="primary"
[disabled]="form.invalid">
{{'user.aliases.create' | i18n}} {{'user.aliases.create' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#aliases" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#aliases" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltipPosition="above" target="_blank" mat-fab color="accent"> matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon> <mat-icon>contact_support</mat-icon>
</a> </a>
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>

View File

@ -2,15 +2,9 @@ mat-form-field {
display: block; display: block;
} }
.mat-header-cell, .mat-mdc-header-cell,
.mat-cell { .mat-mdc-cell {
&.text-right { &.text-right {
text-align: right; text-align: right;
} }
} }
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}

View File

@ -27,8 +27,8 @@
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.domains.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.domains.delete' | i18n}} </th>
<td mat-cell *matCellDef="let domain" class="text-right"> <td mat-cell *matCellDef="let domain" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="confirmDelete(domain)">
<mat-icon (click)="confirmDelete(domain)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -37,28 +37,28 @@
<tr mat-row *matRowDef="let myRowData; columns: domainsColumns"></tr> <tr mat-row *matRowDef="let myRowData; columns: domainsColumns"></tr>
</table> </table>
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm"> <form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm" #domainForm>
<mat-card> <mat-card>
<mat-card-content> <mat-card-content>
<p>{{'user.domains.info' | i18n}}</p> <p>{{'user.domains.info' | i18n}}</p>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'user.domains.domain' | i18n}}" formControlName="domain" <mat-label>{{'user.domains.domain' | i18n}}</mat-label>
[(ngModel)]="domain.domain" required> <input matInput formControlName="domain" [(ngModel)]="domain.domain" required>
<mat-error> <mat-error>
{{'user.domains.error' | i18n}} {{'user.domains.error' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'user.domains.create' | i18n}} {{'user.domains.create' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#domains" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#domains" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltipPosition="above" target="_blank" mat-fab color="accent"> matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon> <mat-icon>contact_support</mat-icon>
</a> </a>
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>

View File

@ -2,19 +2,6 @@ mat-form-field {
display: block; display: block;
} }
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.align-right {
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.secret { .secret {
width: 200px; width: 200px;
max-width: 200px; max-width: 200px;

View File

@ -3,6 +3,6 @@ table {
width: 100%; width: 100%;
} }
td.mat-cell { td.mat-mdc-cell {
padding: 12px; padding: 12px;
} }

View File

@ -3,14 +3,17 @@
{{'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> <qr-code *ngIf="data.qrData" [value]="data.qrData" size="400" errorCorrectionLevel="'M'"
title="{{data.qrData}}"></qr-code>
{{'security.2fa.totp.activate' | i18n}} {{'security.2fa.totp.activate' | i18n}}
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'security.2fa.totp.code' | i18n}}" [formControl]="code" required> <mat-label>{{'security.2fa.totp.code' | i18n}}</mat-label>
<input matInput [formControl]="code" required>
</mat-form-field> </mat-form-field>
</div> </div>
<div mat-dialog-actions> <div mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="code.invalid" mat-raised-button [mat-dialog-close]="code.value" color="accent">{{'security.2fa.totp.enable' | i18n}}</button> <a [disabled]="code.invalid" mat-raised-button [mat-dialog-close]="code.value"
color="accent">{{'security.2fa.totp.enable' | i18n}}</a>
</div> </div>

View File

@ -3,8 +3,8 @@
<mat-card-content> <mat-card-content>
<h2>{{'password.change' | i18n}}</h2> <h2>{{'password.change' | i18n}}</h2>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password.current' | i18n}}" <mat-label>{{'password.current' | i18n}}</mat-label>
formControlName="oldPassword" [(ngModel)]="model.old"> <input matInput type="password" formControlName="oldPassword" [(ngModel)]="model.old">
<mat-error *ngFor="let error of passwordForm.get('oldPassword').errors | keyvalue"> <mat-error *ngFor="let error of passwordForm.get('oldPassword').errors | keyvalue">
{{error.key}} {{error.key}}
</mat-error> </mat-error>
@ -13,15 +13,15 @@
</mat-hint> </mat-hint>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password" <mat-label>{{'password' | i18n}}</mat-label>
[(ngModel)]="model.password"> <input matInput type="password" formControlName="password" [(ngModel)]="model.password">
<mat-error *ngFor="let error of passwordForm.get('password').errors | keyvalue"> <mat-error *ngFor="let error of passwordForm.get('password').errors | keyvalue">
{{error.key}} {{error.key}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" length="6" placeholder="{{'password.confirm' | i18n}}" <mat-label>{{'password.confirm' | i18n}}</mat-label>
formControlName="password2" [(ngModel)]="model.password2"> <input matInput type="password" formControlName="password2" [(ngModel)]="model.password2">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
@ -29,7 +29,7 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> <mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="passwordForm.invalid"> <button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="passwordForm.invalid">
{{'password.change' | i18n}} {{'password.change' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>
@ -42,8 +42,8 @@
<h2>{{'security.status' | i18n}}</h2> <h2>{{'security.status' | i18n}}</h2>
<p> {{'security.status.hint' | i18n}}</p> <p> {{'security.status.hint' | i18n}}</p>
<mat-form-field> <mat-form-field>
<mat-select [(ngModel)]="model.status" formControlName="status" <mat-label>{{'security.status' | i18n}}</mat-label>
placeholder="{{'security.status' | i18n}}"> <mat-select [(ngModel)]="model.status" formControlName="status">
<mat-option *ngFor="let status of statuses" [value]="status"> <mat-option *ngFor="let status of statuses" [value]="status">
{{'security.status.' + status | i18n}} {{'security.status.' + status | i18n}}
</mat-option> </mat-option>
@ -56,13 +56,13 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar> <mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="statusForm.invalid"> <button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="statusForm.invalid">
{{'security.status.change' | i18n}} {{'security.status.change' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#status" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#status" class="help-button"
matTooltipPosition="above" target="_blank" mat-fab color="accent"> matTooltip="{{'help-button' | i18n}}" matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon> <mat-icon>contact_support</mat-icon>
</a> </a>
</mat-card-footer> </mat-card-footer>
@ -75,10 +75,10 @@
<p>{{'security.2fa.info' | i18n}}</p> <p>{{'security.2fa.info' | i18n}}</p>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="!totp" (click)="createTotp()" mat-raised-button color="accent">{{'security.2fa.totp.create' | <a *ngIf="!totp" (click)="createTotp()" mat-raised-button color="accent">{{'security.2fa.totp.create' |
i18n}}</button> i18n}}</a>
<button *ngIf="totp" (click)="removeTotp()" mat-raised-button color="warn">{{'security.2fa.totp.remove' | <a *ngIf="totp" (click)="removeTotp()" mat-raised-button color="warn">{{'security.2fa.totp.remove' |
i18n}}</button> i18n}}</a>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#2fa" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#2fa" class="help-button" matTooltip="{{'help-button' | i18n}}"

View File

@ -3,9 +3,9 @@
<p>{{'vouchers.info' | i18n}}</p> <p>{{'vouchers.info' | i18n}}</p>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngFor="let name of available" mat-raised-button (click)="create(name)" matTooltip="{{'vouchers.' + name + '.text' | i18n}}"> <a *ngFor="let name of available" mat-raised-button (click)="create(name)" matTooltip="{{'vouchers.' + name + '.text' | i18n}}">
{{'vouchers.' + name | i18n}} {{'vouchers.' + name | i18n}}
</button> </a>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#voucher" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/webstly#voucher" class="help-button" matTooltip="{{'help-button' | i18n}}"

View File

@ -8,5 +8,5 @@
{{'vouchers.stored-safely.confirm' | i18n}} {{'vouchers.stored-safely.confirm' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<button mat-button (click)="onOkClick()" [disabled]="!data.confirmClose">{{'ok' | i18n}}</button> <a mat-button (click)="onOkClick()" [disabled]="!data.confirmClose">{{'ok' | i18n}}</a>
</div> </div>

View File

@ -1,6 +1,6 @@
<h2>{{'borrow' | i18n}}</h2> <h2>{{'borrow' | i18n}}</h2>
<nav mat-tab-nav-bar> <nav mat-tab-nav-bar [tabPanel]="tabPanel">
<a mat-tab-link routerLink="items" routerLinkActive="active-tab" #rlaitems="routerLinkActive" <a mat-tab-link routerLink="items" routerLinkActive="active-tab" #rlaitems="routerLinkActive"
[active]="rlaitems.isActive">{{'borrow.items' [active]="rlaitems.isActive">{{'borrow.items'
| i18n}}</a> | i18n}}</a>
@ -11,5 +11,6 @@
[active]="rlaproving.isActive">{{'borrow.proving' [active]="rlaproving.isActive">{{'borrow.proving'
| i18n}}</a> | i18n}}</a>
</nav> </nav>
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel>
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@ -2,28 +2,31 @@
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'borrow.items.name' | i18n}}" formControlName="name"> <mat-label>{{'borrow.items.name' | i18n}}</mat-label>
<input matInput formControlName="name">
<mat-error> <mat-error>
{{'borrow.items.error.name' | i18n}} {{'borrow.items.error.name' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<textarea matInput placeholder="{{'borrow.items.description' | i18n}}" formControlName="description"></textarea> <mat-label>{{'borrow.items.description' | i18n}}</mat-label>
<textarea matInput formControlName="description"></textarea>
<mat-error> <mat-error>
{{'borrow.items.error.description' | i18n}} {{'borrow.items.error.description' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'borrow.items.url' | i18n}}" formControlName="url"> <mat-label>{{'borrow.items.url' | i18n}}</mat-label>
<input matInput formControlName="url">
<mat-error> <mat-error>
{{'borrow.items.error.url' | i18n}} {{'borrow.items.error.url' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="row wrap" fxLayoutGap="24px grid"> <div class="flex wrap" fxLayoutGap="24px grid">
<mat-form-field floatLabel="always" appearance="none" fxFlex="50%" fxFlex.sm="100%" fxFlex.xs="100%"> <mat-form-field floatLabel="always" fxFlex="50%" fxFlex.sm="100%" fxFlex.xs="100%">
<input matInput hidden formControlName="minDuration" /> <input matInput hidden formControlName="minDuration" />
<label>{{'borrow.items.minDuration' | i18n}}</label> <label>{{'borrow.items.minDuration' | i18n}}</label>
<app-durationpicker formControlName="minDuration"></app-durationpicker> <app-durationpicker formControlName="minDuration"></app-durationpicker>
@ -32,7 +35,7 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field floatLabel="always" appearance="none" fxFlex="50%" fxFlex.sm="100%" fxFlex.xs="100%"> <mat-form-field floatLabel="always" fxFlex="50%" fxFlex.sm="100%" fxFlex.xs="100%">
<input matInput hidden formControlName="maxDuration" /> <input matInput hidden formControlName="maxDuration" />
<label>{{'borrow.items.maxDuration' | i18n}}</label> <label>{{'borrow.items.maxDuration' | i18n}}</label>
<app-durationpicker formControlName="maxDuration"></app-durationpicker> <app-durationpicker formControlName="maxDuration"></app-durationpicker>
@ -42,9 +45,10 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div fxLayout="row wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid"> <div class="flex align-center wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid">
<mat-form-field> <mat-form-field>
<mat-select formControlName="availability" placeholder="{{'borrow.items.availability' | i18n}}"> <mat-label>{{'borrow.items.availability' | i18n}}</mat-label>
<mat-select formControlName="availability">
<mat-option value="ALWAYS"> <mat-option value="ALWAYS">
{{'borrow.items.availability.ALWAYS' | i18n}} {{'borrow.items.availability.ALWAYS' | i18n}}
</mat-option> </mat-option>
@ -60,21 +64,16 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field floatLabel="always" appearance="none"> <mat-slide-toggle formControlName="autoAccept">{{'borrow.items.autoAccept' | i18n}}
<mat-slide-toggle formControlName="autoAccept">{{'borrow.items.autoAccept' | i18n}} </mat-slide-toggle>
</mat-slide-toggle>
<input matInput hidden />
</mat-form-field>
<mat-form-field floatLabel="always" appearance="none"> <mat-slide-toggle formControlName="emailNotification">
<mat-slide-toggle formControlName="emailNotification"> {{'borrow.items.emailNotification' | i18n}}
{{'borrow.items.emailNotification' | i18n}} </mat-slide-toggle>
</mat-slide-toggle>
<input matInput hidden />
</mat-form-field>
<mat-form-field *ngIf="form.get('emailNotification').value" fxFlex="1 1 0%"> <mat-form-field *ngIf="form.get('emailNotification').value" fxFlex="1 1 0%">
<input matInput type="email" placeholder="{{'borrow.items.email' | i18n}}" formControlName="email"> <mat-label>{{'borrow.items.email' | i18n}}</mat-label>
<input matInput type="email" formControlName="email">
<mat-error> <mat-error>
{{'borrow.items.error.email' | i18n}} {{'borrow.items.error.email' | i18n}}
</mat-error> </mat-error>
@ -84,14 +83,14 @@
<ng-container *ngIf="form.get('availability').value == 'MANUAL'"> <ng-container *ngIf="form.get('availability').value == 'MANUAL'">
<mat-divider></mat-divider><br> <mat-divider></mat-divider><br>
<label>{{'borrow.items.slot.MANUAL' | i18n}}</label> <label>{{'borrow.items.slot.MANUAL' | i18n}}</label>
<button mat-icon-button (click)="addManualSlot('','','')" title="{{'borrow.items.slot.add' | i18n}}"> <a mat-icon-button (click)="addManualSlot('','','')" title="{{'borrow.items.slot.add' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
</button> </a>
<ng-container *ngFor="let slotForm of slots.controls; let i = index"> <ng-container *ngFor="let slotForm of slots.controls; let i = index">
<div [formGroup]="slotForm" fxLayout="row wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid"> <div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid">
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="slotStartPicker" formControlName="start" <mat-label>{{'borrow.items.slot.start' | i18n}}</mat-label>
placeholder="{{'borrow.items.slot.start' | i18n}}"> <input matInput [ngxMatDatetimePicker]="slotStartPicker" formControlName="start">
<mat-datepicker-toggle matSuffix [for]="slotStartPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="slotStartPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #slotStartPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #slotStartPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -100,8 +99,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="slotEndPicker" formControlName="end" <mat-label>{{'borrow.items.slot.end' | i18n}}</mat-label>
placeholder="{{'borrow.items.slot.end' | i18n}}"> <input matInput [ngxMatDatetimePicker]="slotEndPicker" formControlName="end">
<mat-datepicker-toggle matSuffix [for]="slotEndPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="slotEndPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #slotEndPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #slotEndPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -110,12 +109,12 @@
</mat-form-field> </mat-form-field>
<div> <div>
<button mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}"> <a mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
</button> </a>
<button mat-icon-button (click)="deleteSlot(i)" title="{{'borrow.items.slot.delete' | i18n}}"> <a mat-icon-button (click)="deleteSlot(i)" title="{{'borrow.items.slot.delete' | i18n}}">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </a>
</div> </div>
</div> </div>
</ng-container> </ng-container>
@ -124,13 +123,14 @@
<ng-container *ngIf="form.get('availability').value == 'PERIOD'"> <ng-container *ngIf="form.get('availability').value == 'PERIOD'">
<mat-divider></mat-divider><br> <mat-divider></mat-divider><br>
<label>{{'borrow.items.slot.PERIOD' | i18n}}</label> <label>{{'borrow.items.slot.PERIOD' | i18n}}</label>
<button mat-icon-button (click)="addPeriodSlot('','','','','')" title="{{'borrow.items.slot.add' | i18n}}"> <a mat-icon-button (click)="addPeriodSlot('','','','','')" title="{{'borrow.items.slot.add' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
</button> </a>
<ng-container *ngFor="let slotForm of slots.controls; let i = index"> <ng-container *ngFor="let slotForm of slots.controls; let i = index">
<div [formGroup]="slotForm" fxLayout="row wrap" fxLayoutAlign="start stretch" fxLayoutGap="12px grid"> <div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="12px grid">
<mat-form-field> <mat-form-field>
<mat-select formControlName="startDay" placeholder="{{'borrow.items.slot.startDay' | i18n}}"> <mat-label>{{'borrow.items.slot.startDay' | i18n}}</mat-label>
<mat-select formControlName="startDay">
<mat-option *ngFor="let day of weekdays" [value]="day"> <mat-option *ngFor="let day of weekdays" [value]="day">
{{'borrow.items.slot.day.' + day | i18n}} {{'borrow.items.slot.day.' + day | i18n}}
</mat-option> </mat-option>
@ -141,8 +141,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput format="24" [ngxMatTimepicker]="startTimePicker" formControlName="startTime" <mat-label>{{'borrow.items.slot.startTime' | i18n}}</mat-label>
placeholder="{{'borrow.items.slot.startTime' | i18n}}"> <input matInput format="24" [ngxMatTimepicker]="startTimePicker" formControlName="startTime">
<mat-icon matSuffix (click)="startTimePicker.open()"> <mat-icon matSuffix (click)="startTimePicker.open()">
watch_later watch_later
</mat-icon> </mat-icon>
@ -153,7 +153,8 @@
<ngx-mat-timepicker #startTimePicker></ngx-mat-timepicker> <ngx-mat-timepicker #startTimePicker></ngx-mat-timepicker>
<mat-form-field> <mat-form-field>
<mat-select formControlName="endDay" placeholder="{{'borrow.items.slot.endDay' | i18n}}"> <mat-label>{{'borrow.items.slot.endDay' | i18n}}</mat-label>
<mat-select formControlName="endDay">
<mat-option *ngFor="let day of weekdays" [value]="day"> <mat-option *ngFor="let day of weekdays" [value]="day">
{{'borrow.items.slot.day.' + day | i18n}} {{'borrow.items.slot.day.' + day | i18n}}
</mat-option> </mat-option>
@ -164,8 +165,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput format="24" [ngxMatTimepicker]="endTimePicker" formControlName="endTime" <mat-label>{{'borrow.items.slot.endTime' | i18n}}</mat-label>
placeholder="{{'borrow.items.slot.endTime' | i18n}}"> <input matInput format="24" [ngxMatTimepicker]="endTimePicker" formControlName="endTime">
<mat-icon matSuffix (click)="endTimePicker.open()"> <mat-icon matSuffix (click)="endTimePicker.open()">
watch_later watch_later
</mat-icon> </mat-icon>
@ -176,12 +177,12 @@
<ngx-mat-timepicker #endTimePicker></ngx-mat-timepicker> <ngx-mat-timepicker #endTimePicker></ngx-mat-timepicker>
<div> <div>
<button mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}"> <a mat-icon-button (click)="dublicateSlot(i)" title="{{'borrow.items.slot.dublicate' | i18n}}">
<mat-icon>control_point_duplicate</mat-icon> <mat-icon>control_point_duplicate</mat-icon>
</button> </a>
<button mat-icon-button (click)="deleteSlot(i)" title="{{'borrow.items.slot.delete' | i18n}}"> <a mat-icon-button (click)="deleteSlot(i)" title="{{'borrow.items.slot.delete' | i18n}}">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </a>
</div> </div>
</div> </div>
</ng-container> </ng-container>
@ -189,16 +190,16 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<div fxLayout="row" fxLayoutAlign="space-between start"> <div class="flex" fxLayoutAlign="space-between start">
<div> <div>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent"> <a [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">
<mat-icon>save</mat-icon>{{(create ? 'borrow.items.create' : 'borrow.items.save') | i18n}} <mat-icon>save</mat-icon>{{(create ? 'borrow.items.create' : 'borrow.items.save') | i18n}}
</button> </a>
</div> </div>
<button *ngIf="borrowItemId" mat-button color="warn" (click)="confirmDelete()"> <a *ngIf="borrowItemId" mat-button color="warn" (click)="confirmDelete()">
<mat-icon>delete</mat-icon> {{ 'borrow.items.delete' | <mat-icon>delete</mat-icon> {{ 'borrow.items.delete' |
i18n}} i18n}}
</button> </a>
</div> </div>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -1,16 +1,12 @@
<h3>{{'borrow.items' | i18n}}</h3> <h3>{{'borrow.items' | i18n}}</h3>
<div *ngIf="borrowItems"> <div *ngIf="borrowItems">
<div fxLayout="row" fxLayoutAlign="space-between start"> <div class="flex justify-between">
<form> <mat-form-field>
<mat-form-field> <mat-label>{{'borrow.items.search' | i18n}}</mat-label>
<input matInput [formControl]="searchFormControl" placeholder="{{'borrow.items.search' | i18n}}"> <input matInput [formControl]="searchFormControl">
</mat-form-field> </mat-form-field>
<mat-form-field floatLabel="always" appearance="none"> <mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.items.mine' | i18n}}</mat-slide-toggle>
<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>
<input matInput hidden />
</mat-form-field>
</form>
<button mat-raised-button (click)="create()" color="accent">{{'borrow.items.create' | i18n}}</button>
</div> </div>
<table mat-table matSort [dataSource]="borrowItems.content" (matSortChange)="updateSort($event)"> <table mat-table matSort [dataSource]="borrowItems.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
@ -42,17 +38,17 @@
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.items.actions' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.items.actions' | i18n}} </th>
<td mat-cell *matCellDef="let borrowItem" class="text-right"> <td mat-cell *matCellDef="let borrowItem" class="text-right">
<a mat-icon-button *ngIf="borrowItem.owner == userId"> <a mat-icon-button *ngIf="borrowItem.owner == userId" (click)="request(borrowItem)">
<mat-icon (click)="request(borrowItem)">pending_actions</mat-icon> <mat-icon>pending_actions</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowItem.owner == userId"> <a mat-icon-button *ngIf="borrowItem.owner == userId" (click)="edit(borrowItem)">
<mat-icon (click)="edit(borrowItem)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowItem.owner == userId"> <a mat-icon-button *ngIf="borrowItem.owner == userId" (click)="confirmDelete(borrowItem)">
<mat-icon (click)="confirmDelete(borrowItem)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowItem.owner != userId"> <a mat-icon-button *ngIf="borrowItem.owner != userId" (click)="request(borrowItem)">
<mat-icon (click)="request(borrowItem)">pending_actions</mat-icon> <mat-icon>pending_actions</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>

View File

@ -1,21 +1,8 @@
.mat-form-field+.mat-form-field, .mat-form-field+.mat-slide-toggle { mat-form-field+*, mat-slide-toggle+* {
margin-left: 8px; margin-left: 8px;
} }
.mat-header-cell, .mat-mdc-cell .mat-mdc-button {
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
} }
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}

View File

@ -1,15 +1,16 @@
<video #qrCam></video> <video #qrCam></video>
<form fxLayout="row wrap" fxLayoutGap="24px grid"> <form class="flex wrap" fxLayoutGap="24px grid">
<mat-form-field> <mat-form-field>
<mat-select [formControl]="camera" name="camera" placeholder="{{'borrow.proving.camera' | i18n}}"> <mat-label>{{'borrow.proving.camera' | i18n}}</mat-label>
<mat-select [formControl]="camera" name="camera">
<mat-option *ngFor="let camera of cameras" [value]="camera.id"> <mat-option *ngFor="let camera of cameras" [value]="camera.id">
{{camera.label || camera.id}} {{camera.label || camera.id}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field floatLabel="always" appearance="none"> <mat-form-field floatLabel="always">
<mat-slide-toggle [formControl]="toggleFlash" disabled>{{'borrow.proving.flash' | i18n}}</mat-slide-toggle> <mat-slide-toggle [formControl]="toggleFlash" disabled>{{'borrow.proving.flash' | i18n}}</mat-slide-toggle>
<input matInput hidden name="toggleFlash" /> <input matInput hidden name="toggleFlash" />
</mat-form-field> </mat-form-field>

View File

@ -49,7 +49,7 @@
</table> </table>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button [mat-dialog-close]="false">{{'ok' | i18n}}</button> <a mat-raised-button [mat-dialog-close]="false">{{'ok' | i18n}}</a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</mat-dialog-content> </mat-dialog-content>

View File

@ -14,17 +14,17 @@
<mat-divider></mat-divider> <mat-divider></mat-divider>
<ng-container *ngIf="borrowRequest.id"> <ng-container *ngIf="borrowRequest.id">
<mat-chip-list *ngSwitch="borrowRequest.status"> <mat-chip-listbox *ngSwitch="borrowRequest.status">
<mat-chip *ngSwtichCase="'PENDING'" color="primary" selected></mat-chip> <mat-chip *ngSwtichCase="'PENDING'" color="primary" selected></mat-chip>
<mat-chip color="accent" selected>Accent fish</mat-chip> <mat-chip color="accent" selected>Accent fish</mat-chip>
</mat-chip-list> </mat-chip-listbox>
</ng-container> </ng-container>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="startPicker" formControlName="start" <mat-label>{{'borrow.request.start' | i18n}}</mat-label>
placeholder="{{'borrow.request.start' | i18n}}"> <input matInput [ngxMatDatetimePicker]="startPicker" formControlName="start">
<mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #startPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -33,8 +33,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="endPicker" formControlName="end" <mat-label>{{'borrow.request.end' | i18n}}</mat-label>
placeholder="{{'borrow.request.end' | i18n}}"> <input matInput [ngxMatDatetimePicker]="endPicker" formControlName="end">
<mat-datepicker-toggle matSuffix [for]="endPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="endPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #endPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #endPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -43,7 +43,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<textarea matInput placeholder="{{'borrow.request.comment' | i18n}}" formControlName="comment"></textarea> <mat-label>{{'borrow.request.comment' | i18n}}</mat-label>
<textarea matInput formControlName="comment"></textarea>
<mat-error> <mat-error>
{{'borrow.request.error.comment' | i18n}} {{'borrow.request.error.comment' | i18n}}
</mat-error> </mat-error>
@ -51,16 +52,16 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<div fxLayout="row" fxLayoutAlign="space-between start"> <div class="flex" fxLayoutAlign="space-between start">
<div> <div>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent"> <a [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">
<mat-icon>save</mat-icon>{{(create ? 'borrow.request.create' : 'borrow.request.save') | i18n}} <mat-icon>save</mat-icon>{{(create ? 'borrow.request.create' : 'borrow.request.save') | i18n}}
</button> </a>
</div> </div>
<button *ngIf="borrowRequest.id" mat-button color="warn" (click)="confirmDelete()"> <a *ngIf="borrowRequest.id" mat-button color="warn" (click)="confirmDelete()">
<mat-icon>delete</mat-icon> {{ 'borrow.request.delete' | <mat-icon>delete</mat-icon> {{ 'borrow.request.delete' |
i18n}} i18n}}
</button> </a>
</div> </div>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -1,12 +1,7 @@
<h3>{{'borrow.requests' | i18n}}</h3> <h3>{{'borrow.requests' | i18n}}</h3>
<div *ngIf="borrowRequests"> <div *ngIf="borrowRequests">
<div fxLayout="row" fxLayoutAlign="space-between start"> <div class="flex justify-between">
<form> <mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.requests.mine' | i18n}}</mat-slide-toggle>
<mat-form-field floatLabel="always" appearance="none">
<mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.requests.mine' | i18n}}</mat-slide-toggle>
<input matInput hidden />
</mat-form-field>
</form>
</div> </div>
<table mat-table matSort [dataSource]="borrowRequests.content" (matSortChange)="updateSort($event)"> <table mat-table matSort [dataSource]="borrowRequests.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
@ -35,14 +30,14 @@
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.requests.actions' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.requests.actions' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest" class="text-right"> <td mat-cell *matCellDef="let borrowRequest" class="text-right">
<a mat-icon-button *ngIf="borrowRequest.user == userId"> <a mat-icon-button *ngIf="borrowRequest.user == userId" (click)="edit(borrowRequest)">
<mat-icon (click)="edit(borrowRequest)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowRequest.user == userId"> <a mat-icon-button *ngIf="borrowRequest.user == userId" (click)="confirmDelete(borrowRequest)">
<mat-icon (click)="confirmDelete(borrowRequest)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
<a mat-icon-button *ngIf="borrowRequest.item.owner != userId"> <a mat-icon-button *ngIf="borrowRequest.item.owner != userId" (click)="updateStatus(borrowRequest)">
<mat-icon (click)="updateStatus(borrowRequest)">pending_actions</mat-icon> <mat-icon>pending_actions</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>

View File

@ -1,21 +1,8 @@
.mat-form-field+.mat-form-field, .mat-form-field+.mat-slide-toggle { mat-form-field+mat-form-field, mat-form-field+mat-slide-toggle {
margin-left: 8px; margin-left: 8px;
} }
.mat-header-cell, .mat-mdc-cell .mat-mdc-button {
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
} }
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}

View File

@ -8,19 +8,21 @@
{{'security.2fa.invalid' | i18n}} {{'security.2fa.invalid' | i18n}}
</mat-error> </mat-error>
<input id="provider" name="provider" matInput hidden [value]="selectedProvider.id"> <input id="provider" name="provider" matInput hidden [value]="selectedProvider.id">
<mat-select [(ngModel)]="selectedProvider" placeholder="{{'security.2fa.provider' | i18n}}" <mat-form-field>
[ngModelOptions]="{standalone: true}"> <mat-label>{{'security.2fa.provider' | i18n}}</mat-label>
<mat-option *ngFor="let provider of providers" [value]="provider"> <mat-select [(ngModel)]="selectedProvider" [ngModelOptions]="{standalone: true}">
{{'security.2fa.' + provider.id | i18n}} <mat-option *ngFor="let provider of providers" [value]="provider">
</mat-option> {{'security.2fa.' + provider.id | i18n}}
</mat-select> </mat-option>
</mat-select>
</mat-form-field>
<a mat-raised-button (click)="request()" <a mat-raised-button (click)="request()"
*ngIf="selectedProvider && selectedProvider.request">{{'security.2fa.' + selectedProvider.id + *ngIf="selectedProvider && selectedProvider.request">{{'security.2fa.' + selectedProvider.id +
'.request' '.request'
| i18n}}</a> | i18n}}</a>
<mat-form-field> <mat-form-field>
<input id="code" name="code" matInput placeholder="{{'security.2fa.code' | i18n}}" required <mat-label>{{'security.2fa.code' | i18n}}</mat-label>
matAutofocus> <input id="code" name="code" matInput required matAutofocus>
<mat-error> <mat-error>
{{'security.2fa.missing' | i18n}} {{'security.2fa.missing' | i18n}}
</mat-error> </mat-error>
@ -30,10 +32,10 @@
</mat-slide-toggle> </mat-slide-toggle>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" mat-raised-button color="primary" (click)="form2FA.submit()" <a type="submit" mat-raised-button color="primary" (click)="form2FA.submit()"
[disabled]="form2FA.invalid">{{'security.2fa.login' | i18n}}<mat-icon style="font-size: 1em;"> [disabled]="form2FA.invalid">{{'security.2fa.login' | i18n}}<mat-icon style="font-size: 1em;">
open_in_new open_in_new
</mat-icon></button> </mat-icon></a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</form> </form>

View File

@ -0,0 +1,31 @@
<form [formGroup]="form" action="{{apiUrl}}/oidc/authorize" method="POST">
<mat-card>
<mat-card-content>
<h2>{{'security.oidc.login' | 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="responseType" name="responseType" type="hidden" formControlName="responseType" required>
<input id="redirectUri" name="redirectUri" type="hidden" formControlName="redirectUri" 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">
<mat-list>
<mat-list-item (click)="setAlias('')">{{'username'}}</mat-list-item>
<mat-list-item *ngFor="let userAlias of aliases" (click)="setAlias(userAlias.alias)"
[activated]="userAlias.alias == selectedAlias">{{userAlias.alias}}</mat-list-item>
</mat-list>
</mat-card-content>
<mat-card-actions>
<a type="submit" mat-raised-button color="primary" [disabled]="form.invalid">{{'security.oidc.login' |
i18n}}</a>
</mat-card-actions>
</mat-card>
</form>

View File

@ -0,0 +1,3 @@
mat-form-field {
display: block;
}

View File

@ -0,0 +1,95 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Auth2FAService } from '../../services/auth.2fa.service';
import { Router, ActivatedRoute } from '@angular/router';
import { environment } from '../../../environments/environment';
import { Location } from '@angular/common';
import { UserAliasService } from 'src/app/services/useralias.service';
import { AuthService } from 'src/app/services/auth.service';
@Component({
selector: 'app-login-oidc',
templateUrl: './form-login-oidc.component.html',
styleUrls: ['./form-login-oidc.component.scss']
})
export class FormLoginOidcComponent implements OnInit {
form: FormGroup;
public loginInvalid: boolean;
public apiUrl = environment.apiUrl;
authorize: boolean = false;
alias: boolean = false;
aliases: any[] = [];
selectedAlias: any;
constructor(private formBuilder: FormBuilder, private authService: AuthService, private userAliasService: UserAliasService, private router: Router, private route: ActivatedRoute, private location: Location) { }
ngOnInit() {
this.form = this.formBuilder.group({
code: ['', Validators.required],
state: ['', Validators.required],
client_id: ['', Validators.required],
responseType: ['', Validators.required],
redirectUri: ['', Validators.required],
scope: ['', Validators.required],
nonce: [''],
prompt: [''],
login_hint: ['']
});
this.route.queryParams.subscribe({
next: (params) => {
if (params['code']) {
this.form.get('code').setValue(params['code']);
// this.location.replaceState('/login/oidc')
}
if (params['state']) {
this.form.get('state').setValue(params['state']);
}
if (params['client_id']) {
this.form.get('client_id').setValue(params['client_id']);
}
if (params['responseType']) {
this.form.get('responseType').setValue(params['responseType']);
}
if (params['redirectUri']) {
this.form.get('redirectUri').setValue(params['redirectUri']);
}
if (params['scope']) {
this.form.get('scope').setValue(params['scope']);
}
if (params['nonce']) {
this.form.get('nonce').setValue(params['nonce']);
}
if (params['prompt']) {
this.form.get('prompt').setValue(params['prompt']);
}
if (params['login_hint']) {
this.form.get('login_hint').setValue(params['login_hint']);
}
if (params['authorize']) {
this.authorize = params['authorize'] === 'true';
}
if (params['alias']) {
this.alias = params['alias'] === 'true';
this.userAliasService.get().subscribe({
next: (data: any) => {
this.aliases = data;
}
})
}
}
});
}
setAlias(alias) {
this.authService.setAlias(alias).subscribe({
next: () => {
this.selectedAlias = alias;
}
});
}
}

View File

@ -8,14 +8,15 @@
{{'login.invalid' | i18n}} {{'login.invalid' | i18n}}
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<input id="username" name="username" matInput placeholder="{{'username' | i18n}}" required matAutofocus> <mat-label>{{'username' | i18n}}</mat-label>
<input id="username" name="username" matInput required matAutofocus>
<mat-error> <mat-error>
{{'username.missing' | i18n}} {{'username.missing' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input id="password" name="password" matInput type="password" placeholder="{{'password' | i18n}}" <mat-label>{{'password' | i18n}}</mat-label>
required> <input id="password" name="password" matInput type="password" required>
<mat-error> <mat-error>
{{'password.invalid.hint' | i18n}} {{'password.invalid.hint' | i18n}}
</mat-error> </mat-error>
@ -25,7 +26,8 @@
</mat-slide-toggle> </mat-slide-toggle>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" (click)="loginForm.submit()" mat-raised-button color="primary" [disabled]="loginForm.invalid">{{'login.external' | <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 i18n}}<mat-icon style="font-size: 1em;">open_in_new
</mat-icon></button> </mat-icon></button>
<a routerLink="/password" mat-raised-button color="warn">{{'password.forgot' | i18n}}</a> <a routerLink="/password" mat-raised-button color="warn">{{'password.forgot' | i18n}}</a>

View File

@ -5,7 +5,8 @@
<mat-card-content> <mat-card-content>
<ng-container *ngIf="invite"> <ng-container *ngIf="invite">
<h3> <h3>
<mat-icon inline=true>{{'invites.quota.' + invite.quota + ".icon" | i18n}}</mat-icon> {{'invites.quota.' <mat-icon inline="true">{{'invites.quota.' + invite.quota + ".icon" | i18n}}</mat-icon>
{{'invites.quota.'
+ +
invite.quota invite.quota
| i18n}} | i18n}}
@ -26,18 +27,18 @@
<form [formGroup]="form" *ngIf="!error"> <form [formGroup]="form" *ngIf="!error">
<ng-container *ngIf="!auth.authenticated"> <ng-container *ngIf="!auth.authenticated">
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'username' | i18n}}" formControlName="username" matAutofocus <mat-label>{{'username' | i18n}}</mat-label>
tabindex="1"> <input matInput formControlName="username" matAutofocus tabindex="1">
<mat-error> <mat-error>
{{'username.error' | i18n}} {{'username.error' | i18n}}
</mat-error> </mat-error>
<a mat-button matSuffix mat-icon-button (click)="genUsername()" tabindex="5"> <a mat-icon-button matSuffix (click)="genUsername()" tabindex="5">
<mat-icon>autorenew</mat-icon> <mat-icon>autorenew</mat-icon>
</a> </a>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password' | i18n}}" <mat-label>{{'password' | i18n}}</mat-label>
formControlName="password" tabindex="2"> <input matInput type="password" formControlName="password" tabindex="2">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> <div *ngFor="let error of form.get('password').errors | keyvalue">
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
@ -45,8 +46,8 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" <mat-label>{{'password.confirm' | i18n}}</mat-label>
formControlName="password2" tabindex="3"> <input matInput type="password" formControlName="password2" tabindex="3">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
@ -54,14 +55,15 @@
</ng-container> </ng-container>
<ng-container *ngIf="auth.principal.userId == invite.owner"> <ng-container *ngIf="auth.principal.userId == invite.owner">
<mat-form-field> <mat-form-field>
<textarea matInput placeholder="{{'invite.message' | i18n}}" <mat-label>{{'invite.message' | i18n}}</mat-label>
formControlName="message"></textarea> <textarea matInput formControlName="message"></textarea>
<mat-error> <mat-error>
{{'invite.errors.message' | i18n}} {{'invite.errors.message' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<textarea matInput placeholder="{{'invite.note' | i18n}}" formControlName="note"></textarea> <mat-label>{{'invite.note' | i18n}}</mat-label>
<textarea matInput formControlName="note"></textarea>
<mat-error> <mat-error>
{{'invites.error.note' | i18n}} {{'invites.error.note' | i18n}}
</mat-error> </mat-error>
@ -82,15 +84,15 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<ng-container *ngIf="!success && invite"> <ng-container *ngIf="!success && invite">
<button *ngIf="!working && !auth.authenticated && !error" mat-raised-button color="primary" <a *ngIf="!working && !auth.authenticated && !error" mat-raised-button color="primary"
[disabled]="form.invalid" (click)="register()" tabindex="4"> [disabled]="form.invalid" (click)="register()" tabindex="4">
{{'invites.register' | i18n}} {{'invites.register' | i18n}}
</button> </a>
<button *ngIf="auth.principal.userId == invite.owner && !error" mat-raised-button color="primary" <a *ngIf="auth.principal.userId == invite.owner && !error" mat-raised-button color="primary"
(click)="save()" tabindex="4"> (click)="save()" tabindex="4">
{{'invites.edit.save' | i18n}} {{'invites.edit.save' | i18n}}
</button> </a>
</ng-container> </ng-container>
<ng-container *ngIf="success"> <ng-container *ngIf="success">
<a routerLink="/login" mat-raised-button color="primary"> <a routerLink="/login" mat-raised-button color="primary">

View File

@ -5,13 +5,15 @@
{{'invites.register.error.' + error | i18n}} {{'invites.register.error.' + error | i18n}}
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<textarea matInput placeholder="{{'invite.message' | i18n}}" formControlName="message"></textarea> <mat-label>{{'invite.message' | i18n}}</mat-label>
<mat-error> <textarea matInput formControlName="message"></textarea>
{{'invite.errors.message' | i18n}} <mat-error>
</mat-error> {{'invite.errors.message' | i18n}}
</mat-form-field> </mat-error>
<mat-form-field> </mat-form-field>
<textarea matInput placeholder="{{'invite.note' | i18n}}" formControlName="note" ></textarea> <mat-form-field>
<mat-label>{{'invite.note' | i18n}}</mat-label>
<textarea matInput formControlName="note"></textarea>
<mat-error> <mat-error>
{{'invites.error.note' | i18n}} {{'invites.error.note' | i18n}}
</mat-error> </mat-error>
@ -19,8 +21,8 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button mat-raised-button color="primary" [disabled]="form.invalid || working" (click)="save()"> <a mat-raised-button color="primary" [disabled]="form.invalid || working" (click)="save()">
{{'invites.edit.save' | i18n}} {{'invites.edit.save' | i18n}}
</button> </a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -1,30 +1,33 @@
<h3>{{'invites' | i18n}}</h3> <h3>{{'invites' | i18n}}</h3>
<div *ngIf="invites"> <div *ngIf="invites">
<mat-form-field> <div class="flex">
<input matInput [formControl]="searchFormControl" placeholder="{{'invites.search' | i18n}}"> <mat-form-field>
</mat-form-field> <mat-label>{{'invites.search' | i18n}}</mat-label>
<mat-form-field appearance="fill"> <input matInput [formControl]="searchFormControl">
<mat-label>{{'invites.redeemed.filter' | i18n}}</mat-label> </mat-form-field>
<mat-select [formControl]="redeemedFormControl"> <mat-form-field appearance="fill">
<mat-option>{{'invites.redeemed.filter.none' | i18n}}</mat-option> <mat-label>{{'invites.redeemed.filter' | i18n}}</mat-label>
<mat-option value="true"> <mat-select [formControl]="redeemedFormControl">
<mat-icon>how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}} <mat-option>{{'invites.redeemed.filter.none' | i18n}}</mat-option>
</mat-option> <mat-option value="true">
<mat-option value="false"> <mat-icon>how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}}
<mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} </mat-option>
</mat-option> <mat-option value="false">
<mat-select-trigger [ngSwitch]="redeemedFormControl.value"> <mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
<span *ngSwitchCase="'true'"> </mat-option>
<mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}} <mat-select-trigger [ngSwitch]="redeemedFormControl.value">
</span> <span *ngSwitchCase="'true'">
<span *ngSwitchCase="'false'"> <mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}}
<mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} </span>
</span> <span *ngSwitchCase="'false'">
<span *ngSwitchDefault>{{'invites.redeemed.filter.none' | i18n}}</span> <mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
</mat-select-trigger> </span>
</mat-select> <span *ngSwitchDefault>{{'invites.redeemed.filter.none' | i18n}}</span>
</mat-form-field> </mat-select-trigger>
</mat-select>
</mat-form-field>
</div>
<table mat-table matSort [dataSource]="invites.content"> <table mat-table matSort [dataSource]="invites.content">
<ng-container matColumnDef="starts"> <ng-container matColumnDef="starts">
@ -68,8 +71,8 @@
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> {{'invite.actions' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.actions' | i18n}} </th>
<td mat-cell *matCellDef="let invite"> <td mat-cell *matCellDef="let invite">
<a mat-icon-button> <a mat-icon-button (click)="edit(invite)">
<mat-icon (click)="edit(invite)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -90,40 +93,42 @@
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="inviteQuota && !working" mat-raised-button color="primary" (click)="create()"> <a *ngIf="inviteQuota && !working" mat-raised-button color="primary" (click)="create()">
{{'invite.create' | i18n}} {{'invite.create' | i18n}}
</button> </a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
<div *ngIf="others && others.content"> <div *ngIf="others && others.content">
<h4>{{'invites.others' | i18n}}</h4> <h4>{{'invites.others' | i18n}}</h4>
<mat-form-field> <div class="flex">
<input matInput [formControl]="searchOthersFormControl" placeholder="{{'invites.search' | i18n}}"> <mat-form-field>
</mat-form-field> <mat-label>{{'invites.search' | i18n}}</mat-label>
<mat-form-field appearance="fill"> <input matInput [formControl]="searchOthersFormControl">
<mat-label>{{'invites.redeemed.filter' | i18n}}</mat-label> </mat-form-field>
<mat-select [formControl]="redeemedOthersFormControl"> <mat-form-field appearance="fill">
<mat-option>{{'invites.redeemed.filter.none' | i18n}}</mat-option> <mat-label>{{'invites.redeemed.filter' | i18n}}</mat-label>
<mat-option value="true"> <mat-select [formControl]="redeemedOthersFormControl">
<mat-icon>how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}} <mat-option>{{'invites.redeemed.filter.none' | i18n}}</mat-option>
</mat-option> <mat-option value="true">
<mat-option value="false"> <mat-icon>how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}}
<mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} </mat-option>
</mat-option> <mat-option value="false">
<mat-select-trigger [ngSwitch]="redeemedOthersFormControl.value"> <mat-icon>person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
<span *ngSwitchCase="'true'"> </mat-option>
<mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}} <mat-select-trigger [ngSwitch]="redeemedOthersFormControl.value">
</span> <span *ngSwitchCase="'true'">
<span *ngSwitchCase="'false'"> <mat-icon inline="true">how_to_reg</mat-icon>{{'invites.redeemed.filter.true' | i18n}}
<mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}} </span>
</span> <span *ngSwitchCase="'false'">
<span *ngSwitchDefault>{{'invites.redeemed.filter.none' | i18n}}</span> <mat-icon inline="true">person_remove</mat-icon>{{'invites.redeemed.filter.false' | i18n}}
</mat-select-trigger> </span>
</mat-select> <span *ngSwitchDefault>{{'invites.redeemed.filter.none' | i18n}}</span>
</mat-form-field> </mat-select-trigger>
</mat-select>
</mat-form-field>
</div>
<table mat-table matSort [dataSource]="others.content"> <table mat-table matSort [dataSource]="others.content">
<ng-container matColumnDef="note"> <ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>

View File

@ -1,3 +1,3 @@
.mat-form-field+.mat-form-field { mat-form-field+mat-form-field {
margin-left: 8px; margin-left: 8px;
} }

View File

@ -6,29 +6,31 @@
<ng-container matColumnDef="share"> <ng-container matColumnDef="share">
<th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom"> <td mat-cell *matCellDef="let jitsiRoom">
<button mat-icon-button (click)="share(jitsiRoom)"> <a mat-icon-button (click)="share(jitsiRoom)">
<mat-icon>share</mat-icon> <mat-icon>share</mat-icon>
</button> </a>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="room"> <ng-container matColumnDef="room">
<th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom"> <td mat-cell *matCellDef="let jitsiRoom">
<a *ngIf="open(jitsiRoom, false)" mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank"> <div class="flex align-center">
{{ jitsiRoom.room }} <a *ngIf="open(jitsiRoom, false)" mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank">
<mat-icon style="font-size: 1em;">open_in_new {{ jitsiRoom.room }}
</mat-icon> <mat-icon style="font-size: 1em;">open_in_new</mat-icon>
</a> </a>
<a *ngIf="!open(jitsiRoom, false)" mat-button matTooltip="{{'jitsi.rooms.notStarted' | i18n}}" (click)="copyRoomUrlToClipboard(jitsiRoom)"> <a *ngIf="!open(jitsiRoom, false)" mat-button matTooltip="{{'jitsi.rooms.notStarted' | i18n}}"
{{ jitsiRoom.room }} (click)="copyRoomUrlToClipboard(jitsiRoom)">
</a> {{ jitsiRoom.room }}
</a>
<a *ngIf="!jitsiRoom.code && shortenedUrlQuota" mat-icon-button (click)="createShortenedUrl(jitsiRoom)" inline <a *ngIf="!jitsiRoom.code && shortenedUrlQuota" mat-icon-button (click)="createShortenedUrl(jitsiRoom)" inline
matTooltip="{{'urlshortener.create' | i18n}}"> matTooltip="{{'urlshortener.create' | i18n}}">
<mat-icon>add_link</mat-icon> <mat-icon>add_link</mat-icon>
</a> </a>
</div>
</td> </td>
</ng-container> </ng-container>
@ -68,8 +70,8 @@
<ng-container matColumnDef="edit"> <ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'jitsi.rooms.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'jitsi.rooms.edit' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right"> <td mat-cell *matCellDef="let jitsiRoom" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="edit(jitsiRoom)">
<mat-icon (click)="edit(jitsiRoom)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -77,8 +79,8 @@
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right"> <td mat-cell *matCellDef="let jitsiRoom" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="confirmDelete(jitsiRoom)">
<mat-icon (click)="confirmDelete(jitsiRoom)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -99,16 +101,17 @@
<div *ngIf="jitsiRoomsQuota"> <div *ngIf="jitsiRoomsQuota">
<p>{{'jitsi.rooms.left' | i18n:jitsiRoomsQuota}}</p> <p>{{'jitsi.rooms.left' | i18n:jitsiRoomsQuota}}</p>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'jitsi.rooms.room' | i18n}}" formControlName="room" <mat-label>{{'jitsi.rooms.room' | i18n}}</mat-label>
[(ngModel)]="jitsiRoom.room" required pattern="[a-zA-Z0-9]+"> <input matInput formControlName="room" [(ngModel)]="jitsiRoom.room" required pattern="[a-zA-Z0-9]+">
<mat-error> <mat-error>
{{'jitsi.rooms.error.room' | i18n}} {{'jitsi.rooms.error.room' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts" <mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label>
placeholder="{{'jitsi.rooms.starts' | i18n}}" (dateChange)="clearModeration(jitsiRoom)"> <input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts"
(dateChange)="clearModeration(jitsiRoom)">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -118,7 +121,8 @@
<mat-form-field *ngIf="jitsiRoom.starts"> <mat-form-field *ngIf="jitsiRoom.starts">
<input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts" <input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts"
formControlName="moderationStarts" placeholder="{{'jitsi.rooms.moderationStarts' | i18n}}"> formControlName="moderationStarts">
<mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -128,7 +132,8 @@
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires" <input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires"
formControlName="expires" placeholder="{{'jitsi.rooms.expires' | i18n}}"> formControlName="expires">
<mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -139,15 +144,16 @@
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="jitsiRoomsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="jitsiRoomsQuota && !working" mat-raised-button color="primary"
[disabled]="form.invalid">
{{'jitsi.rooms.create' | i18n}} {{'jitsi.rooms.create' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/jitsi#rooms" class="help-button" <a href="https://wiki.bstly.de/services/jitsi#rooms" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltip="{{'help-button' | i18n}}" matTooltipPosition="above" target="_blank" mat-fab color="accent"> matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon> <mat-icon>contact_support</mat-icon>
</a> </a>
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>

View File

@ -2,24 +2,10 @@ mat-form-field {
display: block; display: block;
} }
.mat-header-cell, .mat-mdc-cell .mat-mdc-button {
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
} }
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.url { .url {
display: inline-block; display: inline-block;
width: 200px; width: 200px;

View File

@ -4,16 +4,17 @@
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'jitsi.rooms.room' | i18n}}" formControlName="room" [(ngModel)]="jitsiRoom.room" <mat-label>{{'jitsi.rooms.room' | i18n}}</mat-label>
required pattern="[a-zA-Z0-9]+"> <input matInput formControlName="room" [(ngModel)]="jitsiRoom.room" required pattern="[a-zA-Z0-9]+">
<mat-error> <mat-error>
{{'jitsi.rooms.error.room' | i18n}} {{'jitsi.rooms.error.room' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts" <input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts"
placeholder="{{'jitsi.rooms.starts' | i18n}}" (dateChange)="clearModeration(jitsiRoom)"> (dateChange)="clearModeration(jitsiRoom)">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -23,7 +24,8 @@
<mat-form-field *ngIf="jitsiRoom.starts"> <mat-form-field *ngIf="jitsiRoom.starts">
<input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts" <input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts"
formControlName="moderationStarts" placeholder="{{'jitsi.rooms.moderationStarts' | i18n}}"> formControlName="moderationStarts">
<mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -32,8 +34,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires" formControlName="expires" <input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires" formControlName="expires">
placeholder="{{'jitsi.rooms.expires' | i18n}}"> <mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -43,7 +45,7 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">{{ 'jitsi.rooms.save' | <a [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">{{ 'jitsi.rooms.save' |
i18n}}</button> i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -29,5 +29,5 @@
</mat-list> </mat-list>
</div> </div>
<div mat-dialog-actions> <div mat-dialog-actions>
<button mat-button mat-dialog-close>{{'close' | i18n}}</button> <a mat-button mat-dialog-close>{{'close' | i18n}}</a>
</div> </div>

View File

@ -19,15 +19,16 @@
<mat-divider></mat-divider> <mat-divider></mat-divider>
<br /> <br />
<mat-form-field> <mat-form-field>
<input matInput [formControl]="searchFormControl" placeholder="{{'jukebox.search' | i18n}}"> <mat-label>{{'jukebox.search' | i18n}}</mat-label>
<input matInput [formControl]="searchFormControl" >
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button type="submit" <a mat-raised-button type="submit"
[disabled]="!searchFormControl.value || timeout > 0 || searchDisabled"> [disabled]="!searchFormControl.value || timeout > 0 || searchDisabled">
<mat-icon inline="true">{{'jukebox.search.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'jukebox.search.icon' | i18n}}</mat-icon>
{{'jukebox.search.submit' | i18n}} {{'jukebox.search.submit' | i18n}}
</button> </a>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a (click)="check()" class="help-button" mat-fab color="accent"> <a (click)="check()" class="help-button" mat-fab color="accent">
@ -62,7 +63,7 @@
<mat-card *ngIf="searchResult && ((searchResult.offset + searchResult.limit) < searchResult.total)"> <mat-card *ngIf="searchResult && ((searchResult.offset + searchResult.limit) < searchResult.total)">
<mat-card-actions> <mat-card-actions>
<button mat-button (click)="searchMore()"> {{'jukebox.search.more' | i18n}}</button> <a mat-button (click)="searchMore()"> {{'jukebox.search.more' | i18n}}</a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </div>

View File

@ -6,7 +6,8 @@
{{'security.2fa.totp.invalid' | i18n}} {{'security.2fa.totp.invalid' | i18n}}
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<input id="code" name="code" matInput placeholder="{{'security.2fa.totp.code' | i18n}}" formControlName="code" <mat-label>{{'security.2fa.totp.code' | i18n}}</mat-label>
<input id="code" name="code" matInput formControlName="code"
required matAutofocus> required matAutofocus>
<mat-error> <mat-error>
{{'security.2fa.totp.missing' | i18n}} {{'security.2fa.totp.missing' | i18n}}
@ -17,8 +18,8 @@
</mat-slide-toggle> </mat-slide-toggle>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" mat-raised-button color="primary" <a type="submit" mat-raised-button color="primary"
[disabled]="form.invalid">{{'security.2fa.totp.login' | i18n}}</button> [disabled]="form.invalid">{{'security.2fa.totp.login' | i18n}}</a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</form> </form>

View File

@ -6,7 +6,7 @@ import { Router, ActivatedRoute } from '@angular/router';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
@Component({ @Component({
selector: 'app-login', selector: 'app-login-totp',
templateUrl: './login-totp.component.html', templateUrl: './login-totp.component.html',
styleUrls: [ './login-totp.component.scss' ] styleUrls: [ './login-totp.component.scss' ]
}) })

View File

@ -6,14 +6,16 @@
{{'login.invalid' | i18n}} {{'login.invalid' | i18n}}
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<input id="username" name="username" matInput placeholder="{{'username' | i18n}}" <mat-label>{{'username' | i18n}}</mat-label>
<input id="username" name="username" matInput
formControlName="username" required matAutofocus> formControlName="username" required matAutofocus>
<mat-error> <mat-error>
{{'username.missing' | i18n}} {{'username.missing' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input id="password" name="password" matInput type="password" placeholder="{{'password' | i18n}}" <mat-label>{{'password' | i18n}}</mat-label>
<input id="password" name="password" matInput type="password"
formControlName="password" required> formControlName="password" required>
<mat-error> <mat-error>
{{'password.invalid.hint' | i18n}} {{'password.invalid.hint' | i18n}}
@ -24,7 +26,7 @@
</mat-slide-toggle> </mat-slide-toggle>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" mat-raised-button color="primary" <button type="submit" type="submit" mat-raised-button color="primary"
[disabled]="form.invalid">{{'login' | i18n}}</button> [disabled]="form.invalid">{{'login' | i18n}}</button>
<a routerLink="/password" mat-raised-button color="warn">{{'password.forgot' | i18n}}</a> <a routerLink="/password" mat-raised-button color="warn">{{'password.forgot' | i18n}}</a>
</mat-card-actions> </mat-card-actions>

View File

@ -12,11 +12,11 @@
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'minetest.accounts.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'minetest.accounts.delete' | i18n}} </th>
<td mat-cell *matCellDef="let minetestAccount" class="text-right"> <td mat-cell *matCellDef="let minetestAccount" class="text-right">
<a mat-icon-button color="warn"> <a mat-icon-button color="warn" matTooltip="{{'minetest.accounts.deletion' | i18n}}">
<mat-icon matTooltip="{{'minetest.accounts.deletion' | i18n}}">warning</mat-icon> <mat-icon>warning</mat-icon>
</a> </a>
<a mat-icon-button> <a mat-icon-button (click)="confirmDelete(minetestAccount.name)">
<mat-icon (click)="confirmDelete(minetestAccount.name)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -33,7 +33,8 @@
<div *ngIf="minetestAccountsQuota"> <div *ngIf="minetestAccountsQuota">
<p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p> <p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'minetest.accounts.name' | i18n}}" formControlName="name" <mat-label>{{'minetest.accounts.name' | i18n}}</mat-label>
<input matInput formControlName="name"
[(ngModel)]="minetestAccount.name" required pattern="[-_a-zA-Z0-9]+"> [(ngModel)]="minetestAccount.name" required pattern="[-_a-zA-Z0-9]+">
<mat-error> <mat-error>
{{'minetest.accounts.error.name' | i18n}} {{'minetest.accounts.error.name' | i18n}}
@ -42,7 +43,7 @@
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="minetestAccountsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="minetestAccountsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'minetest.accounts.create' | i18n}} {{'minetest.accounts.create' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>

View File

@ -2,24 +2,11 @@ mat-form-field {
display: block; display: block;
} }
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button { .mat-cell .mat-button {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
} }
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.url { .url {
display: inline-block; display: inline-block;
width: 200px; width: 200px;

View File

@ -2,7 +2,7 @@
<p *ngIf="!tags || tags.length == 0">{{'partey.tags.none' | i18n}}</p> <p *ngIf="!tags || tags.length == 0">{{'partey.tags.none' | i18n}}</p>
<mat-chip-list> <mat-chip-listbox>
<ng-container *ngFor="let tag of tags"> <ng-container *ngFor="let tag of tags">
<mat-chip *ngIf="activeTag(tag)" color="accent" selected matTooltip="{{ 'partey.tag.' + tag.tag + '.hint' | i18nEmpty}}"> <mat-chip *ngIf="activeTag(tag)" color="accent" selected matTooltip="{{ 'partey.tag.' + tag.tag + '.hint' | i18nEmpty}}">
{{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip> {{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip>
@ -12,7 +12,7 @@
matTooltip="{{'partey.tags.expires' | i18n:(tag.expires | datef)}}" selected> matTooltip="{{'partey.tags.expires' | i18n:(tag.expires | datef)}}" selected>
{{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip> {{ 'partey.tag.' + tag.tag | i18nEmpty}}</mat-chip>
</ng-container> </ng-container>
</mat-chip-list> </mat-chip-listbox>
<app-partey-timeslots></app-partey-timeslots> <app-partey-timeslots></app-partey-timeslots>

View File

@ -1,24 +1,26 @@
<h1 mat-dialog-title> <h1 mat-dialog-title>
<mat-icon inline="true">{{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon> {{(timeslot.id ? 'partey.timeslots.edit' : 'partey.timeslots.create.' + <mat-icon inline="true">{{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon> {{(timeslot.id ?
'partey.timeslots.edit' : 'partey.timeslots.create.' +
timeslot.type) | i18n}} timeslot.type) | i18n}}
</h1> </h1>
<mat-dialog-content> <mat-dialog-content>
<h3 *ngIf="timeslot.id">{{'partey.timeslots.type.' + timeslot.type | i18n}}</h3> <h3 *ngIf="timeslot.id">{{'partey.timeslots.type.' + timeslot.type | i18n}}</h3>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field *ngIf="timeslot.type == 'VIDEO' || timeslot.type == 'AUDIO'"> <mat-form-field *ngIf="timeslot.type == 'VIDEO' || timeslot.type == 'AUDIO'">
<input matInput [(ngModel)]="timeslot.share" formControlName="share" <mat-label>{{'partey.timeslots.share.placeholder' | i18n}}</mat-label>
placeholder="{{'partey.timeslots.share.placeholder' | i18n}}"> <input matInput [(ngModel)]="timeslot.share" formControlName="share">
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="timeslot.type == 'VIDEO_STREAM'"> <mat-form-field *ngIf="timeslot.type == 'VIDEO_STREAM'">
<input matInput [(ngModel)]="timeslot.stream" formControlName="stream" <mat-label>{{'partey.timeslots.stream' | i18n}}</mat-label>
placeholder="{{'partey.timeslots.stream' | i18n}}"> <input matInput [(ngModel)]="timeslot.stream" formControlName="stream">
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="timeslot.type == 'AUDIO_STREAM' && timeslot.secret"> <mat-form-field *ngIf="timeslot.type == 'AUDIO_STREAM' && timeslot.secret">
<textarea [mat-autosize] matInput [value]="timeslot.secret" readonly placeholder="{{'partey.timeslots.secret' | i18n}}"></textarea> <mat-label>{{'partey.timeslots.secret' | i18n}}</mat-label>
<textarea [mat-autosize] matInput [value]="timeslot.secret" readonly></textarea>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="timeslot.starts" formControlName="starts" <mat-label>{{'partey.timeslots.starts' | i18n}}</mat-label>
placeholder="{{'partey.timeslots.starts' | i18n}}"> <input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="timeslot.starts" formControlName="starts">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -26,8 +28,8 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="endsPicker" [(ngModel)]="timeslot.ends" formControlName="ends" <mat-label>{{'partey.timeslots.ends' | i18n}}</mat-label>
placeholder="{{'partey.timeslots.ends' | i18n}}"> <input matInput [ngxMatDatetimePicker]="endsPicker" [(ngModel)]="timeslot.ends" formControlName="ends">
<mat-datepicker-toggle matSuffix [for]="endsPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="endsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #endsPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #endsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -35,18 +37,18 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [(ngModel)]="timeslot.title" formControlName="title" <mat-label>{{'partey.timeslots.title' | i18n}}</mat-label>
placeholder="{{'partey.timeslots.title' | i18n}}"> <input matInput [(ngModel)]="timeslot.title" formControlName="title">
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<textarea matInput [(ngModel)]="timeslot.description" formControlName="description" <mat-label>{{'partey.timeslots.description' | i18n}}</mat-label>
placeholder="{{'partey.timeslots.description' | i18n}}"></textarea> <textarea matInput [(ngModel)]="timeslot.description" formControlName="description"></textarea>
</mat-form-field> </mat-form-field>
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="form.invalid" mat-raised-button (click)="timeslot.id ? save(timeslot) : create(timeslot)" <a [disabled]="form.invalid" mat-raised-button (click)="timeslot.id ? save(timeslot) : create(timeslot)"
color="accent">{{ (timeslot.id ? 'partey.timeslots.save' : 'partey.timeslots.create') | color="accent">{{ (timeslot.id ? 'partey.timeslots.save' : 'partey.timeslots.create') |
i18n}}</button> i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -1,39 +1,42 @@
<h3>{{'partey.timeslots' | i18n}}</h3> <h3>{{'partey.timeslots' | i18n}}</h3>
<div *ngIf="timeslots"> <div *ngIf="timeslots">
<mat-form-field> <div class="flex">
<input matInput [formControl]="searchFormControl" placeholder="{{'partey.timeslots.filter.search' | i18n}}"> <mat-form-field>
</mat-form-field> <mat-label>{{'partey.timeslots.filter.search' | i18n}}</mat-label>
<mat-form-field appearance="fill"> <input matInput [formControl]="searchFormControl">
<mat-label>{{'partey.timeslots.filter.owner' | i18n}}</mat-label> </mat-form-field>
<mat-select [formControl]="ownerFormControl"> <mat-form-field appearance="fill">
<mat-option value="">{{'partey.timeslots.filter.owner.mine' | i18n}}</mat-option> <mat-label>{{'partey.timeslots.filter.owner' | i18n}}</mat-label>
<mat-option value="others">{{'partey.timeslots.filter.owner.others' | i18n}} </mat-option> <mat-select [formControl]="ownerFormControl">
<mat-option value="all">{{'partey.timeslots.filter.owner.all' | i18n}} </mat-option> <mat-option value="">{{'partey.timeslots.filter.owner.mine' | i18n}}</mat-option>
</mat-select> <mat-option value="others">{{'partey.timeslots.filter.owner.others' | i18n}} </mat-option>
</mat-form-field> <mat-option value="all">{{'partey.timeslots.filter.owner.all' | i18n}} </mat-option>
<mat-form-field> </mat-select>
<mat-label>{{'partey.timeslots.filter.type' | i18n}}</mat-label> </mat-form-field>
<mat-select [formControl]="typeFormControl"> <mat-form-field>
<mat-option value="">{{'partey.timeslots.filter.type.all' | i18n}}</mat-option> <mat-label>{{'partey.timeslots.filter.type' | i18n}}</mat-label>
<mat-option *ngFor="let type of types" [value]="type"> <mat-select [formControl]="typeFormControl">
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon> <mat-option value="">{{'partey.timeslots.filter.type.all' | i18n}}</mat-option>
{{'partey.timeslots.filter.type.' + type | i18n}} <mat-option *ngFor="let type of types" [value]="type">
</mat-option> <mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
<mat-select-trigger> {{'partey.timeslots.filter.type.' + type | i18n}}
<mat-icon inline="true" *ngIf="typeFormControl.value ">{{'partey.timeslots.type.' + typeFormControl.value + </mat-option>
'.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ? typeFormControl.value <mat-select-trigger>
: 'all') | i18n}} <mat-icon inline="true" *ngIf="typeFormControl.value ">{{'partey.timeslots.type.' + typeFormControl.value +
</mat-select-trigger> '.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ?
</mat-select> typeFormControl.value
</mat-form-field> : 'all') | i18n}}
<mat-form-field> </mat-select-trigger>
<input matInput [ngxMatDatetimePicker]="afterPicker" [formControl]="afterFormControl" </mat-select>
placeholder="{{'partey.timeslots.filter.after' | i18n}}"> </mat-form-field>
<mat-datepicker-toggle matSuffix [for]="afterPicker"></mat-datepicker-toggle> <mat-form-field>
<ngx-mat-datetime-picker #afterPicker></ngx-mat-datetime-picker> <mat-label>{{'partey.timeslots.filter.after' | i18n}}</mat-label>
</mat-form-field> <input matInput [ngxMatDatetimePicker]="afterPicker" [formControl]="afterFormControl">
<mat-datepicker-toggle matSuffix [for]="afterPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #afterPicker></ngx-mat-datetime-picker>
</mat-form-field>
</div>
<table mat-table matSort [dataSource]="timeslots.content" (matSortChange)="updateSort($event)"> <table mat-table matSort [dataSource]="timeslots.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="starts"> <ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th>
@ -79,8 +82,8 @@
</th> </th>
<td mat-cell *matCellDef="let timeslot"> <td mat-cell *matCellDef="let timeslot">
<div *ngIf="timeslot.owner == userId"> <div *ngIf="timeslot.owner == userId">
<button *ngIf="timeslot.type == 'AUDIO_STREAM'" mat-raised-button <a *ngIf="timeslot.type == 'AUDIO_STREAM'" mat-raised-button
(click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</button> (click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</a>
<span *ngIf="timeslot.type == 'VIDEO_STREAM'"> {{ timeslot.stream }} </span> <span *ngIf="timeslot.type == 'VIDEO_STREAM'"> {{ timeslot.stream }} </span>
</div> </div>
</td> </td>
@ -89,8 +92,8 @@
<ng-container matColumnDef="edit"> <ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right"> <td mat-cell *matCellDef="let timeslot" class="text-right">
<a *ngIf="timeslot.owner == userId" mat-icon-button> <a *ngIf="timeslot.owner == userId" mat-icon-button (click)="openEdit(timeslot)">
<mat-icon (click)="openEdit(timeslot)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -98,8 +101,8 @@
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right"> <td mat-cell *matCellDef="let timeslot" class="text-right">
<a *ngIf="timeslot.owner == userId" mat-icon-button> <a *ngIf="timeslot.owner == userId" mat-icon-button (click)="confirmDelete(timeslot)">
<mat-icon (click)="confirmDelete(timeslot)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -120,12 +123,12 @@
<p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p> <p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p>
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions *ngIf="timeslotsQuota"> <mat-card-actions *ngIf="timeslotsQuota" class="flex wrap">
<div *ngFor="let type of types"> <div *ngFor="let type of types">
<button mat-raised-button (click)="openCreate(type)"> <a mat-raised-button (click)="openCreate(type)">
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
{{'partey.timeslots.create.' + type | i18n}} {{'partey.timeslots.create.' + type | i18n}}
</button> </a>
<br> <br>
<br> <br>
</div> </div>

View File

@ -1,21 +1,7 @@
.mat-header-cell, mat-cell mat-button {
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
} }
mat-form-field+mat-form-field {
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.mat-form-field+.mat-form-field {
margin-left: 8px; margin-left: 8px;
} }

View File

@ -6,14 +6,16 @@
{{'password.reset.tokenInvalid' | i18n}} {{'password.reset.tokenInvalid' | i18n}}
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password" <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password"
[(ngModel)]="model.password"> [(ngModel)]="model.password">
<mat-error *ngFor="let error of form.get('password').errors | keyvalue"> <mat-error *ngFor="let error of form.get('password').errors | keyvalue">
{{error.key}} {{error.key}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" length="6" placeholder="{{'password.confirm' | i18n}}" <mat-label>{{'password.confirm' | i18n}}</mat-label>
<input matInput type="password" length="6"
formControlName="password2" [(ngModel)]="model.password2"> formControlName="password2" [(ngModel)]="model.password2">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
@ -21,7 +23,7 @@
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'password.reset' | i18n}} {{'password.reset' | i18n}}
</button> </button>

View File

@ -3,7 +3,8 @@
<mat-card-content> <mat-card-content>
<h2>{{'password.request' | i18n}}</h2> <h2>{{'password.request' | i18n}}</h2>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'username' | i18n}}" formControlName="username" [(ngModel)]="model.username"> <mat-label>{{'username' | i18n}}</mat-label>
<input matInput formControlName="username" [(ngModel)]="model.username">
<mat-error> <mat-error>
{{'username.missing' | i18n}} {{'username.missing' | i18n}}
</mat-error> </mat-error>
@ -11,12 +12,13 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'pgp.privateKey' | i18n}}</mat-label> <mat-label>{{'pgp.privateKey' | i18n}}</mat-label>
<textarea matInput formControlName="privateKey" placeholder="Private Key" <mat-label>Private Key</mat-label>
<textarea matInput formControlName="privateKey"
[(ngModel)]="model.privateKey"></textarea> [(ngModel)]="model.privateKey"></textarea>
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'password.request' | i18n}} {{'password.request' | i18n}}
</button> </button>

View File

@ -18,17 +18,19 @@
color="warn">{{'register.token.locked.action' | i18n}}</a> color="warn">{{'register.token.locked.action' | i18n}}</a>
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'username' | i18n}}" formControlName="username" <mat-label>{{'username' | i18n}}</mat-label>
<input matInput formControlName="username"
[(ngModel)]="model.username" required matAutofocus tabindex="1"> [(ngModel)]="model.username" required matAutofocus tabindex="1">
<mat-error> <mat-error>
{{'username.error' | i18n}} {{'username.error' | i18n}}
</mat-error> </mat-error>
<a mat-button matSuffix mat-icon-button (click)="genUsername()" tabindex="8"> <a mat-icon-button matSuffix (click)="genUsername()" tabindex="8">
<mat-icon>autorenew</mat-icon> <mat-icon>autorenew</mat-icon>
</a> </a>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password" <mat-label>{{'password' | i18n}}</mat-label>
<input matInput type="password" formControlName="password"
[(ngModel)]="model.password" required tabindex="2"> [(ngModel)]="model.password" required tabindex="2">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> <div *ngFor="let error of form.get('password').errors | keyvalue">
@ -37,7 +39,8 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" formControlName="password2" <mat-label>{{'password.confirm' | i18n}}</mat-label>
<input matInput type="password" formControlName="password2"
[(ngModel)]="model.password2" required tabindex="3"> [(ngModel)]="model.password2" required tabindex="3">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
@ -52,7 +55,8 @@
matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon> matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon>
<mat-form-field *ngIf="model.primaryEmail"> <mat-form-field *ngIf="model.primaryEmail">
<input matInput type="email" placeholder="{{'email' | i18n}}" formControlName="email" <mat-label>{{'email' | i18n}}</mat-label>
<input matInput type="email" formControlName="email"
[(ngModel)]="model.email" required tabindex="5"> [(ngModel)]="model.email" required tabindex="5">
<mat-error> <mat-error>
{{'email.invalid' | i18n}} {{'email.invalid' | i18n}}
@ -69,7 +73,7 @@
<mat-divider></mat-divider> <mat-divider></mat-divider>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid" tabindex="6"> <button type="submit" *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid" tabindex="6">
{{'register' | i18n}} {{'register' | i18n}}
</button> </button>

View File

@ -16,9 +16,9 @@
i18n}}</a> i18n}}</a>
<a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' | <a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' |
i18n}}</a> i18n}}</a>
<button mat-icon-button #privateKeyHelp="matTooltip" (click)="privateKeyHelp.toggle()" [matTooltip]="'pgp.privateKey.help' | i18n" matTooltipPosition="after"> <a mat-icon-button #privateKeyHelp="matTooltip" (click)="privateKeyHelp.toggle()" [matTooltip]="'pgp.privateKey.help' | i18n" matTooltipPosition="after">
<mat-icon>help</mat-icon> <mat-icon>help</mat-icon>
</button> </a>
</mat-dialog-actions> </mat-dialog-actions>
<br /> <br />
<mat-dialog-actions> <mat-dialog-actions>
@ -26,5 +26,5 @@
<mat-slide-toggle [(ngModel)]="data.confirmClose" [disabled]="!downloaded"> <mat-slide-toggle [(ngModel)]="data.confirmClose" [disabled]="!downloaded">
{{'pgp.privateKey.confirmStore' | i18n}} {{'pgp.privateKey.confirmStore' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<button mat-button [disabled]="!data.confirmClose" [mat-dialog-close]="true">{{'ok' | i18n}}</button> <a mat-button [disabled]="!data.confirmClose" [mat-dialog-close]="true">{{'ok' | i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -3,15 +3,15 @@
<mat-form-field> <mat-form-field>
<mat-chip-list #chipList [multiple]="true" [selectable]="true" cdkDropList cdkDropListOrientation="horizontal" <mat-chip-listbox #chipList [multiple]="true" [selectable]="true" cdkDropList cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"> (cdkDropListDropped)="drop($event)">
<mat-chip *ngFor="let dict of dicts" cdkDrag [selected]="dict.selected" (click)="toggle(dict)"> <mat-chip *ngFor="let dict of dicts" cdkDrag [selected]="dict.selected" (click)="toggle(dict)">
{{dict.name}} {{dict.name}}
</mat-chip> </mat-chip>
</mat-chip-list> </mat-chip-listbox>
</mat-form-field> </mat-form-field>
</div> </div>
<div mat-dialog-actions> <div mat-dialog-actions>
<button mat-button (click)="onOkClick()">Ok</button> <a mat-button (click)="onOkClick()">Ok</a>
</div> </div>

View File

@ -6,9 +6,9 @@
<mat-list-item *ngFor="let item of items"> <mat-list-item *ngFor="let item of items">
<mat-icon mat-list-icon>plus_one</mat-icon> <mat-icon mat-list-icon>plus_one</mat-icon>
{{ item.name[currentLocale] || 'missing' }} {{ item.name[currentLocale] || 'missing' }}
<button mat-icon-button (click)="removeSecret(item.secret)"> <a mat-icon-button (click)="removeSecret(item.secret)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </a>
</mat-list-item> </mat-list-item>
</mat-list> </mat-list>
@ -16,9 +16,9 @@
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="auth.authenticated" mat-raised-button color="accent" (click)="redeem()"> <a *ngIf="auth.authenticated" mat-raised-button color="accent" (click)="redeem()">
<mat-icon>redeem</mat-icon> {{'tokens.redeem' | i18n}} <mat-icon>redeem</mat-icon> {{'tokens.redeem' | i18n}}
</button> </a>
<ng-container *ngIf="!auth.authenticated"> <ng-container *ngIf="!auth.authenticated">
<p>{{'tokens.register' | i18n}}</p> <p>{{'tokens.register' | i18n}}</p>
@ -65,14 +65,16 @@
{{'tokens.redeemed' | i18n}} {{'tokens.redeemed' | i18n}}
</mat-error> </mat-error>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'token' | i18n}}" formControlName="token" matAutofocus> <mat-label>{{'token' | i18n}}</mat-label>
<input matInput formControlName="token" matAutofocus>
<mat-error> <mat-error>
{{'tokens.provide-valid' | i18n}} {{'tokens.provide-valid' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button mat-raised-button color="primary" [disabled]="form.invalid">{{ (items && items[0] ? 'tokens.validate.other' : 'tokens.validate') | i18n}}</button> <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-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#token" class="help-button" <a href="https://wiki.bstly.de/services/webstly#token" class="help-button"

View File

@ -1,17 +1,20 @@
<h3>{{'urlshortener' | i18n}}</h3> <h3>{{'urlshortener' | i18n}}</h3>
<div *ngIf="shortenedUrls"> <div *ngIf="shortenedUrls">
<mat-form-field> <div class="flex">
<input matInput [formControl]="searchFormControl" placeholder="{{'urlshortener.search' | i18n}}"> <mat-form-field>
</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)"> <table mat-table matSort [dataSource]="shortenedUrls.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="share"> <ng-container matColumnDef="share">
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.share' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'urlshortener.share' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl"> <td mat-cell *matCellDef="let shortenedUrl">
<button mat-icon-button (click)="share(shortenedUrl)"> <a mat-icon-button (click)="share(shortenedUrl)">
<mat-icon>share</mat-icon> <mat-icon>share</mat-icon>
</button> </a>
</td> </td>
</ng-container> </ng-container>
@ -52,8 +55,8 @@
<ng-container matColumnDef="edit"> <ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'urlshortener.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef> {{'urlshortener.edit' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl" class="text-right"> <td mat-cell *matCellDef="let shortenedUrl" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="edit(shortenedUrl)">
<mat-icon (click)="edit(shortenedUrl)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -61,8 +64,8 @@
<ng-container matColumnDef="delete"> <ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'urlshortener.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'urlshortener.delete' | i18n}} </th>
<td mat-cell *matCellDef="let shortenedUrl" class="text-right"> <td mat-cell *matCellDef="let shortenedUrl" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="confirmDelete(shortenedUrl)">
<mat-icon (click)="confirmDelete(shortenedUrl)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -84,16 +87,16 @@
<p>{{'urlshortener.left' | i18n:shortenedUrlQuota}}</p> <p>{{'urlshortener.left' | i18n:shortenedUrlQuota}}</p>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'urlshortener.url' | i18n}}" formControlName="url" <mat-label>{{'urlshortener.url' | i18n}}</mat-label>
[(ngModel)]="shortenedUrl.url" type="url"> <input matInput formControlName="url" [(ngModel)]="shortenedUrl.url" type="url">
<mat-error> <mat-error>
{{'urlshortener.error.url' | i18n}} {{'urlshortener.error.url' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<textarea matInput type="note" placeholder="{{'urlshortener.note' | i18n}}" formControlName="note" <mat-label>{{'urlshortener.note' | i18n}}</mat-label>
[(ngModel)]="shortenedUrl.note"></textarea> <textarea matInput type="note" formControlName="note" [(ngModel)]="shortenedUrl.note"></textarea>
<mat-error> <mat-error>
{{'urlshortener.error.note' | i18n}} {{'urlshortener.error.note' | i18n}}
</mat-error> </mat-error>
@ -106,8 +109,8 @@
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password" <mat-label>{{'password' | i18n}}</mat-label>
[(ngModel)]="shortenedUrl.password"> <input matInput type="password" formControlName="password" [(ngModel)]="shortenedUrl.password">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> <div *ngFor="let error of form.get('password').errors | keyvalue">
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
@ -116,16 +119,17 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" formControlName="password2" <mat-label>{{'password.confirm' | i18n}}</mat-label>
[(ngModel)]="shortenedUrl.password2"> <input matInput type="password" formControlName="password2" [(ngModel)]="shortenedUrl.password2">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.expires' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrl.expires" <input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrl.expires"
formControlName="expires" placeholder="{{'urlshortener.expires' | i18n}}"> formControlName="expires">
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -139,8 +143,8 @@
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'urlshortener.code' | i18n}}" formControlName="code" <mat-label>{{'urlshortener.code' | i18n}}</mat-label>
[(ngModel)]="shortenedUrl.code"> <input matInput formControlName="code" [(ngModel)]="shortenedUrl.code">
<mat-error> <mat-error>
{{'urlshortener.error.code' | i18n}} {{'urlshortener.error.code' | i18n}}
</mat-error> </mat-error>
@ -149,15 +153,16 @@
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button *ngIf="shortenedUrlQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid"> <button type="submit" *ngIf="shortenedUrlQuota && !working" mat-raised-button color="primary"
[disabled]="form.invalid">
{{'urlshortener.create' | i18n}} {{'urlshortener.create' | i18n}}
</button> </button>
</mat-card-actions> </mat-card-actions>
<mat-card-footer> <mat-card-footer>
<a href="https://wiki.bstly.de/services/urlshortener" class="help-button" matTooltip="{{'help-button' | i18n}}" <a href="https://wiki.bstly.de/services/urlshortener" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltipPosition="above" target="_blank" mat-fab color="accent"> matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon> <mat-icon>contact_support</mat-icon>
</a> </a>
</mat-card-footer> </mat-card-footer>
</mat-card> </mat-card>

View File

@ -2,24 +2,11 @@ mat-form-field {
display: block; display: block;
} }
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button { .mat-cell .mat-button {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
} }
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.url { .url {
display: inline-block; display: inline-block;
width: 200px; width: 200px;

View File

@ -4,16 +4,16 @@
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'urlshortener.url' | i18n}}" formControlName="url" <mat-label>{{'urlshortener.url' | i18n}}</mat-label>
[(ngModel)]="shortenedUrlModel.url" type="url"> <input matInput formControlName="url" [(ngModel)]="shortenedUrlModel.url" type="url">
<mat-error> <mat-error>
{{'urlshortener.error.url' | i18n}} {{'urlshortener.error.url' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<textarea matInput type="note" placeholder="{{'urlshortener.note' | i18n}}" formControlName="note" <mat-label>{{'urlshortener.note' | i18n}}</mat-label>
[(ngModel)]="shortenedUrlModel.note"></textarea> <textarea matInput formControlName="note" [(ngModel)]="shortenedUrlModel.note"></textarea>
<mat-error> <mat-error>
{{'urlshortener.error.note' | i18n}} {{'urlshortener.error.note' | i18n}}
</mat-error> </mat-error>
@ -31,8 +31,8 @@
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field *ngIf="shortenedUrlModel.newPassword"> <mat-form-field *ngIf="shortenedUrlModel.newPassword">
<input matInput type="password" placeholder="{{'password' | i18n}}" formControlName="password" <mat-label>{{'password' | i18n}}</mat-label>
[(ngModel)]="shortenedUrlModel.password"> <input matInput type="password" formControlName="password" [(ngModel)]="shortenedUrlModel.password">
<mat-error> <mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue"> <div *ngFor="let error of form.get('password').errors | keyvalue">
{{'password.error.' + error.key | i18n}}<br> {{'password.error.' + error.key | i18n}}<br>
@ -41,16 +41,17 @@
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="shortenedUrlModel.newPassword"> <mat-form-field *ngIf="shortenedUrlModel.newPassword">
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" formControlName="password2" <mat-label>{{'password.confirm' | i18n}}</mat-label>
[(ngModel)]="shortenedUrlModel.password2"> <input matInput type="password" formControlName="password2" [(ngModel)]="shortenedUrlModel.password2">
<mat-error> <mat-error>
{{'password.not-match' | i18n}} {{'password.not-match' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.expires' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrlModel.expires" <input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrlModel.expires"
formControlName="expires" placeholder="{{'urlshortener.expires' | i18n}}"> formControlName="expires">
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -64,8 +65,8 @@
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field> <mat-form-field>
<input matInput placeholder="{{'urlshortener.code' | i18n}}" formControlName="code" <mat-label>{{'urlshortener.code' | i18n}}</mat-label>
[(ngModel)]="shortenedUrlModel.newCode"> <input matInput formControlName="code" [(ngModel)]="shortenedUrlModel.newCode">
<mat-error> <mat-error>
{{'urlshortener.error.code' | i18n}} {{'urlshortener.error.code' | i18n}}
</mat-error> </mat-error>
@ -74,8 +75,8 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">{{ <a [disabled]="form.invalid" mat-raised-button (click)="save()" color="accent">{{
'urlshortener.save' 'urlshortener.save'
| i18n}}</button> | i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -4,17 +4,18 @@
<h2>{{'urlshortener.password' | i18n}} <h2>{{'urlshortener.password' | i18n}}
</h2> </h2>
<mat-form-field> <mat-form-field>
<input id="password" name="password" matInput type="password" placeholder="{{'password' | i18n}}" required matAutofocus> <mat-label>{{'password' | i18n}}</mat-label>
<input id="password" name="password" matInput type="password" required matAutofocus>
</mat-form-field> </mat-form-field>
<mat-error *ngIf="invalidPassword"> <mat-error *ngIf="invalidPassword">
{{'urlshortener.password.invalid' | i18n}} {{'urlshortener.password.invalid' | i18n}}
</mat-error> </mat-error>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" (click)="loginForm.submit()" mat-raised-button color="primary" <a type="submit" (click)="loginForm.submit()" mat-raised-button color="primary"
[disabled]="loginForm.invalid">{{'urlshortener.password.submit' | [disabled]="loginForm.invalid">{{'urlshortener.password.submit' |
i18n}}<mat-icon style="font-size: 1em;">open_in_new i18n}}<mat-icon style="font-size: 1em;">open_in_new
</mat-icon></button> </mat-icon></a>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</form> </form>

View File

@ -33,5 +33,5 @@
</mat-list> </mat-list>
</div> </div>
<div mat-dialog-actions> <div mat-dialog-actions>
<button mat-button mat-dialog-close>{{'close' | i18n}}</button> <a mat-button mat-dialog-close>{{'close' | i18n}}</a>
</div> </div>

View File

@ -3,9 +3,9 @@
<div *ngIf="success"> <div *ngIf="success">
<h3>{{model.username}} <h3>{{model.username}}
<button *ngIf="isMe" mat-icon-button color="accent"> <a *ngIf="isMe" mat-icon-button color="accent">
<mat-icon>star</mat-icon> <mat-icon>star</mat-icon>
</button> </a>
</h3> </h3>
<app-profilefields [username]="model.username"></app-profilefields> <app-profilefields [username]="model.username"></app-profilefields>
<div *ngIf="model.aliases && model.aliases.length > 0"> <div *ngIf="model.aliases && model.aliases.length > 0">

View File

@ -1,8 +1,8 @@
import {Injectable} from '@angular/core'; import { Injectable } from '@angular/core';
import {ReplaySubject, of} from 'rxjs'; import { ReplaySubject, of } from 'rxjs';
import {HttpClient, HttpHeaders} from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import {environment} from './../../environments/environment'; import { environment } from './../../environments/environment';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -27,6 +27,7 @@ export class AuthService {
return this.http.get(environment.apiUrl + "/auth/me"); return this.http.get(environment.apiUrl + "/auth/me");
} }
login(loginModel) { login(loginModel) {
return this.http.post(environment.apiUrl + "/auth/login", loginModel); return this.http.post(environment.apiUrl + "/auth/login", loginModel);
} }
@ -37,11 +38,15 @@ export class AuthService {
passwordRequest(username) { passwordRequest(username) {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8'); const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
return this.http.post(environment.apiUrl + "/auth/password/request", username, {headers, responseType: 'text'}); return this.http.post(environment.apiUrl + "/auth/password/request", username, { headers, responseType: 'text' });
} }
passwordReset(model) { passwordReset(model) {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8'); const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
return this.http.post(environment.apiUrl + "/auth/password/reset", model); return this.http.post(environment.apiUrl + "/auth/password/reset", model);
} }
setAlias(alias) {
return this.http.post(environment.apiUrl + "/auth/alias", alias);
}
} }

View File

@ -2,6 +2,6 @@
{{text}} {{text}}
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-raised-button [mat-dialog-close]="true" color="accent" matAutofocus>{{'confirm' | i18n}}</button> <a mat-raised-button [mat-dialog-close]="true" color="accent" matAutofocus>{{'confirm' | i18n}}</a>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -1,18 +1,21 @@
<div fxLayout="row wrap" fxLayoutAlign="start stretch"> <div class="flex wrap" fxLayoutAlign="start stretch">
<div fxFlex="33%"> <div [style.flex-basis]="'33%'">
<mat-form-field> <mat-form-field>
<input matInput type="number" min="0" placeholder="{{'durationpicker.days' | i18n}}" [formControl]="days"> <mat-label>{{'durationpicker.days' | i18n}}</mat-label>
<input matInput type="number" min="0" [formControl]="days">
</mat-form-field> </mat-form-field>
</div> </div>
<div fxFlex="33%"> <div [style.flex-basis]="'33%'">
<mat-form-field> <mat-form-field>
<input matInput type="number" min="0" max="23" placeholder="{{'durationpicker.hours' | i18n}}" <mat-label>{{'durationpicker.hours' | i18n}}</mat-label>
<input matInput type="number" min="0" max="23"
[formControl]="hours"> [formControl]="hours">
</mat-form-field> </mat-form-field>
</div> </div>
<div fxFlex="33%"> <div [style.flex-basis]="'33%'">
<mat-form-field> <mat-form-field>
<input matInput type="number" min="0" max="59" placeholder="{{'durationpicker.minutes' | i18n}}" <mat-label>{{'durationpicker.minutes' | i18n}}</mat-label>
<input matInput type="number" min="0" max="59"
[formControl]="minutes"> [formControl]="minutes">
<mat-hint align="end">{{duration}}</mat-hint> <mat-hint align="end">{{duration}}</mat-hint>
</mat-form-field> </mat-form-field>

View File

@ -1,6 +1,6 @@
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<a href="javascript:" mat-icon-button> <a href="javascript:" mat-icon-button (click)="sidenav.toggle()">
<mat-icon (click)="sidenav.toggle()">menu</mat-icon> <mat-icon>menu</mat-icon>
</a> </a>
<mat-icon svgIcon="logo"></mat-icon> <mat-icon svgIcon="logo"></mat-icon>
<span> <span>
@ -9,17 +9,17 @@
<span class="spacer"></span> <span class="spacer"></span>
<ng-container> <ng-container>
<button mat-button [matMenuTriggerFor]="menu"> <a mat-button [matMenuTriggerFor]="menu">
<mat-icon>settings</mat-icon> <mat-icon>settings</mat-icon>
<mat-icon>arrow_drop_down</mat-icon> <mat-icon>arrow_drop_down</mat-icon>
</button> </a>
<mat-menu #menu="matMenu"> <mat-menu #menu="matMenu">
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-menu-item> <a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-menu-item>
<mat-icon>login</mat-icon> {{'login' | i18n}} <mat-icon>login</mat-icon> {{'login' | i18n}}
</a> </a>
<mat-divider *ngIf="!auth || auth && !auth.authenticated"></mat-divider> <mat-divider *ngIf="!auth || auth && !auth.authenticated"></mat-divider>
<a *ngFor="let locale of locales" mat-menu-item (click)="setLocale(locale)">{{'locale.' + locale + '.long' | <a *ngFor="let locale of locales" mat-menu-item (click)="setLocale(locale)">{{'locale.' + locale + '.long' |
i18n}} <mat-icon inline=true *ngIf="locale == currentLocale">done</mat-icon></a> i18n}} <mat-icon *ngIf="locale == currentLocale">done</mat-icon></a>
<a mat-menu-item> <a mat-menu-item>
<mat-slide-toggle (change)="darkThemeChange($event)" [checked]="darkTheme == 'true'"> <mat-slide-toggle (change)="darkThemeChange($event)" [checked]="darkTheme == 'true'">
{{'profileField.name.darkTheme' | i18n}} {{'profileField.name.darkTheme' | i18n}}

View File

@ -3,9 +3,9 @@
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'permissions.name' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'permissions.name' | i18n}} </th>
<td mat-cell *matCellDef="let permission"> <td mat-cell *matCellDef="let permission">
<mat-icon inline=true>{{'services.' + permission.name + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'services.' + permission.name + '.icon' | i18n}}</mat-icon>
{{'services.' + permission.name + '.title' | i18n}} {{'services.' + permission.name + '.title' | i18n}}
<mat-icon inline=true *ngIf="permission.addon">add_circle</mat-icon> <mat-icon inline="true" *ngIf="permission.addon">add_circle</mat-icon>
<br> <br>
<small>{{'services.' + permission.name + '.subtitle' | i18n}}</small> <small>{{'services.' + permission.name + '.subtitle' | i18n}}</small>
</td> </td>

View File

@ -13,9 +13,9 @@
i18n}}</a> i18n}}</a>
<a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' | <a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' |
i18n}}</a> i18n}}</a>
<button mat-icon-button #privateKeyHelp="matTooltip" (click)="privateKeyHelp.toggle()" [matTooltip]="'pgp.privateKey.help' | i18n" matTooltipPosition="after"> <a mat-icon-button #privateKeyHelp="matTooltip" (click)="privateKeyHelp.toggle()" [matTooltip]="'pgp.privateKey.help' | i18n" matTooltipPosition="after">
<mat-icon>help</mat-icon> <mat-icon>help</mat-icon>
</button> </a>
</mat-dialog-actions> </mat-dialog-actions>
<br /> <br />
<mat-dialog-actions> <mat-dialog-actions>
@ -23,6 +23,6 @@
<mat-slide-toggle [(ngModel)]="data.confirmClose" [disabled]="!downloaded"> <mat-slide-toggle [(ngModel)]="data.confirmClose" [disabled]="!downloaded">
{{'pgp.privateKey.confirmStore' | i18n}} {{'pgp.privateKey.confirmStore' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button mat-button color="accent" [disabled]="!data.confirmClose" [mat-dialog-close]="data.publicKey">{{'ok' | i18n}}</button> <a mat-button color="accent" [disabled]="!data.confirmClose" [mat-dialog-close]="data.publicKey">{{'ok' | i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -5,5 +5,5 @@
</pre> </pre>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close>{{'close' | i18n}}</button> <a mat-button mat-dialog-close>{{'close' | i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -2,15 +2,16 @@
<mat-dialog-content> <mat-dialog-content>
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
<input matInput type="text" min="3" [(ngModel)]="profileField.name" formControlName="name" <mat-label>{{'profileField.name' | i18n}}</mat-label>
placeholder="{{'profileField.name' | i18n}}"> <input matInput type="text" min="3" [(ngModel)]="profileField.name" formControlName="name">
<mat-error> <mat-error>
{{'profileField.error.name' | i18n}} {{'profileField.error.name' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-select [(ngModel)]="profileField.type" formControlName="type" placeholder="{{'profileField.type' | i18n}}"> <mat-label>{{'profileField.type' | i18n}}</mat-label>
<mat-select [(ngModel)]="profileField.type" formControlName="type">
<mat-option *ngFor="let type of types" [value]="type"> <mat-option *ngFor="let type of types" [value]="type">
{{'profileField.type.' + type | i18n}} {{'profileField.type.' + type | i18n}}
</mat-option> </mat-option>
@ -22,15 +23,15 @@
<div [ngSwitch]="profileField.type"> <div [ngSwitch]="profileField.type">
<mat-form-field *ngSwitchCase="'TEXT'"> <mat-form-field *ngSwitchCase="'TEXT'">
<input matInput type="text" max="255" [(ngModel)]="profileField.value" formControlName="value" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"> <input matInput type="text" max="255" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.TEXT' | i18n}} {{'profileField.error.TEXT' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'DATE'"> <mat-form-field *ngSwitchCase="'DATE'">
<input matInput [matDatepicker]="datePicker" [(ngModel)]="profileField.value" formControlName="value" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"> <input matInput [matDatepicker]="datePicker" [(ngModel)]="profileField.value" formControlName="value">
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
<mat-datepicker #datePicker></mat-datepicker> <mat-datepicker #datePicker></mat-datepicker>
<mat-error> <mat-error>
@ -38,8 +39,9 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'DATETIME'"> <mat-form-field *ngSwitchCase="'DATETIME'">
<input matInput [ngxMatDatetimePicker]="datetimePicker" [(ngModel)]="profileField.value" formControlName="value" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"> <input matInput [ngxMatDatetimePicker]="datetimePicker" [(ngModel)]="profileField.value"
formControlName="value">
<mat-datepicker-toggle matSuffix [for]="datetimePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="datetimePicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #datetimePicker></ngx-mat-datetime-picker> <ngx-mat-datetime-picker #datetimePicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
@ -47,15 +49,15 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'URL'"> <mat-form-field *ngSwitchCase="'URL'">
<input matInput type="url" [(ngModel)]="profileField.value" formControlName="value" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"> <input matInput type="url" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.URL' | i18n}} {{'profileField.error.URL' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'EMAIL'"> <mat-form-field *ngSwitchCase="'EMAIL'">
<input matInput type="email" [(ngModel)]="profileField.value" formControlName="value" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"> <input matInput type="email" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.EMAIL' | i18n}} {{'profileField.error.EMAIL' | i18n}}
</mat-error> </mat-error>
@ -65,16 +67,16 @@
{{'profileField.value' | i18n}} {{'profileField.value' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<mat-form-field *ngSwitchCase="'NUMBER'"> <mat-form-field *ngSwitchCase="'NUMBER'">
<input matInput type="number" [(ngModel)]="profileField.value" formControlName="value" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"> <input matInput type="number" [(ngModel)]="profileField.value" formControlName="value">
<mat-error> <mat-error>
{{'profileField.error.NUMBER' | i18n}} {{'profileField.error.NUMBER' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div *ngSwitchCase="'BLOB'"> <div *ngSwitchCase="'BLOB'">
<mat-form-field> <mat-form-field>
<textarea matInput [(ngModel)]="profileField.blob" formControlName="blob" <mat-label>{{'profileField.value' | i18n}}</mat-label>
placeholder="{{'profileField.value' | i18n}}"></textarea> <textarea matInput [(ngModel)]="profileField.blob" formControlName="blob"></textarea>
<mat-error> <mat-error>
{{'profileField.error.BLOB' | i18n}} {{'profileField.error.BLOB' | i18n}}
</mat-error> </mat-error>
@ -85,8 +87,8 @@
<mat-form-field> <mat-form-field>
<mat-select [(ngModel)]="profileField.visibility" formControlName="visibility" <mat-label>{{'visibility' | i18n}}</mat-label>
placeholder="{{'visibility' | i18n}}"> <mat-select [(ngModel)]="profileField.visibility" formControlName="visibility">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility"> <mat-option *ngFor="let visibility of visibilities" [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + <mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}} visibility | i18n}}
@ -104,8 +106,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput type="number" min="0" [(ngModel)]="profileField.index" formControlName="index" <input matInput type="number" min="0" [(ngModel)]="profileField.index" formControlName="index">
placeholder="{{'profileField.index' | i18n}}"> <mat-label>{{'profileField.index' | i18n}}</mat-label>
<mat-error> <mat-error>
{{'profileField.error.index' | i18n}} {{'profileField.error.index' | i18n}}
</mat-error> </mat-error>
@ -113,7 +115,7 @@
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button> <a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<button [disabled]="form.invalid" mat-raised-button (click)="save(profileField)" color="accent">{{'save' | <a [disabled]="form.invalid" mat-raised-button (click)="save(profileField)" color="accent">{{'save' |
i18n}}</button> i18n}}</a>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -16,7 +16,7 @@
<a *ngSwitchCase="'URL'" class="accent" href="{{profileField.value}}">{{profileField.value}}</a> <a *ngSwitchCase="'URL'" class="accent" href="{{profileField.value}}">{{profileField.value}}</a>
<a *ngSwitchCase="'EMAIL'" class="accent" href="mailto:{{profileField.value}}">{{profileField.value}}</a> <a *ngSwitchCase="'EMAIL'" class="accent" href="mailto:{{profileField.value}}">{{profileField.value}}</a>
<span *ngSwitchCase="'NUMBER'">{{profileField.value}}</span> <span *ngSwitchCase="'NUMBER'">{{profileField.value}}</span>
<button *ngSwitchCase="'BLOB'" mat-raised-button (click)="openBlob(profileField)">{{'profileField.openBlob' | i18n}}</button> <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 *ngSwitchCase="'BOOL'" [checked]="profileField.value == 'true'" disabled>
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
@ -34,8 +34,8 @@
<ng-container matColumnDef="edit" *ngIf="edit"> <ng-container matColumnDef="edit" *ngIf="edit">
<th mat-header-cell *matHeaderCellDef class="text-right"> {{'profileField.edit' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="text-right"> {{'profileField.edit' | i18n}} </th>
<td mat-cell *matCellDef="let profileField" class="text-right"> <td mat-cell *matCellDef="let profileField" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="openEdit(profileField)">
<mat-icon (click)="openEdit(profileField)">edit</mat-icon> <mat-icon>edit</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -43,8 +43,8 @@
<ng-container matColumnDef="delete" *ngIf="edit"> <ng-container matColumnDef="delete" *ngIf="edit">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'profileField.delete' | i18n}} </th> <th mat-header-cell *matHeaderCellDef class="align-right"> {{'profileField.delete' | i18n}} </th>
<td mat-cell *matCellDef="let profileField" class="text-right"> <td mat-cell *matCellDef="let profileField" class="text-right">
<a mat-icon-button> <a mat-icon-button (click)="confirmDelete(profileField)">
<mat-icon (click)="confirmDelete(profileField)">delete</mat-icon> <mat-icon>delete</mat-icon>
</a> </a>
</td> </td>
</ng-container> </ng-container>
@ -54,6 +54,6 @@
</table> </table>
<br> <br>
<div *ngIf="edit" class="text-center"> <div *ngIf="edit" class="text-center">
<button mat-raised-button color="primary" (click)="openCreate()">{{'profileField.create' | <a mat-raised-button color="primary" (click)="openCreate()">{{'profileField.create' |
i18n}}</button> i18n}}</a>
</div> </div>

View File

@ -1,16 +1,3 @@
table { table {
width: 100%; width: 100%;
} }
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.align-right {
display: flex;
padding: 21px 0;
justify-content: flex-end;
}

View File

@ -3,7 +3,7 @@
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'quotas.name' | i18n}} </th> <th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'quotas.name' | i18n}} </th>
<td mat-cell *matCellDef="let quota"> <td mat-cell *matCellDef="let quota">
<mat-icon inline=true>{{'services.' + quota.name + '.icon' | i18n}}</mat-icon> <mat-icon inline="true">{{'services.' + quota.name + '.icon' | i18n}}</mat-icon>
{{'services.' + quota.name + '.title' | i18n}} {{'services.' + quota.name + '.title' | i18n}}
</td> </td>
</ng-container> </ng-container>

View File

@ -1,10 +1,12 @@
<div fxLayout="row wrap" fxLayoutGap="24px grid"> <div class="service-grid" fxLayoutGap="24px grid">
<div fxFlex="33%" fxFlex.sm="50%" fxFlex.xs="100%" *ngFor="let service of services"> <div *ngFor="let service of services">
<mat-card> <mat-card>
<a *ngIf="service.url" href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" <a *ngIf="service.url" href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'"
color="accent"> color="accent">
<mat-card-header> <mat-card-header>
<mat-icon mat-card-avatar>{{'services.' + service.name + '.icon' | i18n}}</mat-icon> <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> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
<mat-icon *ngIf="!service.sameSite" inline="true"> <mat-icon *ngIf="!service.sameSite" inline="true">
open_in_new</mat-icon> open_in_new</mat-icon>
@ -14,7 +16,9 @@
</a> </a>
<mat-card-header *ngIf="!service.url"> <mat-card-header *ngIf="!service.url">
<mat-icon mat-card-avatar>{{'services.' + service.name + '.icon' | i18n}}</mat-icon> <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> <strong>{{'services.' + service.name + '.title' | i18n}}</strong>
</mat-card-title> </mat-card-title>
<mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle> <mat-card-subtitle>{{'services.' + service.name + '.subtitle' | i18n}}</mat-card-subtitle>

View File

@ -1,22 +1,50 @@
@import '../../../variables.scss'; @import '../../../variables.scss';
mat-card { .service-grid {
display: flex;
flex-direction: column;
height: 100%;
a { display: grid;
text-decoration: none; column-gap: 24px;
color: inherit; row-gap: 24px;
mat-card-title { @media (min-width: 576px) {
color: $accent; grid-template-columns: 1fr;
}
} }
mat-card-content { @media (min-width: 768px) {
flex-grow: 1; grid-template-columns: 1fr 1fr;
overflow: auto; }
@media (min-width: 992px) {
grid-template-columns: 1fr 1fr 1fr;
}
mat-card {
display: flex;
flex-direction: column;
height: 100%;
a {
text-decoration: none;
color: inherit;
mat-card-title {
color: $accent;
}
}
mat-card-header {
.icon {
display: flex;
justify-content: center;
align-items: center;
}
}
mat-card-content {
flex-grow: 1;
overflow: auto;
}
} }
} }

View File

@ -1,5 +1,5 @@
// Custom Theming for Angular Material // Custom Theming for Angular Material
@use '@angular/material'as mat; @use '@angular/material' as mat;
// For more information: https://material.angular.io/guide/theming // For more information: https://material.angular.io/guide/theming
// Plus imports for other components in your app. // Plus imports for other components in your app.
@ -15,15 +15,15 @@
// (imported above). For each palette, you can optionally specify a default, lighter, and darker // (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/ // hue. Available color palettes: https://material.io/design/color/
$light-theme: mat.define-light-theme((color: (primary: $light-primary, $light-theme: mat.define-light-theme((color: (primary: $light-primary,
accent: $light-accent, accent: $light-accent,
warn: $light-warn, warn: $light-warn,
))); )));
// Define an alternate dark theme. // Define an alternate dark theme.
$dark-theme: mat.define-dark-theme((color: (primary: $dark-primary, $dark-theme: mat.define-dark-theme((color: (primary: $dark-primary,
accent: $light-accent, accent: $light-accent,
warn: $light-warn, warn: $light-warn,
))); )));
@ -83,6 +83,14 @@ body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
} }
a {
text-decoration: none;
}
button {
font-size: inherit;
}
app-root, app-root,
app-main { app-main {
height: 100%; height: 100%;
@ -107,6 +115,12 @@ app-main {
mat-card { mat-card {
max-width: 400px; max-width: 400px;
margin: 2em auto; margin: 2em auto;
mat-card-actions {
&>* {
margin-right: 15px;
}
}
} }
mat-form-field { mat-form-field {
@ -156,6 +170,31 @@ qr-code canvas {
max-width: 400px !important; max-width: 400px !important;
} }
.flex {
display: flex;
&.wrap {
flex-wrap: wrap;
}
&.column {
flex-direction: column;
}
&.justify-center {
justify-content: center;
}
&.justify-between {
justify-content: space-between;
}
&.align-center {
align-items: center;
}
}
.spacer { .spacer {
flex: 1 1 auto; flex: 1 1 auto;
} }
@ -182,33 +221,33 @@ mat-sidenav-container {
margin-left: auto; margin-left: auto;
margin-bottom: 15px; margin-bottom: 15px;
@media screen and (min-width: 576px) { @media (min-width: 576px) {
width: 540px; width: 540px;
} }
@media screen and (min-width: 768px) { @media (min-width: 768px) {
width: 580px; width: 580px;
} }
@media screen and (min-width: 992px) { @media (min-width: 992px) {
width: 820px; width: 820px;
} }
@media screen and (min-width: 1200px) { @media (min-width: 1200px) {
width: 1000px; width: 1000px;
} }
} }
.text-center { .text-center {
text-align: center; text-align: center !important;
} }
.text-justify { .text-justify {
text-align: justify; text-align: justify !important;
} }
.text-right { .text-right {
text-align: right; text-align: right !important;
} }
.text-warning { .text-warning {
@ -217,7 +256,7 @@ mat-sidenav-container {
.align-right { .align-right {
display: flex; display: flex;
padding: 21px 0; padding: 21px !important;
justify-content: flex-end; justify-content: flex-end;
} }
@ -322,11 +361,16 @@ table {
} }
} }
.help-button { mat-card-footer,
float: right; footer {
position: relative; position: relative;
top: -40px;
right: 15px; .help-button {
float: right;
position: absolute;
top: -40px;
right: 15px;
}
} }
mat-card.success { mat-card.success {

View File

@ -10,10 +10,10 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"target": "es2015", "target": "ES2022",
"module": "es2020", "module": "ES2022",
"lib": [ "lib": [
"es2018", "ES2022",
"dom" "dom"
] ]
} }