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",
"plaintext",
"json"
]
],
"angular.enable-strict-mode-prompt": false
}

7941
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "we-bstly-angular",
"version": "2.0.0",
"version": "2.1.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
@ -11,39 +11,38 @@
},
"private": true,
"dependencies": {
"@angular-material-components/datetime-picker": "^8.0.0",
"@angular-material-components/moment-adapter": "^8.0.0",
"@angular/animations": "^14.2.9",
"@angular/cdk": "^14.2.6",
"@angular/common": "^14.2.9",
"@angular/compiler": "^14.2.9",
"@angular/core": "^14.2.9",
"@angular/forms": "^14.2.9",
"@angular/material": "^14.2.7",
"@angular/material-moment-adapter": "^14.2.7",
"@angular/platform-browser": "^14.2.9",
"@angular/platform-browser-dynamic": "^14.2.9",
"@angular/router": "^14.2.9",
"@angular-material-components/datetime-picker": "^9.0.0",
"@angular-material-components/moment-adapter": "^9.0.0",
"@angular/animations": "^15.0.3",
"@angular/cdk": "^15.0.2",
"@angular/common": "^15.0.3",
"@angular/compiler": "^15.0.3",
"@angular/core": "^15.0.3",
"@angular/forms": "^15.0.3",
"@angular/material": "^15.0.2",
"@angular/material-moment-adapter": "^15.0.2",
"@angular/platform-browser": "^15.0.3",
"@angular/platform-browser-dynamic": "^15.0.3",
"@angular/router": "^15.0.3",
"moment": "^2.29.4",
"ng-qrcode": "^7.0.0",
"ngx-mat-timepicker": "^14.0.6",
"ng-qrcode": "^8.0.1",
"ngx-mat-timepicker": "^15.1.2",
"openpgp": "^5.5.0",
"qr-scanner": "^1.4.2",
"rxjs": "~7.5.7",
"rxjs": "~7.6.0",
"tslib": "^2.4.1",
"unique-names-generator": "^4.7.1",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.2.8",
"@angular/cli": "^14.2.8",
"@angular/compiler-cli": "^14.2.9",
"@angular/localize": "^14.2.9",
"@types/jasmine": "^4.3.0",
"@angular-devkit/build-angular": "^15.0.3",
"@angular/cli": "^15.0.3",
"@angular/compiler-cli": "^15.0.3",
"@angular/localize": "^15.0.3",
"@types/jasmine": "^4.3.1",
"@types/jasminewd2": "^2.0.10",
"@types/node": "^18.11.9",
"@types/node": "^18.11.12",
"@types/openpgp": "^4.4.18",
"codelyzer": "^6.0.2",
"jasmine-core": "~4.5.0",
"jasmine-spec-reporter": "~7.0.0",
"karma": "^6.4.1",
@ -54,6 +53,6 @@
"protractor": "~7.0.0",
"ts-node": "~10.9.1",
"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 { InviteCodeComponent } from './pages/invites/code/code.component';
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component';
const routes: Routes = [
{ path: 'profile/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] },
{ path: 'edit-profile', component: ProfileComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'jukebox', component: JukeboxComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'partey/manage', component: ParteyComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'profile/:username', component: UserComponent, canActivate: [AuthUpdateGuard] },
{ path: 'edit-profile', component: ProfileComponent, canActivate: [AuthenticatedGuard] },
{ path: 'jukebox', component: JukeboxComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey/manage', component: ParteyComponent, canActivate: [AuthenticatedGuard] },
{
path: '', component: MainComponent, children: [
{ path: '', redirectTo: "/services", pathMatch: 'full' },
{ path: 'login', component: FormLoginComponent, canActivate: [ AnonymousGuard ] },
{ path: 'login/2fa', component: FormLogin2FAComponent, canActivate: [ AnonymousGuard ] },
{ path: 'service-login', component: FormLoginComponent, canActivate: [ AnonymousGuard ] },
{ path: 'service-login/2fa', component: FormLogin2FAComponent, canActivate: [ AnonymousGuard ] },
{ path: 'password', component: PasswordComponent, canActivate: [ AnonymousGuard ] },
{ path: 'password-reset', component: PasswordResetComponent, canActivate: [ AnonymousGuard ] },
{ path: 'services', component: ServicesComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'login', component: FormLoginComponent, canActivate: [AnonymousGuard] },
{ path: 'login/2fa', component: FormLogin2FAComponent, canActivate: [AnonymousGuard] },
{ path: 'login/oidc', component: FormLoginOidcComponent, canActivate: [AuthenticatedGuard] },
{ path: 'service-login', component: FormLoginComponent, canActivate: [AnonymousGuard] },
{ path: 'service-login/2fa', component: FormLogin2FAComponent, canActivate: [AnonymousGuard] },
{ path: 'password', component: PasswordComponent, canActivate: [AnonymousGuard] },
{ 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: 'info', component: InfoComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'profile', component: ProfileComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'security', component: SecurityComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'voucher', component: VoucherComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'aliases', component: AliasesComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'domains', component: DomainsComponent, canActivate: [ AuthenticatedGuard ] }
{ path: 'info', component: InfoComponent, canActivate: [AuthenticatedGuard] },
{ path: 'profile', component: ProfileComponent, canActivate: [AuthenticatedGuard] },
{ path: 'security', component: SecurityComponent, canActivate: [AuthenticatedGuard] },
{ path: 'voucher', component: VoucherComponent, canActivate: [AuthenticatedGuard] },
{ path: 'aliases', component: AliasesComponent, 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: 'items', component: BorrowItemsComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'requests', component: BorrowRequestsComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'proving', component: BorrowProvingComponent, canActivate: [ AuthenticatedGuard ] }
{ path: 'items', component: BorrowItemsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'requests', component: BorrowRequestsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'proving', component: BorrowProvingComponent, canActivate: [AuthenticatedGuard] }
]
},
{ path: 'register', component: RegisterComponent, canActivate: [ AnonymousGuard ] },
{ path: 'tokens', component: TokensComponent, canActivate: [ AuthGuard ] },
{ path: 'jitsi', component: JitsiComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'partey', component: ParteyComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'partey/timeslots', component: ParteyTimeslotsComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'partey/jukebox', component: JukeboxComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'minetest/accounts', component: MinetestAccountsComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard] },
{ path: 'tokens', component: TokensComponent, canActivate: [AuthGuard] },
{ path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey', component: ParteyComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey/timeslots', component: ParteyTimeslotsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'partey/jukebox', component: JukeboxComponent, canActivate: [AuthenticatedGuard] },
{ path: 'minetest/accounts', component: MinetestAccountsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'dividertest', component: DividertestComponent },
{ path: 'urlshortener', component: UrlShortenerComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [ AuthUpdateGuard ] },
{ path: 'invites/:quota', component: InvitesComponent, canActivate: [ AuthenticatedGuard ] },
{ path: 'invite/:code', component: InviteCodeComponent, canActivate: [ AuthGuard ] },
{ path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard] },
{ path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard] },
{ path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard] },
{ path: 'invite/:code', component: InviteCodeComponent, canActivate: [AuthGuard] },
{ path: 'unavailable', component: UnavailableComponent },
{ path: 'p/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] },
{ path: '**', component: NotfoundComponent, pathMatch: 'full', canActivate: [ AuthUpdateGuard ] }, ]
{ path: 'p/:username', component: UserComponent, canActivate: [AuthUpdateGuard] },
{ path: '**', component: NotfoundComponent, pathMatch: 'full', canActivate: [AuthUpdateGuard] },]
},
];
@NgModule({
imports: [ RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy' }) ],
exports: [ RouterModule ]
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
exports: [RouterModule]
})
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 { MatPaginatorIntl } from '@angular/material/paginator';
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 { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component';
export function init_app(i18n: I18nService) {
@ -94,6 +94,7 @@ export class XhrInterceptor implements HttpInterceptor {
LoginComponent,
FormLoginComponent,
FormLogin2FAComponent,
FormLoginOidcComponent,
TokensComponent,
InvitesComponent, InviteCodeComponent, InviteEditComponent,
ServicesComponent,
@ -140,21 +141,21 @@ export class XhrInterceptor implements HttpInterceptor {
QrCodeModule,
],
exports: [ MaterialModule ],
providers: [ { provide: APP_INITIALIZER, useFactory: init_app, deps: [ I18nService ], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }, DatePipe,
exports: [MaterialModule],
providers: [{ provide: APP_INITIALIZER, useFactory: init_app, deps: [I18nService], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }, DatePipe,
{
provide: MatPaginatorIntl, useFactory: (i18n) => {
const service = new I18nPaginatorIntl();
service.injectI18n(i18n)
return service;
}, deps: [ I18nService ]
}, deps: [I18nService]
},
{
provide: NgxMatDateAdapter,
useClass: NgxMatMomentAdapter,
useFactory: (i18n: I18nService) => {
return new NgxMatMomentAdapter(i18n.getLocale(), { strict: true });
}, deps: [ I18nService ]
}, deps: [I18nService]
}, {
provide: NGX_MAT_DATE_FORMATS, useFactory: (i18n: I18nService) => {
const datetimeformat = i18n.get('format.datetime', []);
@ -169,9 +170,9 @@ export class XhrInterceptor implements HttpInterceptor {
monthYearA11yLabel: "MMMM YYYY"
}
};
}, deps: [ I18nService ]
} ],
bootstrap: [ AppComponent ],
}, deps: [I18nService]
}],
bootstrap: [AppComponent],
})
export class AppModule {
}

View File

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

View File

@ -10,8 +10,7 @@
<ng-container matColumnDef="visibility">
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
<td mat-cell *matCellDef="let alias">
<mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)"
placeholder="{{'visibility' | i18n}}">
<mat-select [(ngModel)]="alias.visibility" (selectionChange)="updateAlias(alias)">
<mat-option *ngFor="let visibility of visibilities" [value]="visibility">
<mat-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}}
@ -28,8 +27,8 @@
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.aliases.delete' | i18n}} </th>
<td mat-cell *matCellDef="let alias" class="text-right">
<a mat-icon-button>
<mat-icon (click)="confirmDelete(alias)">delete</mat-icon>
<a mat-icon-button (click)="confirmDelete(alias)">
<mat-icon>delete</mat-icon>
</a>
</td>
</ng-container>
@ -46,14 +45,15 @@
<div *ngIf="aliasCreation">
<p>{{'user.aliases.left' | i18n:aliasCreation}}</p>
<mat-form-field>
<input matInput placeholder="{{'user.aliases.alias' | i18n}}" formControlName="alias"
[(ngModel)]="alias.alias" required>
<mat-label>{{'user.aliases.alias' | i18n}}</mat-label>
<input matInput formControlName="alias" [(ngModel)]="alias.alias" required>
<mat-error>
{{'user.aliases.error' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-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-icon inline="true">{{'visibility.' + visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
visibility | i18n}}
@ -68,7 +68,8 @@
</div>
</mat-card-content>
<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}}
</button>
</mat-card-actions>

View File

@ -2,15 +2,9 @@ mat-form-field {
display: block;
}
.mat-header-cell,
.mat-cell {
.mat-mdc-header-cell,
.mat-mdc-cell {
&.text-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">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'user.domains.delete' | i18n}} </th>
<td mat-cell *matCellDef="let domain" class="text-right">
<a mat-icon-button>
<mat-icon (click)="confirmDelete(domain)">delete</mat-icon>
<a mat-icon-button (click)="confirmDelete(domain)">
<mat-icon>delete</mat-icon>
</a>
</td>
</ng-container>
@ -37,20 +37,20 @@
<tr mat-row *matRowDef="let myRowData; columns: domainsColumns"></tr>
</table>
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm" #domainForm>
<mat-card>
<mat-card-content>
<p>{{'user.domains.info' | i18n}}</p>
<mat-form-field>
<input matInput placeholder="{{'user.domains.domain' | i18n}}" formControlName="domain"
[(ngModel)]="domain.domain" required>
<mat-label>{{'user.domains.domain' | i18n}}</mat-label>
<input matInput formControlName="domain" [(ngModel)]="domain.domain" required>
<mat-error>
{{'user.domains.error' | i18n}}
</mat-error>
</mat-form-field>
</mat-card-content>
<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}}
</button>
</mat-card-actions>

View File

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

View File

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

View File

@ -3,14 +3,17 @@
{{'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}}
<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>
</div>
<div mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button>
<button [disabled]="code.invalid" mat-raised-button [mat-dialog-close]="code.value" color="accent">{{'security.2fa.totp.enable' | i18n}}</button>
<a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
<a [disabled]="code.invalid" mat-raised-button [mat-dialog-close]="code.value"
color="accent">{{'security.2fa.totp.enable' | i18n}}</a>
</div>

View File

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

View File

@ -3,9 +3,9 @@
<p>{{'vouchers.info' | i18n}}</p>
</mat-card-content>
<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}}
</button>
</a>
</mat-card-actions>
<mat-card-footer>
<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}}
</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>

View File

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

View File

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

View File

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

View File

@ -1,15 +1,16 @@
<video #qrCam></video>
<form fxLayout="row wrap" fxLayoutGap="24px grid">
<form class="flex wrap" fxLayoutGap="24px grid">
<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">
{{camera.label || camera.id}}
</mat-option>
</mat-select>
</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>
<input matInput hidden name="toggleFlash" />
</mat-form-field>

View File

@ -49,7 +49,7 @@
</table>
</mat-card-content>
<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>
</mat-dialog-content>

View File

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

View File

@ -1,12 +1,7 @@
<h3>{{'borrow.requests' | i18n}}</h3>
<div *ngIf="borrowRequests">
<div fxLayout="row" fxLayoutAlign="space-between start">
<form>
<mat-form-field floatLabel="always" appearance="none">
<div class="flex justify-between">
<mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.requests.mine' | i18n}}</mat-slide-toggle>
<input matInput hidden />
</mat-form-field>
</form>
</div>
<table mat-table matSort [dataSource]="borrowRequests.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="name">
@ -35,14 +30,14 @@
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.requests.actions' | i18n}} </th>
<td mat-cell *matCellDef="let borrowRequest" class="text-right">
<a mat-icon-button *ngIf="borrowRequest.user == userId">
<mat-icon (click)="edit(borrowRequest)">edit</mat-icon>
<a mat-icon-button *ngIf="borrowRequest.user == userId" (click)="edit(borrowRequest)">
<mat-icon>edit</mat-icon>
</a>
<a mat-icon-button *ngIf="borrowRequest.user == userId">
<mat-icon (click)="confirmDelete(borrowRequest)">delete</mat-icon>
<a mat-icon-button *ngIf="borrowRequest.user == userId" (click)="confirmDelete(borrowRequest)">
<mat-icon>delete</mat-icon>
</a>
<a mat-icon-button *ngIf="borrowRequest.item.owner != userId">
<mat-icon (click)="updateStatus(borrowRequest)">pending_actions</mat-icon>
<a mat-icon-button *ngIf="borrowRequest.item.owner != userId" (click)="updateStatus(borrowRequest)">
<mat-icon>pending_actions</mat-icon>
</a>
</td>
</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;
}
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
.mat-mdc-cell .mat-mdc-button {
padding-left: 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}}
</mat-error>
<input id="provider" name="provider" matInput hidden [value]="selectedProvider.id">
<mat-select [(ngModel)]="selectedProvider" placeholder="{{'security.2fa.provider' | i18n}}"
[ngModelOptions]="{standalone: true}">
<mat-form-field>
<mat-label>{{'security.2fa.provider' | i18n}}</mat-label>
<mat-select [(ngModel)]="selectedProvider" [ngModelOptions]="{standalone: true}">
<mat-option *ngFor="let provider of providers" [value]="provider">
{{'security.2fa.' + provider.id | i18n}}
</mat-option>
</mat-select>
</mat-form-field>
<a mat-raised-button (click)="request()"
*ngIf="selectedProvider && selectedProvider.request">{{'security.2fa.' + selectedProvider.id +
'.request'
| i18n}}</a>
<mat-form-field>
<input id="code" name="code" matInput placeholder="{{'security.2fa.code' | i18n}}" required
matAutofocus>
<mat-label>{{'security.2fa.code' | i18n}}</mat-label>
<input id="code" name="code" matInput required matAutofocus>
<mat-error>
{{'security.2fa.missing' | i18n}}
</mat-error>
@ -30,10 +32,10 @@
</mat-slide-toggle>
</mat-card-content>
<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;">
open_in_new
</mat-icon></button>
</mat-icon></a>
</mat-card-actions>
</mat-card>
</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}}
</mat-error>
<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>
{{'username.missing' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<input id="password" name="password" matInput type="password" placeholder="{{'password' | i18n}}"
required>
<mat-label>{{'password' | i18n}}</mat-label>
<input id="password" name="password" matInput type="password" required>
<mat-error>
{{'password.invalid.hint' | i18n}}
</mat-error>
@ -25,7 +26,8 @@
</mat-slide-toggle>
</mat-card-content>
<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
</mat-icon></button>
<a routerLink="/password" mat-raised-button color="warn">{{'password.forgot' | i18n}}</a>

View File

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

View File

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

View File

@ -1,8 +1,10 @@
<h3>{{'invites' | i18n}}</h3>
<div *ngIf="invites">
<div class="flex">
<mat-form-field>
<input matInput [formControl]="searchFormControl" placeholder="{{'invites.search' | i18n}}">
<mat-label>{{'invites.search' | i18n}}</mat-label>
<input matInput [formControl]="searchFormControl">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'invites.redeemed.filter' | i18n}}</mat-label>
@ -25,6 +27,7 @@
</mat-select-trigger>
</mat-select>
</mat-form-field>
</div>
<table mat-table matSort [dataSource]="invites.content">
<ng-container matColumnDef="starts">
@ -68,8 +71,8 @@
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> {{'invite.actions' | i18n}} </th>
<td mat-cell *matCellDef="let invite">
<a mat-icon-button>
<mat-icon (click)="edit(invite)">edit</mat-icon>
<a mat-icon-button (click)="edit(invite)">
<mat-icon>edit</mat-icon>
</a>
</td>
</ng-container>
@ -90,17 +93,19 @@
</div>
</mat-card-content>
<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}}
</button>
</a>
</mat-card-actions>
</mat-card>
<div *ngIf="others && others.content">
<h4>{{'invites.others' | i18n}}</h4>
<div class="flex">
<mat-form-field>
<input matInput [formControl]="searchOthersFormControl" placeholder="{{'invites.search' | i18n}}">
<mat-label>{{'invites.search' | i18n}}</mat-label>
<input matInput [formControl]="searchOthersFormControl">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'invites.redeemed.filter' | i18n}}</mat-label>
@ -123,7 +128,7 @@
</mat-select-trigger>
</mat-select>
</mat-form-field>
</div>
<table mat-table matSort [dataSource]="others.content">
<ng-container matColumnDef="note">
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>

View File

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

View File

@ -6,22 +6,23 @@
<ng-container matColumnDef="share">
<th mat-header-cell *matHeaderCellDef> {{'jitsi.share' | i18n}} </th>
<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>
</button>
</a>
</td>
</ng-container>
<ng-container matColumnDef="room">
<th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom">
<div class="flex align-center">
<a *ngIf="open(jitsiRoom, false)" mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank">
{{ jitsiRoom.room }}
<mat-icon style="font-size: 1em;">open_in_new
</mat-icon>
<mat-icon style="font-size: 1em;">open_in_new</mat-icon>
</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}}"
(click)="copyRoomUrlToClipboard(jitsiRoom)">
{{ jitsiRoom.room }}
</a>
@ -29,6 +30,7 @@
matTooltip="{{'urlshortener.create' | i18n}}">
<mat-icon>add_link</mat-icon>
</a>
</div>
</td>
</ng-container>
@ -68,8 +70,8 @@
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'jitsi.rooms.edit' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right">
<a mat-icon-button>
<mat-icon (click)="edit(jitsiRoom)">edit</mat-icon>
<a mat-icon-button (click)="edit(jitsiRoom)">
<mat-icon>edit</mat-icon>
</a>
</td>
</ng-container>
@ -77,8 +79,8 @@
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right">
<a mat-icon-button>
<mat-icon (click)="confirmDelete(jitsiRoom)">delete</mat-icon>
<a mat-icon-button (click)="confirmDelete(jitsiRoom)">
<mat-icon>delete</mat-icon>
</a>
</td>
</ng-container>
@ -99,16 +101,17 @@
<div *ngIf="jitsiRoomsQuota">
<p>{{'jitsi.rooms.left' | i18n:jitsiRoomsQuota}}</p>
<mat-form-field>
<input matInput placeholder="{{'jitsi.rooms.room' | i18n}}" formControlName="room"
[(ngModel)]="jitsiRoom.room" required pattern="[a-zA-Z0-9]+">
<mat-label>{{'jitsi.rooms.room' | i18n}}</mat-label>
<input matInput formControlName="room" [(ngModel)]="jitsiRoom.room" required pattern="[a-zA-Z0-9]+">
<mat-error>
{{'jitsi.rooms.error.room' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label>
<input matInput [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>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error>
@ -118,7 +121,8 @@
<mat-form-field *ngIf="jitsiRoom.starts">
<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>
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
<mat-error>
@ -128,7 +132,8 @@
<mat-form-field>
<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>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error>
@ -139,13 +144,14 @@
</div>
</mat-card-content>
<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}}
</button>
</mat-card-actions>
<mat-card-footer>
<a href="https://wiki.bstly.de/services/jitsi#rooms" class="help-button"
matTooltip="{{'help-button' | i18n}}" matTooltipPosition="above" target="_blank" mat-fab color="accent">
<a href="https://wiki.bstly.de/services/jitsi#rooms" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon>
</a>
</mat-card-footer>

View File

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

View File

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

View File

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

View File

@ -19,15 +19,16 @@
<mat-divider></mat-divider>
<br />
<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-card-content>
<mat-card-actions>
<button mat-raised-button type="submit"
<a mat-raised-button type="submit"
[disabled]="!searchFormControl.value || timeout > 0 || searchDisabled">
<mat-icon inline="true">{{'jukebox.search.icon' | i18n}}</mat-icon>
{{'jukebox.search.submit' | i18n}}
</button>
</a>
</mat-card-actions>
<mat-card-footer>
<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-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>
</div>

View File

@ -6,7 +6,8 @@
{{'security.2fa.totp.invalid' | i18n}}
</mat-error>
<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>
<mat-error>
{{'security.2fa.totp.missing' | i18n}}
@ -17,8 +18,8 @@
</mat-slide-toggle>
</mat-card-content>
<mat-card-actions>
<button type="submit" mat-raised-button color="primary"
[disabled]="form.invalid">{{'security.2fa.totp.login' | i18n}}</button>
<a type="submit" mat-raised-button color="primary"
[disabled]="form.invalid">{{'security.2fa.totp.login' | i18n}}</a>
</mat-card-actions>
</mat-card>
</form>

View File

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

View File

@ -6,14 +6,16 @@
{{'login.invalid' | i18n}}
</mat-error>
<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>
<mat-error>
{{'username.missing' | i18n}}
</mat-error>
</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>
<mat-error>
{{'password.invalid.hint' | i18n}}
@ -24,7 +26,7 @@
</mat-slide-toggle>
</mat-card-content>
<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>
<a routerLink="/password" mat-raised-button color="warn">{{'password.forgot' | i18n}}</a>
</mat-card-actions>

View File

@ -12,11 +12,11 @@
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'minetest.accounts.delete' | i18n}} </th>
<td mat-cell *matCellDef="let minetestAccount" class="text-right">
<a mat-icon-button color="warn">
<mat-icon matTooltip="{{'minetest.accounts.deletion' | i18n}}">warning</mat-icon>
<a mat-icon-button color="warn" matTooltip="{{'minetest.accounts.deletion' | i18n}}">
<mat-icon>warning</mat-icon>
</a>
<a mat-icon-button>
<mat-icon (click)="confirmDelete(minetestAccount.name)">delete</mat-icon>
<a mat-icon-button (click)="confirmDelete(minetestAccount.name)">
<mat-icon>delete</mat-icon>
</a>
</td>
</ng-container>
@ -33,7 +33,8 @@
<div *ngIf="minetestAccountsQuota">
<p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p>
<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]+">
<mat-error>
{{'minetest.accounts.error.name' | i18n}}
@ -42,7 +43,7 @@
</div>
</mat-card-content>
<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}}
</button>
</mat-card-actions>

View File

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

View File

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

View File

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

View File

@ -1,8 +1,10 @@
<h3>{{'partey.timeslots' | i18n}}</h3>
<div *ngIf="timeslots">
<div class="flex">
<mat-form-field>
<input matInput [formControl]="searchFormControl" placeholder="{{'partey.timeslots.filter.search' | i18n}}">
<mat-label>{{'partey.timeslots.filter.search' | i18n}}</mat-label>
<input matInput [formControl]="searchFormControl">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'partey.timeslots.filter.owner' | i18n}}</mat-label>
@ -22,18 +24,19 @@
</mat-option>
<mat-select-trigger>
<mat-icon inline="true" *ngIf="typeFormControl.value ">{{'partey.timeslots.type.' + typeFormControl.value +
'.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ? typeFormControl.value
'.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ?
typeFormControl.value
: 'all') | i18n}}
</mat-select-trigger>
</mat-select>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="afterPicker" [formControl]="afterFormControl"
placeholder="{{'partey.timeslots.filter.after' | i18n}}">
<mat-label>{{'partey.timeslots.filter.after' | i18n}}</mat-label>
<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)">
<ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th>
@ -79,8 +82,8 @@
</th>
<td mat-cell *matCellDef="let timeslot">
<div *ngIf="timeslot.owner == userId">
<button *ngIf="timeslot.type == 'AUDIO_STREAM'" mat-raised-button
(click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</button>
<a *ngIf="timeslot.type == 'AUDIO_STREAM'" mat-raised-button
(click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</a>
<span *ngIf="timeslot.type == 'VIDEO_STREAM'"> {{ timeslot.stream }} </span>
</div>
</td>
@ -89,8 +92,8 @@
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right">
<a *ngIf="timeslot.owner == userId" mat-icon-button>
<mat-icon (click)="openEdit(timeslot)">edit</mat-icon>
<a *ngIf="timeslot.owner == userId" mat-icon-button (click)="openEdit(timeslot)">
<mat-icon>edit</mat-icon>
</a>
</td>
</ng-container>
@ -98,8 +101,8 @@
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right">
<a *ngIf="timeslot.owner == userId" mat-icon-button>
<mat-icon (click)="confirmDelete(timeslot)">delete</mat-icon>
<a *ngIf="timeslot.owner == userId" mat-icon-button (click)="confirmDelete(timeslot)">
<mat-icon>delete</mat-icon>
</a>
</td>
</ng-container>
@ -120,12 +123,12 @@
<p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p>
</div>
</mat-card-content>
<mat-card-actions *ngIf="timeslotsQuota">
<mat-card-actions *ngIf="timeslotsQuota" class="flex wrap">
<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>
{{'partey.timeslots.create.' + type | i18n}}
</button>
</a>
<br>
<br>
</div>

View File

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

View File

@ -6,14 +6,16 @@
{{'password.reset.tokenInvalid' | i18n}}
</mat-error>
<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">
<mat-error *ngFor="let error of form.get('password').errors | keyvalue">
{{error.key}}
</mat-error>
</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">
<mat-error>
{{'password.not-match' | i18n}}
@ -21,7 +23,7 @@
</mat-form-field>
</mat-card-content>
<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}}
</button>

View File

@ -3,7 +3,8 @@
<mat-card-content>
<h2>{{'password.request' | i18n}}</h2>
<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>
{{'username.missing' | i18n}}
</mat-error>
@ -11,12 +12,13 @@
<mat-form-field>
<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>
</mat-form-field>
</mat-card-content>
<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}}
</button>

View File

@ -18,17 +18,19 @@
color="warn">{{'register.token.locked.action' | i18n}}</a>
</mat-error>
<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">
<mat-error>
{{'username.error' | i18n}}
</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>
</a>
</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">
<mat-error>
<div *ngFor="let error of form.get('password').errors | keyvalue">
@ -37,7 +39,8 @@
</mat-error>
</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">
<mat-error>
{{'password.not-match' | i18n}}
@ -52,7 +55,8 @@
matTooltip="{{'email.primary.hint' | i18n:model.username}}">info</mat-icon>
<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">
<mat-error>
{{'email.invalid' | i18n}}
@ -69,7 +73,7 @@
<mat-divider></mat-divider>
</mat-card-content>
<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}}
</button>

View File

@ -16,9 +16,9 @@
i18n}}</a>
<a mat-raised-button (click)="copyKey(privkey)">{{'pgp.privateKey.copyKey' |
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>
</button>
</a>
</mat-dialog-actions>
<br />
<mat-dialog-actions>
@ -26,5 +26,5 @@
<mat-slide-toggle [(ngModel)]="data.confirmClose" [disabled]="!downloaded">
{{'pgp.privateKey.confirmStore' | i18n}}
</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>

View File

@ -3,15 +3,15 @@
<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)">
<mat-chip *ngFor="let dict of dicts" cdkDrag [selected]="dict.selected" (click)="toggle(dict)">
{{dict.name}}
</mat-chip>
</mat-chip-list>
</mat-chip-listbox>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onOkClick()">Ok</button>
<a mat-button (click)="onOkClick()">Ok</a>
</div>

View File

@ -6,9 +6,9 @@
<mat-list-item *ngFor="let item of items">
<mat-icon mat-list-icon>plus_one</mat-icon>
{{ 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>
</button>
</a>
</mat-list-item>
</mat-list>
@ -16,9 +16,9 @@
</mat-card-content>
<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}}
</button>
</a>
<ng-container *ngIf="!auth.authenticated">
<p>{{'tokens.register' | i18n}}</p>
@ -65,14 +65,16 @@
{{'tokens.redeemed' | i18n}}
</mat-error>
<mat-form-field>
<input matInput placeholder="{{'token' | i18n}}" formControlName="token" matAutofocus>
<mat-label>{{'token' | i18n}}</mat-label>
<input matInput formControlName="token" matAutofocus>
<mat-error>
{{'tokens.provide-valid' | i18n}}
</mat-error>
</mat-form-field>
</mat-card-content>
<mat-card-actions>
<button 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-footer>
<a href="https://wiki.bstly.de/services/webstly#token" class="help-button"

View File

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

View File

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

View File

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

View File

@ -4,17 +4,18 @@
<h2>{{'urlshortener.password' | i18n}}
</h2>
<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-error *ngIf="invalidPassword">
{{'urlshortener.password.invalid' | i18n}}
</mat-error>
</mat-card-content>
<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' |
i18n}}<mat-icon style="font-size: 1em;">open_in_new
</mat-icon></button>
</mat-icon></a>
</mat-card-actions>
</mat-card>
</form>

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import {Injectable} from '@angular/core';
import {ReplaySubject, of} from 'rxjs';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ReplaySubject, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {environment} from './../../environments/environment';
import { environment } from './../../environments/environment';
@Injectable({
providedIn: 'root',
@ -27,6 +27,7 @@ export class AuthService {
return this.http.get(environment.apiUrl + "/auth/me");
}
login(loginModel) {
return this.http.post(environment.apiUrl + "/auth/login", loginModel);
}
@ -37,11 +38,15 @@ export class AuthService {
passwordRequest(username) {
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) {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
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}}
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button [mat-dialog-close]="true" color="accent" matAutofocus>{{'confirm' | i18n}}</button>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button>
<a mat-raised-button [mat-dialog-close]="true" color="accent" matAutofocus>{{'confirm' | i18n}}</a>
<a mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</a>
</mat-dialog-actions>

View File

@ -1,18 +1,21 @@
<div fxLayout="row wrap" fxLayoutAlign="start stretch">
<div fxFlex="33%">
<div class="flex wrap" fxLayoutAlign="start stretch">
<div [style.flex-basis]="'33%'">
<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>
</div>
<div fxFlex="33%">
<div [style.flex-basis]="'33%'">
<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">
</mat-form-field>
</div>
<div fxFlex="33%">
<div [style.flex-basis]="'33%'">
<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">
<mat-hint align="end">{{duration}}</mat-hint>
</mat-form-field>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,3 @@
table {
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">
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'quotas.name' | i18n}} </th>
<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}}
</td>
</ng-container>

View File

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

View File

@ -1,6 +1,24 @@
@import '../../../variables.scss';
mat-card {
.service-grid {
display: grid;
column-gap: 24px;
row-gap: 24px;
@media (min-width: 576px) {
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
grid-template-columns: 1fr 1fr;
}
@media (min-width: 992px) {
grid-template-columns: 1fr 1fr 1fr;
}
mat-card {
display: flex;
flex-direction: column;
height: 100%;
@ -14,9 +32,19 @@ mat-card {
}
}
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
@use '@angular/material'as mat;
@use '@angular/material' as mat;
// For more information: https://material.angular.io/guide/theming
// Plus imports for other components in your app.
@ -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;
}
a {
text-decoration: none;
}
button {
font-size: inherit;
}
app-root,
app-main {
height: 100%;
@ -107,6 +115,12 @@ app-main {
mat-card {
max-width: 400px;
margin: 2em auto;
mat-card-actions {
&>* {
margin-right: 15px;
}
}
}
mat-form-field {
@ -156,6 +170,31 @@ qr-code canvas {
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 {
flex: 1 1 auto;
}
@ -182,33 +221,33 @@ mat-sidenav-container {
margin-left: auto;
margin-bottom: 15px;
@media screen and (min-width: 576px) {
@media (min-width: 576px) {
width: 540px;
}
@media screen and (min-width: 768px) {
@media (min-width: 768px) {
width: 580px;
}
@media screen and (min-width: 992px) {
@media (min-width: 992px) {
width: 820px;
}
@media screen and (min-width: 1200px) {
@media (min-width: 1200px) {
width: 1000px;
}
}
.text-center {
text-align: center;
text-align: center !important;
}
.text-justify {
text-align: justify;
text-align: justify !important;
}
.text-right {
text-align: right;
text-align: right !important;
}
.text-warning {
@ -217,7 +256,7 @@ mat-sidenav-container {
.align-right {
display: flex;
padding: 21px 0;
padding: 21px !important;
justify-content: flex-end;
}
@ -322,11 +361,16 @@ table {
}
}
.help-button {
float: right;
mat-card-footer,
footer {
position: relative;
.help-button {
float: right;
position: absolute;
top: -40px;
right: 15px;
}
}
mat-card.success {

View File

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