update borrow + fix invite
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<h2>{{'borrow' | i18n}}</h2>
|
||||
|
||||
<nav mat-tab-nav-bar>
|
||||
<a mat-tab-link routerLink="items" routerLinkActive="active-tab" #rlaitems="routerLinkActive"
|
||||
[active]="rlaitems.isActive">{{'borrow.items'
|
||||
| i18n}}</a>
|
||||
<a mat-tab-link routerLink="requests" routerLinkActive="active-tab" #rlarequests="routerLinkActive"
|
||||
[active]="rlarequests.isActive">{{'borrow.requests'
|
||||
| i18n}}</a>
|
||||
<a mat-tab-link routerLink="proving" routerLinkActive="active-tab" #rlaproving="routerLinkActive"
|
||||
[active]="rlaproving.isActive">{{'borrow.proving'
|
||||
| i18n}}</a>
|
||||
</nav>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow',
|
||||
templateUrl: './borrow.component.html',
|
||||
styleUrls: [ './borrow.component.scss' ]
|
||||
})
|
||||
export class BorrowComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<h2 mat-dialog-title>{{(create ? 'borrow.items.create' : 'borrow.items.edit') | i18n}}</h2>
|
||||
<mat-dialog-content>
|
||||
<form [formGroup]="form">
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'borrow.items.name' | i18n}}" 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-error>
|
||||
{{'borrow.items.error.description' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'borrow.items.url' | i18n}}" 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%">
|
||||
<input matInput hidden formControlName="minDuration" />
|
||||
<label>{{'borrow.items.minDuration' | i18n}}</label>
|
||||
<app-durationpicker formControlName="minDuration"></app-durationpicker>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.minDuration' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field floatLabel="always" appearance="none" 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>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.maxDuration' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid">
|
||||
<mat-form-field>
|
||||
<mat-select formControlName="availability" placeholder="{{'borrow.items.availability' | i18n}}">
|
||||
<mat-option value="ALWAYS">
|
||||
{{'borrow.items.availability.ALWAYS' | i18n}}
|
||||
</mat-option>
|
||||
<mat-option value="PERIOD">
|
||||
{{'borrow.items.availability.PERIOD' | i18n}}
|
||||
</mat-option>
|
||||
<mat-option value="MANUAL">
|
||||
{{'borrow.items.availability.MANUAL' | i18n}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.availability' | i18n}}
|
||||
</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-error>
|
||||
{{'borrow.items.error.email' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<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}}">
|
||||
<mat-icon>control_point_duplicate</mat-icon>
|
||||
</button>
|
||||
<ng-container *ngFor="let slotForm of slots.controls; let i = index">
|
||||
<div [formGroup]="slotForm" fxLayout="row wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid">
|
||||
<mat-form-field>
|
||||
<input matInput [ngxMatDatetimePicker]="slotStartPicker" formControlName="start"
|
||||
placeholder="{{'borrow.items.slot.start' | i18n}}">
|
||||
<mat-datepicker-toggle matSuffix [for]="slotStartPicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #slotStartPicker></ngx-mat-datetime-picker>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.slot.start' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput [ngxMatDatetimePicker]="slotEndPicker" formControlName="end"
|
||||
placeholder="{{'borrow.items.slot.end' | i18n}}">
|
||||
<mat-datepicker-toggle matSuffix [for]="slotEndPicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #slotEndPicker></ngx-mat-datetime-picker>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.slot.end' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div>
|
||||
<button 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}}">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<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}}">
|
||||
<mat-icon>control_point_duplicate</mat-icon>
|
||||
</button>
|
||||
<ng-container *ngFor="let slotForm of slots.controls; let i = index">
|
||||
<div [formGroup]="slotForm" fxLayout="row wrap" fxLayoutAlign="start stretch" fxLayoutGap="12px grid">
|
||||
<mat-form-field>
|
||||
<mat-select formControlName="startDay" placeholder="{{'borrow.items.slot.startDay' | i18n}}">
|
||||
<mat-option *ngFor="let day of weekdays" [value]="day">
|
||||
{{'borrow.items.slot.day.' + day | i18n}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.slot.startDay' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput format="24" [ngxMatTimepicker]="startTimePicker" formControlName="startTime"
|
||||
placeholder="{{'borrow.items.slot.startTime' | i18n}}">
|
||||
<mat-icon matSuffix (click)="startTimePicker.open()">
|
||||
watch_later
|
||||
</mat-icon>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.slot.startTime' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<ngx-mat-timepicker #startTimePicker></ngx-mat-timepicker>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select formControlName="endDay" placeholder="{{'borrow.items.slot.endDay' | i18n}}">
|
||||
<mat-option *ngFor="let day of weekdays" [value]="day">
|
||||
{{'borrow.items.slot.day.' + day | i18n}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.slot.endDay' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput format="24" [ngxMatTimepicker]="endTimePicker" formControlName="endTime"
|
||||
placeholder="{{'borrow.items.slot.endTime' | i18n}}">
|
||||
<mat-icon matSuffix (click)="endTimePicker.open()">
|
||||
watch_later
|
||||
</mat-icon>
|
||||
<mat-error>
|
||||
{{'borrow.items.error.slot.endTime' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<ngx-mat-timepicker #endTimePicker></ngx-mat-timepicker>
|
||||
|
||||
<div>
|
||||
<button 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}}">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<div fxLayout="row" 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">
|
||||
<mat-icon>save</mat-icon>{{(create ? 'borrow.items.create' : 'borrow.items.save') | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<button *ngIf="borrowItemId" mat-button color="warn" (click)="confirmDelete()">
|
||||
<mat-icon>delete</mat-icon> {{ 'borrow.items.delete' |
|
||||
i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,7 @@
|
||||
mat-form-field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-dialog-actions>div {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<h3>{{'borrow.items' | i18n}}</h3>
|
||||
<div *ngIf="borrowItems">
|
||||
<div fxLayout="row" fxLayoutAlign="space-between start">
|
||||
<form>
|
||||
<mat-form-field>
|
||||
<input matInput [formControl]="searchFormControl" placeholder="{{'borrow.items.search' | i18n}}">
|
||||
</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>
|
||||
</div>
|
||||
<table mat-table matSort [dataSource]="borrowItems.content" (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'borrow.items.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowItem"> {{ borrowItem.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'borrow.items.description' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowItem"> {{ borrowItem.description}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="availability">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'borrow.items.availability' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowItem"> {{ 'borrow.items.availability.' + borrowItem.availability | i18n}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="url">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'borrow.items.url' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowItem">
|
||||
<a *ngIf="borrowItem.url" mat-button color="accent" href="{{ borrowItem.url }}" target="_blank">
|
||||
{{ borrowItem.url }}
|
||||
<mat-icon style="font-size: 1em;">open_in_new</mat-icon>
|
||||
</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.items.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowItem" class="text-right">
|
||||
<a mat-icon-button *ngIf="borrowItem.owner == userId">
|
||||
<mat-icon (click)="request(borrowItem)">pending_actions</mat-icon>
|
||||
</a>
|
||||
<a mat-icon-button *ngIf="borrowItem.owner == userId">
|
||||
<mat-icon (click)="edit(borrowItem)">edit</mat-icon>
|
||||
</a>
|
||||
<a mat-icon-button *ngIf="borrowItem.owner == userId">
|
||||
<mat-icon (click)="confirmDelete(borrowItem)">delete</mat-icon>
|
||||
</a>
|
||||
<a mat-icon-button *ngIf="borrowItem.owner != userId">
|
||||
<mat-icon (click)="request(borrowItem)">pending_actions</mat-icon>
|
||||
</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="borrowItemColumns"></tr>
|
||||
<tr mat-row *matRowDef="let myRowData; columns: borrowItemColumns"></tr>
|
||||
</table>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="borrowItems.totalElements" [pageSize]="borrowItems.size"
|
||||
(page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
.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 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.align-right{
|
||||
display: flex;
|
||||
padding: 21px 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { Sort } from '@angular/material/sort';
|
||||
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import { ConfirmDialog } from '../../../ui/confirm/confirm.component';
|
||||
import { BorrowItemsService } from './../../../services/borrow.service';
|
||||
import { AuthService } from './../../../services/auth.service';
|
||||
import { BorrowRequestEditComponent } from '../requests/requests.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow-items',
|
||||
templateUrl: './items.component.html',
|
||||
styleUrls: [ './items.component.scss' ]
|
||||
})
|
||||
export class BorrowItemsComponent implements OnInit {
|
||||
|
||||
borrowItems: any[];
|
||||
page: any = { page: 0, size: 10, sort: "id", desc: false };
|
||||
pageSizeOptions: number[] = [ 5, 10, 25, 50 ];
|
||||
searchFormControl = new FormControl();
|
||||
ownerFormControl = new FormControl();
|
||||
userId: any;
|
||||
|
||||
borrowItemColumns = [ "name", "description", "availability", "url", "actions" ];
|
||||
|
||||
constructor(private borrowItemsService: BorrowItemsService,
|
||||
private authService: AuthService,
|
||||
public dialog: MatDialog) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authService.auth.subscribe((auth: any) => {
|
||||
if (auth.principal && auth.principal.userId) {
|
||||
this.userId = auth.principal.userId;
|
||||
}
|
||||
});
|
||||
|
||||
this.searchFormControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
|
||||
this.borrowItemsService.getItems(0, this.page.size, this.page.sort, this.page.desc, value, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowItems = data;
|
||||
}, (error) => { })
|
||||
})
|
||||
|
||||
this.ownerFormControl.valueChanges.subscribe(value => {
|
||||
this.borrowItemsService.getItems(0, this.page.size, this.page.sort, this.page.desc, this.searchFormControl.value, value).subscribe((data: any) => {
|
||||
this.borrowItems = data;
|
||||
}, (error) => { })
|
||||
})
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this.borrowItemsService.getItems(this.page.page, this.page.size, this.page.sort, this.page.desc, this.searchFormControl.value, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowItems = data;
|
||||
})
|
||||
}
|
||||
|
||||
updatePages(event: PageEvent) {
|
||||
this.page.page = event.pageIndex;
|
||||
this.page.size = event.pageSize;
|
||||
this.borrowItemsService.getItems(this.page.page, this.page.size, this.page.sort, this.page.desc, this.searchFormControl.value, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowItems = data;
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
updateSort(sort: Sort) {
|
||||
if (sort.direction == "") {
|
||||
this.page.sort = "id";
|
||||
this.page.desc = false;
|
||||
} else {
|
||||
this.page.sort = sort.active;
|
||||
this.page.desc = sort.direction == "desc";
|
||||
}
|
||||
this.borrowItemsService.getItems(this.page.page, this.page.size, this.page.sort, this.page.desc, this.searchFormControl.value, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowItems = data;
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
edit(borrowItem) {
|
||||
const dialogRef = this.dialog.open(BorrowItemEditComponent, {
|
||||
data: borrowItem,
|
||||
minWidth: '80%'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
create() {
|
||||
const dialogRef = this.dialog.open(BorrowItemEditComponent, {
|
||||
data: {},
|
||||
minWidth: '80%'
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
request(borrowItem) {
|
||||
const dialogRef = this.dialog.open(BorrowRequestEditComponent, {
|
||||
data: {
|
||||
"user": this.userId,
|
||||
"borrowItem": borrowItem
|
||||
}
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmDelete(borrowItem) {
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
'label': 'borrow.items.confirmDelete',
|
||||
'args': [ borrowItem.name ]
|
||||
}
|
||||
})
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.borrowItemsService.deleteItem(borrowItem.id).subscribe((result: any) => {
|
||||
this.refresh();
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow-item-edit',
|
||||
templateUrl: './item.edit.html',
|
||||
styleUrls: [ './item.edit.scss' ]
|
||||
})
|
||||
export class BorrowItemEditComponent {
|
||||
|
||||
borrowItemId: number;
|
||||
create: boolean = false;
|
||||
form: FormGroup;
|
||||
weekdays: string[] = [ 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY' ];
|
||||
|
||||
constructor(private borrowItemsService: BorrowItemsService,
|
||||
private formBuilder: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
public dialogRef: MatDialogRef<BorrowItemEditComponent>,
|
||||
public dialog: MatDialog) {
|
||||
|
||||
this.form = this.formBuilder.group({
|
||||
name: [ '', Validators.required ],
|
||||
description: [ '', Validators.nullValidator ],
|
||||
url: [ '', Validators.nullValidator ],
|
||||
minDuration: [ '', Validators.nullValidator ],
|
||||
maxDuration: [ '', Validators.nullValidator ],
|
||||
availability: [ '', Validators.required ],
|
||||
autoAccept: [ '', Validators.nullValidator ],
|
||||
emailNotification: [ '', Validators.nullValidator ],
|
||||
email: [ '', Validators.nullValidator ],
|
||||
slots: this.formBuilder.array([])
|
||||
}, { validators: [ durationValidator ] })
|
||||
|
||||
this.form.get('availability').valueChanges.subscribe((value) => {
|
||||
this.slots.clear();
|
||||
if (value == 'MANUAL') {
|
||||
this.addManualSlot('', '', '');
|
||||
} else if (value == 'PERIOD') {
|
||||
this.addPeriodSlot('', '', '', '', '');
|
||||
}
|
||||
})
|
||||
|
||||
if (data.id) {
|
||||
this.borrowItemId = + data.id;
|
||||
} else {
|
||||
this.create = true;
|
||||
this.borrowItemId = undefined;
|
||||
}
|
||||
|
||||
this.form.get('name').setValue(data.name, { emitEvent: false });
|
||||
this.form.get('description').setValue(data.description, { emitEvent: false });
|
||||
this.form.get('url').setValue(data.url, { emitEvent: false });
|
||||
this.form.get('minDuration').setValue(data.minDuration, { emitEvent: false });
|
||||
this.form.get('maxDuration').setValue(data.maxDuration, { emitEvent: false });
|
||||
this.form.get('availability').setValue(data.availability || 'ALWAYS', { emitEvent: false });
|
||||
this.form.get('autoAccept').setValue(data.autoAccept, { emitEvent: false });
|
||||
this.form.get('emailNotification').setValue(data.emailNotification, { emitEvent: false });
|
||||
this.form.get('email').setValue(data.email), { emitEvent: false };
|
||||
if (data.slots) {
|
||||
for (let slot of data.slots) {
|
||||
if (data.availability == 'MANUAL') {
|
||||
this.addManualSlot(slot.id, slot.start, slot.end);
|
||||
} else if (data.availability == 'PERIOD') {
|
||||
this.addPeriodSlot(slot.id, slot.startDay, slot.startTime, slot.endDay, slot.endTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formToBorrowItem(): any {
|
||||
const borrowItem: any = {};
|
||||
borrowItem.id = this.borrowItemId;
|
||||
borrowItem.name = this.form.get('name').value;
|
||||
borrowItem.description = this.form.get('description').value;
|
||||
borrowItem.url = this.form.get('url').value;
|
||||
borrowItem.minDuration = this.form.get('minDuration').value;
|
||||
borrowItem.maxDuration = this.form.get('maxDuration').value;
|
||||
borrowItem.availability = this.form.get('availability').value;
|
||||
borrowItem.autoAccept = this.form.get('autoAccept').value;
|
||||
borrowItem.emailNotification = this.form.get('emailNotification').value;
|
||||
borrowItem.email = this.form.get('email').value;
|
||||
const slots: any[] = [];
|
||||
for (let slotForm of this.slots.controls) {
|
||||
if (borrowItem.availability == 'MANUAL') {
|
||||
slots.push({
|
||||
id: slotForm.get('id').value,
|
||||
start: slotForm.get('start').value,
|
||||
end: slotForm.get('end').value,
|
||||
type: 'MANUAL'
|
||||
})
|
||||
} else if (borrowItem.availability == 'PERIOD') {
|
||||
slots.push({
|
||||
id: slotForm.get('id').value,
|
||||
startDay: slotForm.get('startDay').value,
|
||||
startTime: slotForm.get('startTime').value,
|
||||
endDay: slotForm.get('endDay').value,
|
||||
endTime: slotForm.get('endTime').value,
|
||||
type: 'PERIOD'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
borrowItem.slots = slots;
|
||||
return borrowItem;
|
||||
}
|
||||
|
||||
get slots() {
|
||||
return this.form.controls[ "slots" ] as FormArray;
|
||||
}
|
||||
|
||||
deleteSlot(index: number) {
|
||||
this.slots.removeAt(index);
|
||||
}
|
||||
|
||||
dublicateSlot(index: number) {
|
||||
this.slots.push(this.slots.at(index));
|
||||
}
|
||||
|
||||
addManualSlot(id, start, end) {
|
||||
const manualSlotForm = this.formBuilder.group({
|
||||
id: [ id, Validators.required ],
|
||||
start: [ start, Validators.required ],
|
||||
end: [ end, Validators.required ]
|
||||
}, { validators: [ manualSlotValidator ] });
|
||||
this.slots.push(manualSlotForm);
|
||||
}
|
||||
|
||||
addPeriodSlot(id, startDay: string, startTime: string, endDay: string, endTime: string) {
|
||||
if (startTime.length == 8) {
|
||||
startTime = startTime.substr(0, 5);
|
||||
}
|
||||
if (endTime.length == 8) {
|
||||
endTime = endTime.substr(0, 5);
|
||||
}
|
||||
const perdiodSlotForm = this.formBuilder.group({
|
||||
id: [ id, Validators.required ],
|
||||
startDay: [ startDay, Validators.required ],
|
||||
startTime: [ startTime, Validators.required ],
|
||||
endDay: [ endDay, Validators.required ],
|
||||
endTime: [ endTime, Validators.required ]
|
||||
}, { validators: [ periodSlotValidator ] });
|
||||
this.slots.push(perdiodSlotForm);
|
||||
}
|
||||
|
||||
|
||||
save() {
|
||||
this.borrowItemsService.createOrUpdateItem(this.formToBorrowItem()).subscribe((data: any) => {
|
||||
this.dialogRef.close(data);
|
||||
}, (error) => {
|
||||
if (error.status == 409) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmDelete() {
|
||||
const borrowItem = this.formToBorrowItem();
|
||||
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
'label': 'borrow.items.confirmDelete',
|
||||
'args': [ borrowItem.name ]
|
||||
}
|
||||
})
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.borrowItemsService.deleteItem(borrowItem.id).subscribe((result: any) => {
|
||||
this.dialogRef.close(true);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const durationValidator: ValidatorFn = (fg: FormGroup) => {
|
||||
const minDuration = fg.get('minDuration').value;
|
||||
const maxDuration = fg.get('maxDuration').value;
|
||||
fg.get('minDuration').setErrors(null);
|
||||
fg.get('maxDuration').setErrors(null);
|
||||
|
||||
if (minDuration && maxDuration && (moment.duration(minDuration).asMinutes() >= moment.duration(maxDuration).asMinutes())) {
|
||||
fg.get('minDuration').setErrors([ 'INVALID' ]);
|
||||
fg.get('maxDuration').setErrors([ 'INVALID' ]);
|
||||
return { 'INVALID': true };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
const manualSlotValidator: ValidatorFn = (fg: FormGroup) => {
|
||||
const start = fg.get('start').value;
|
||||
const end = fg.get('end').value;
|
||||
|
||||
fg.get('id').setErrors(null);
|
||||
fg.get('start').setErrors(null);
|
||||
fg.get('end').setErrors(null);
|
||||
|
||||
if (!start) {
|
||||
fg.get('start').setErrors([ 'MISSING_DATES' ]);
|
||||
return { 'MISSING_DATES': true };
|
||||
}
|
||||
if (!end) {
|
||||
fg.get('end').setErrors([ 'MISSING_DATES' ]);
|
||||
return { 'MISSING_DATES': true };
|
||||
}
|
||||
|
||||
if (start >= end) {
|
||||
fg.get('start').setErrors([ 'INVALID_DATES' ]);
|
||||
fg.get('end').setErrors([ 'INVALID_DATES' ]);
|
||||
return { 'INVALID_DATES': true };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const periodSlotValidator: ValidatorFn = (fg: FormGroup) => {
|
||||
const weekdays: string[] = [ 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY' ];
|
||||
const startDay = fg.get('startDay').value;
|
||||
const startTime = fg.get('startTime').value;
|
||||
const endDay = fg.get('endDay').value;
|
||||
const endTime = fg.get('endTime').value;
|
||||
|
||||
fg.get('id').setErrors(null);
|
||||
fg.get('startDay').setErrors(null);
|
||||
fg.get('startTime').setErrors(null);
|
||||
fg.get('endDay').setErrors(null);
|
||||
fg.get('endTime').setErrors(null);
|
||||
|
||||
if (!startDay) {
|
||||
fg.get('startDay').setErrors([ 'MISSING_DATES' ]);
|
||||
return { 'MISSING_DATES': true };
|
||||
}
|
||||
if (!startTime) {
|
||||
fg.get('startTime').setErrors([ 'MISSING_DATES' ]);
|
||||
return { 'MISSING_DATES': true };
|
||||
}
|
||||
if (!endDay) {
|
||||
fg.get('endDay').setErrors([ 'MISSING_DATES' ]);
|
||||
return { 'MISSING_DATES': true };
|
||||
}
|
||||
if (!endTime) {
|
||||
fg.get('endTime').setErrors([ 'MISSING_DATES' ]);
|
||||
return { 'MISSING_DATES': true };
|
||||
}
|
||||
|
||||
if (weekdays.indexOf(startDay) > weekdays.indexOf(endDay)) {
|
||||
fg.get('startDay').setErrors([ 'INVALID_DAY' ]);
|
||||
fg.get('endDay').setErrors([ 'INVALID_DAY' ]);
|
||||
return { 'INVALID_DAY': true };
|
||||
}
|
||||
|
||||
if (weekdays.indexOf(startDay) == weekdays.indexOf(endDay) && startTime >= endTime) {
|
||||
fg.get('startTime').setErrors([ 'INVALID_TIME' ]);
|
||||
fg.get('endTime').setErrors([ 'INVALID_TIME' ]);
|
||||
return { 'INVALID_TIME': true };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<video #qrCam></video>
|
||||
|
||||
<form fxLayout="row wrap" fxLayoutGap="24px grid">
|
||||
<mat-form-field>
|
||||
<mat-select [formControl]="camera" name="camera" placeholder="{{'borrow.proving.camera' | i18n}}">
|
||||
<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-slide-toggle [formControl]="toggleFlash" disabled>{{'borrow.proving.flash' | i18n}}</mat-slide-toggle>
|
||||
<input matInput hidden name="toggleFlash" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
<mat-card class="warn" *ngIf="initialized && noCamera">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<mat-icon>no_photography</mat-icon>
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>{{'borrow.proving.noCamera' | i18n}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<p>
|
||||
{{'borrow.proving.noCamera.text' | i18n}}
|
||||
</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@@ -0,0 +1,4 @@
|
||||
video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { AfterViewInit, Component, ElementRef, ViewChild, Inject } from '@angular/core';
|
||||
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
import { BorrowRequestsService } from 'src/app/services/borrow.service';
|
||||
|
||||
import QrScanner from 'qr-scanner';
|
||||
import { HttpResponse } from '@angular/common/http';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow-proving',
|
||||
templateUrl: './proving.component.html',
|
||||
styleUrls: [ './proving.component.scss' ]
|
||||
})
|
||||
export class BorrowProvingComponent implements AfterViewInit {
|
||||
|
||||
@ViewChild('qrCam') private qrCamVideo: ElementRef;
|
||||
toggleFlash = new FormControl();
|
||||
camera = new FormControl();
|
||||
|
||||
qrScanner: QrScanner;
|
||||
cameras: QrScanner.Camera[];
|
||||
noCamera: boolean = true;
|
||||
initialized: boolean = false;
|
||||
|
||||
constructor(private borrowRequestService: BorrowRequestsService, private dialog: MatDialog) {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.qrScanner = new QrScanner(this.qrCamVideo.nativeElement, (result) => {
|
||||
this.onResult(result);
|
||||
});
|
||||
|
||||
this.qrScanner.start().then(() => {
|
||||
this.initialized = true;
|
||||
QrScanner.listCameras(true).then((cameras: QrScanner.Camera[]) => {
|
||||
this.cameras = cameras;
|
||||
if (this.cameras.length) {
|
||||
this.noCamera = false;
|
||||
this.camera.setValue(this.cameras[ 0 ].id, { emitEvent: false });
|
||||
this.camera.valueChanges.subscribe((value) => {
|
||||
this.qrScanner.setCamera(value);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.qrScanner.hasFlash().then(hasFlash => {
|
||||
if (hasFlash) {
|
||||
this.toggleFlash.enable();
|
||||
this.toggleFlash.valueChanges.subscribe(value => {
|
||||
if (value) {
|
||||
this.qrScanner.turnFlashOn();
|
||||
} else {
|
||||
this.qrScanner.turnFlashOff();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.toggleFlash.disable();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onResult(result: string) {
|
||||
this.qrScanner.pause();
|
||||
this.borrowRequestService.verifyRequest(result).subscribe((response) => {
|
||||
this.openResultDialog(response, true);
|
||||
}, (error) => {
|
||||
this.openResultDialog(error, false);
|
||||
});
|
||||
}
|
||||
|
||||
openResultDialog(response, success: boolean) {
|
||||
const dialogRef = this.dialog.open(BorrowProvingResultDialog, {
|
||||
data: { "success": success, "response": response },
|
||||
minWidth: '400px',
|
||||
panelClass: "mat-card-dialog",
|
||||
closeOnNavigation: false,
|
||||
disableClose: true,
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
this.qrScanner.start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow-prooving-result',
|
||||
templateUrl: 'proving.dialog.html',
|
||||
styleUrls: [ './proving.dialog.scss' ]
|
||||
})
|
||||
export class BorrowProvingResultDialog {
|
||||
|
||||
result: any;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) private data: any) {
|
||||
this.result = JSON.parse(JSON.stringify(data));
|
||||
console.log(this.result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<mat-dialog-content>
|
||||
<mat-card [ngClass]="result.success ? 'success' : (result.response.status == 412 ? 'accent' : 'warn')">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<mat-icon *ngIf="!result.success && result.response.status != 412">block</mat-icon>
|
||||
<mat-icon *ngIf="!result.success && result.response.status == 412">help_outline</mat-icon>
|
||||
<mat-icon *ngIf="result.success">check</mat-icon>
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>{{'borrow.proving.' + (result.success ? 'valid' : 'invalid') | i18n}}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<p>
|
||||
{{'borrow.proving.' + (result.success ? 'valid' : 'invalid') + '.text' | i18n}}
|
||||
</p>
|
||||
<p *ngFor="let error of result.response.error">
|
||||
<ng-container [ngSwitch]="error.field">
|
||||
<span *ngSwitchCase="'nbf'">
|
||||
{{'borrow.proving.error.' + error.code | i18n:(error.defaultMessage | datef)}}
|
||||
</span>
|
||||
<span *ngSwitchCase="'exp'">
|
||||
{{'borrow.proving.error.' + error.code | i18n:(error.defaultMessage | datef)}}
|
||||
</span>
|
||||
<span *ngSwitchDefault>
|
||||
{{'borrow.proving.error.default.' + error.code | i18n:error.defaultMessage}}
|
||||
</span>
|
||||
</ng-container>
|
||||
</p>
|
||||
<table *ngIf="result.success">
|
||||
<tr>
|
||||
<th>{{'borrow.request.user' | i18n}}</th>
|
||||
<td>{{result.response.user}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{'borrow.request.item' | i18n}}</th>
|
||||
<td>{{result.response.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{'borrow.request.owner' | i18n}}</th>
|
||||
<td>{{result.response.owner}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{'borrow.request.started' | i18n}}</th>
|
||||
<td>{{result.response.nbf | datef}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{'borrow.request.ends' | i18n}}</th>
|
||||
<td>{{result.response.exp | datef}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-raised-button [mat-dialog-close]="false">{{'ok' | i18n}}</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</mat-dialog-content>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<h2 mat-dialog-title>{{(create ? 'borrow.request.create' : 'borrow.request.edit') | i18n}}</h2>
|
||||
<mat-dialog-content>
|
||||
|
||||
<h3>{{borrowRequest.borrowItem.name}}</h3>
|
||||
<p>{{borrowRequest.borrowItem.description}}</p>
|
||||
|
||||
<a *ngIf="borrowRequest.borrowItem.url" mat-button color="accent" href="{{ borrowRequest.borrowItem.url }}"
|
||||
target="_blank">
|
||||
{{ borrowRequest.borrowItem.url }}
|
||||
<mat-icon style="font-size: 1em;">open_in_new</mat-icon>
|
||||
</a>
|
||||
<br>
|
||||
<br>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<ng-container *ngIf="borrowRequest.id">
|
||||
<mat-chip-list *ngSwitch="borrowRequest.status">
|
||||
<mat-chip *ngSwtichCase="'PENDING'" color="primary" selected></mat-chip>
|
||||
<mat-chip color="accent" selected>Accent fish</mat-chip>
|
||||
</mat-chip-list>
|
||||
|
||||
</ng-container>
|
||||
|
||||
<form [formGroup]="form">
|
||||
<mat-form-field>
|
||||
<input matInput [ngxMatDatetimePicker]="startPicker" formControlName="start"
|
||||
placeholder="{{'borrow.request.start' | i18n}}">
|
||||
<mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #startPicker></ngx-mat-datetime-picker>
|
||||
<mat-error>
|
||||
{{'borrow.request.error.start' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<input matInput [ngxMatDatetimePicker]="endPicker" formControlName="end"
|
||||
placeholder="{{'borrow.request.end' | i18n}}">
|
||||
<mat-datepicker-toggle matSuffix [for]="endPicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #endPicker></ngx-mat-datetime-picker>
|
||||
<mat-error>
|
||||
{{'borrow.request.error.end' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<textarea matInput placeholder="{{'borrow.request.comment' | i18n}}" formControlName="comment"></textarea>
|
||||
<mat-error>
|
||||
{{'borrow.request.error.comment' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<div fxLayout="row" 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">
|
||||
<mat-icon>save</mat-icon>{{(create ? 'borrow.request.create' : 'borrow.request.save') | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<button *ngIf="borrowRequest.id" mat-button color="warn" (click)="confirmDelete()">
|
||||
<mat-icon>delete</mat-icon> {{ 'borrow.request.delete' |
|
||||
i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,7 @@
|
||||
mat-form-field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-dialog-actions>div {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<h3>{{'borrow.requests' | i18n}}</h3>
|
||||
<div *ngIf="borrowRequests">
|
||||
<div fxLayout="row" fxLayoutAlign="space-between start">
|
||||
<form>
|
||||
<mat-form-field floatLabel="always" appearance="none">
|
||||
<mat-slide-toggle [formControl]="ownerFormControl">{{'borrow.requests.mine' | i18n}}</mat-slide-toggle>
|
||||
<input matInput hidden />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</div>
|
||||
<table mat-table matSort [dataSource]="borrowRequests.content" (matSortChange)="updateSort($event)">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="name"> {{'borrow.item.name' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.borrowItem.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="starts">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'borrow.requests.starts' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.starts | datef}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="ends">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'borrow.requests.ends' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowRequest"> {{ borrowRequest.ends | datef}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="status">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'borrow.requests.status' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowRequest"> {{ 'borrow.requests.status.' + borrowRequest.status | i18n}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'borrow.requests.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let borrowRequest" class="text-right">
|
||||
<a mat-icon-button *ngIf="borrowRequest.user == userId">
|
||||
<mat-icon (click)="edit(borrowRequest)">edit</mat-icon>
|
||||
</a>
|
||||
<a mat-icon-button *ngIf="borrowRequest.user == userId">
|
||||
<mat-icon (click)="confirmDelete(borrowRequest)">delete</mat-icon>
|
||||
</a>
|
||||
<a mat-icon-button *ngIf="borrowRequest.item.owner != userId">
|
||||
<mat-icon (click)="updateStatus(borrowRequest)">pending_actions</mat-icon>
|
||||
</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="borrowRequestColumns"></tr>
|
||||
<tr mat-row *matRowDef="let myRowData; columns: borrowRequestColumns"></tr>
|
||||
</table>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="borrowRequests.totalElements"
|
||||
[pageSize]="borrowRequests.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
.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 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.align-right{
|
||||
display: flex;
|
||||
padding: 21px 0;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { Sort } from '@angular/material/sort';
|
||||
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import { ConfirmDialog } from '../../../ui/confirm/confirm.component';
|
||||
import { BorrowRequestsService } from './../../../services/borrow.service';
|
||||
import { I18nService } from './../../../services/i18n.service';
|
||||
import { AuthService } from './../../../services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow-requests',
|
||||
templateUrl: './requests.component.html',
|
||||
styleUrls: [ './requests.component.scss' ]
|
||||
})
|
||||
export class BorrowRequestsComponent implements OnInit {
|
||||
|
||||
borrowRequests: any[];
|
||||
page: any = { page: 0, size: 10, sort: "id", desc: false };
|
||||
pageSizeOptions: number[] = [ 5, 10, 25, 50 ];
|
||||
ownerFormControl = new FormControl();
|
||||
userId: any;
|
||||
|
||||
borrowRequestColumns = [ "name", "status", "starts", "ends", "actions" ];
|
||||
|
||||
constructor(private borrowRequestsService: BorrowRequestsService,
|
||||
private i18n: I18nService,
|
||||
private authService: AuthService,
|
||||
public dialog: MatDialog) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authService.auth.subscribe((auth: any) => {
|
||||
if (auth.principal && auth.principal.userId) {
|
||||
this.userId = auth.principal.userId;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.ownerFormControl.valueChanges.subscribe(value => {
|
||||
this.borrowRequestsService.getRequests(0, this.page.size, this.page.sort, this.page.desc, value).subscribe((data: any) => {
|
||||
this.borrowRequests = data;
|
||||
}, (error) => { })
|
||||
})
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this.borrowRequestsService.getRequests(this.page.page, this.page.size, this.page.sort, this.page.desc, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowRequests = data;
|
||||
})
|
||||
}
|
||||
|
||||
updatePages(event: PageEvent) {
|
||||
this.page.page = event.pageIndex;
|
||||
this.page.size = event.pageSize;
|
||||
this.borrowRequestsService.getRequests(this.page.page, this.page.size, this.page.sort, this.page.desc, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowRequests = data;
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
updateSort(sort: Sort) {
|
||||
if (sort.direction == "") {
|
||||
this.page.sort = "id";
|
||||
this.page.desc = false;
|
||||
} else {
|
||||
this.page.sort = sort.active;
|
||||
this.page.desc = sort.direction == "desc";
|
||||
}
|
||||
this.borrowRequestsService.getRequests(this.page.page, this.page.size, this.page.sort, this.page.desc, this.ownerFormControl.value).subscribe((data: any) => {
|
||||
this.borrowRequests = data;
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
edit(borrowRequest) {
|
||||
const dialogRef = this.dialog.open(BorrowRequestEditComponent, {
|
||||
data: borrowRequest
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmDelete(borrowRequest) {
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
'label': 'borrow.items.confirmDelete',
|
||||
'args': [ borrowRequest.name ]
|
||||
}
|
||||
})
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.borrowRequestsService.deleteRequest(borrowRequest.id).subscribe((result: any) => {
|
||||
this.refresh();
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-borrow-request-edit',
|
||||
templateUrl: './request.edit.html',
|
||||
styleUrls: [ './request.edit.scss' ]
|
||||
})
|
||||
export class BorrowRequestEditComponent {
|
||||
|
||||
borrowRequest: any;
|
||||
create: boolean = false;
|
||||
form: FormGroup;
|
||||
|
||||
constructor(private borrowRequestsService: BorrowRequestsService,
|
||||
private formBuilder: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
public dialogRef: MatDialogRef<BorrowRequestEditComponent>,
|
||||
public dialog: MatDialog) {
|
||||
|
||||
this.form = this.formBuilder.group({
|
||||
})
|
||||
|
||||
this.borrowRequest = data;
|
||||
|
||||
if (!this.borrowRequest.id) {
|
||||
this.create = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
formToBorrowRequest(): any {
|
||||
const borrowRequest: any = {};
|
||||
return borrowRequest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
save() {
|
||||
this.borrowRequestsService.createOrUpdateRequest(this.formToBorrowRequest()).subscribe((data: any) => {
|
||||
this.dialogRef.close(data);
|
||||
}, (error) => {
|
||||
if (error.status == 409) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmDelete() {
|
||||
const borrowRequest = this.formToBorrowRequest();
|
||||
|
||||
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||
data: {
|
||||
'label': 'borrow.requests.confirmDelete',
|
||||
'args': [ borrowRequest.name ]
|
||||
}
|
||||
})
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.borrowRequestsService.deleteRequest(borrowRequest.id).subscribe((result: any) => {
|
||||
this.dialogRef.close(true);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'invites.register' | i18n}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<ng-container *ngIf="invite">
|
||||
<h3>
|
||||
<mat-icon inline=true>{{'invites.quota.' + invite.quota + ".icon" | i18n}}</mat-icon> {{'invites.quota.'
|
||||
+
|
||||
invite.quota
|
||||
| i18n}}
|
||||
</h3>
|
||||
|
||||
<p>{{'invites.quota.' + invite.quota +
|
||||
".text" | i18n}}</p>
|
||||
<p *ngIf="invite.url">
|
||||
<a mat-button href="{{invite.url}}" target="_blank" color="accent">{{'invites.register.url' |
|
||||
i18n}}</a>
|
||||
</p>
|
||||
|
||||
<ng-container *ngIf="!success">
|
||||
<blockquote *ngIf="invite.message && auth.principal.userId != invite.owner" class="message">
|
||||
{{invite.message}}</blockquote>
|
||||
<blockquote *ngIf="invite.note && auth.principal.userId != invite.owner" class="note">{{invite.note}}
|
||||
</blockquote>
|
||||
<form [formGroup]="form" *ngIf="!error">
|
||||
<ng-container *ngIf="!auth.authenticated">
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'username' | i18n}}" formControlName="username" matAutofocus
|
||||
tabindex="1">
|
||||
<mat-error>
|
||||
{{'username.error' | i18n}}
|
||||
</mat-error>
|
||||
<a mat-button matSuffix mat-icon-button (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-error>
|
||||
<div *ngFor="let error of form.get('password').errors | keyvalue">
|
||||
{{'password.error.' + error.key | i18n}}<br>
|
||||
</div>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}"
|
||||
formControlName="password2" tabindex="3">
|
||||
<mat-error>
|
||||
{{'password.not-match' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="auth.principal.userId == invite.owner">
|
||||
<mat-form-field>
|
||||
<textarea matInput placeholder="{{'invite.message' | i18n}}"
|
||||
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-error>
|
||||
{{'invites.error.note' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
</form>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="success">
|
||||
<h2>{{'invites.register.success' | i18n}}</h2>
|
||||
<p>{{'invites.register.success.text' | i18n}}</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="error">
|
||||
<mat-error>
|
||||
{{'invites.register.error.' + error | i18n}}
|
||||
</mat-error>
|
||||
</ng-container>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<ng-container *ngIf="!success && invite">
|
||||
<button *ngIf="!working && !auth.authenticated && !error" mat-raised-button color="primary"
|
||||
[disabled]="form.invalid" (click)="register()" tabindex="4">
|
||||
{{'invites.register' | i18n}}
|
||||
</button>
|
||||
|
||||
<button *ngIf="auth.principal.userId == invite.owner && !error" mat-raised-button color="primary"
|
||||
(click)="save()" tabindex="4">
|
||||
{{'invites.edit.save' | i18n}}
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="success">
|
||||
<a routerLink="/login" mat-raised-button color="primary">
|
||||
{{'invites.register.login' | i18n}}
|
||||
</a>
|
||||
</ng-container>
|
||||
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar>
|
||||
</mat-card-actions>
|
||||
<mat-card-footer>
|
||||
<a href="https://wiki.bstly.de/services/webstly#invite" class="help-button"
|
||||
matTooltip="{{'help-button' | i18n}}" matTooltipPosition="above" target="_blank" mat-fab color="accent">
|
||||
<mat-icon>contact_support</mat-icon>
|
||||
</a>
|
||||
</mat-card-footer>
|
||||
</mat-card>
|
||||
|
||||
<div *ngIf="!success && permissions && permissions[0] && !error">
|
||||
<h3>{{'permissions' | i18n}}</h3>
|
||||
<app-permissions [permissions]="permissions"></app-permissions>
|
||||
</div>
|
||||
<div *ngIf="!success && quotas && quotas[0] && !error">
|
||||
<h3>{{'quotas' | i18n}}</h3>
|
||||
<app-quotas [quotas]="quotas"></app-quotas>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
@import '../../../../variables.scss';
|
||||
|
||||
|
||||
mat-form-field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-hint {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-left: 5px;
|
||||
padding: 5px;
|
||||
|
||||
&.message {
|
||||
border-left: 2px solid $accent;
|
||||
}
|
||||
|
||||
&.note {
|
||||
border-left: 2px solid $primary;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { MatchingValidator } from './../../../utils/matching.validator';
|
||||
import { InviteService } from './../../../services/invites.service';
|
||||
import { uniqueNamesGenerator, Config, adjectives, colors, animals } from 'unique-names-generator';
|
||||
import { I18nService } from 'src/app/services/i18n.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { InviteEditComponent } from '../edit/invite.edit';
|
||||
import { AuthService } from 'src/app/services/auth.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-invite-code',
|
||||
templateUrl: './code.component.html',
|
||||
styleUrls: [ './code.component.scss' ]
|
||||
})
|
||||
export class InviteCodeComponent implements OnInit {
|
||||
|
||||
form: FormGroup;
|
||||
error: string;
|
||||
working: boolean = true;
|
||||
success: boolean = false;
|
||||
invite: any;
|
||||
datetimeformat: string;
|
||||
auth: any;
|
||||
permissions = [];
|
||||
quotas = [];
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private inviteService: InviteService,
|
||||
private formBuilder: FormBuilder,
|
||||
private i18n: I18nService,
|
||||
public dialog: MatDialog,
|
||||
private route: ActivatedRoute) {
|
||||
this.authService.auth.subscribe(data => {
|
||||
this.auth = data;
|
||||
})
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
||||
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||
|
||||
this.form = this.formBuilder.group({
|
||||
username: [ '', Validators.required ],
|
||||
password: [ '', Validators.nullValidator ],
|
||||
password2: [ '', Validators.required ]
|
||||
}, {
|
||||
validators: MatchingValidator('password', 'password2')
|
||||
});
|
||||
|
||||
const code = this.route.snapshot.paramMap.get('code');
|
||||
this.inviteService.code(code).subscribe((data) => {
|
||||
this.invite = data;
|
||||
|
||||
if (this.invite.redeemed) {
|
||||
this.error = "ALREADY_REDEEMED";
|
||||
}
|
||||
|
||||
if (this.invite.owner == this.auth.principal.userId) {
|
||||
this.form = this.formBuilder.group({
|
||||
message: [ '', Validators.nullValidator ],
|
||||
note: [ '', Validators.nullValidator ],
|
||||
});
|
||||
|
||||
this.form.get("message").setValue(this.invite.message);
|
||||
this.form.get("note").setValue(this.invite.note);
|
||||
}
|
||||
|
||||
this.working = false;
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 406) {
|
||||
this.error = "INVALID_CODE";
|
||||
}
|
||||
})
|
||||
|
||||
this.inviteService.permissions(code).subscribe((data: any) => {
|
||||
this.permissions = data;
|
||||
})
|
||||
|
||||
this.inviteService.quotas(code).subscribe((data: any) => {
|
||||
this.quotas = data;
|
||||
})
|
||||
}
|
||||
|
||||
genUsername() {
|
||||
const config: Config = {
|
||||
dictionaries: [ adjectives, colors, animals ],
|
||||
separator: "",
|
||||
style: "capital",
|
||||
length: 3
|
||||
};
|
||||
|
||||
this.form.get("username").setValue(uniqueNamesGenerator(config));
|
||||
}
|
||||
|
||||
register() {
|
||||
if (this.form.valid && !this.working) {
|
||||
this.working = true;
|
||||
|
||||
const model: any = {};
|
||||
model.token = this.invite.code;
|
||||
model.username = this.form.get("username").value;
|
||||
model.password = this.form.get("password").value;
|
||||
model.password2 = this.form.get("password2").value;
|
||||
|
||||
this.inviteService.register(model).subscribe((result: any) => {
|
||||
this.working = false;
|
||||
this.success = true;
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 401) {
|
||||
this.error = "NO_CODE";
|
||||
} else if (error.status == 406) {
|
||||
this.error = "INVALID_CODE";
|
||||
} else if (error.status == 410) {
|
||||
this.error = "ALREADY_REDEEMED";
|
||||
} else if (error.status == 409) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.form.valid && !this.working) {
|
||||
this.working = true;
|
||||
this.invite.message = this.form.get("message").value;
|
||||
this.invite.note = this.form.get("note").value;
|
||||
this.inviteService.update(this.invite).subscribe((result: any) => {
|
||||
this.invite = result;
|
||||
this.working = false;
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 406) {
|
||||
this.error = "INVALID_CODE";
|
||||
} if (error.status == 410) {
|
||||
this.error = "ALREADY_REDEEMED";
|
||||
} else if (error.status == 409) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<h2 mat-dialog-title>{{'invites.edit' | i18n}}</h2>
|
||||
<mat-dialog-content>
|
||||
<form [formGroup]="form">
|
||||
<mat-error *ngIf="error">
|
||||
{{'invites.register.error.' + error | i18n}}
|
||||
</mat-error>
|
||||
<mat-form-field>
|
||||
<textarea matInput placeholder="{{'invite.message' | i18n}}" 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-error>
|
||||
{{'invites.error.note' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</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()">
|
||||
{{'invites.edit.save' | i18n}}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,7 @@
|
||||
mat-form-field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-hint {
|
||||
white-space: normal;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { MatchingValidator } from '../../../utils/matching.validator';
|
||||
import { InviteService } from '../../../services/invites.service';
|
||||
import { uniqueNamesGenerator, Config, adjectives, colors, animals } from 'unique-names-generator';
|
||||
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-invite-edit',
|
||||
templateUrl: './invite.edit.html',
|
||||
styleUrls: [ './invite.edit.scss' ]
|
||||
})
|
||||
export class InviteEditComponent {
|
||||
|
||||
form: FormGroup;
|
||||
error: string;
|
||||
working: boolean = false;
|
||||
success: boolean = false;
|
||||
invite: any;
|
||||
|
||||
constructor(
|
||||
private inviteService: InviteService,
|
||||
private formBuilder: FormBuilder,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
public dialogRef: MatDialogRef<InviteEditComponent>,
|
||||
public dialog: MatDialog) {
|
||||
this.form = this.formBuilder.group({
|
||||
message: [ '', Validators.nullValidator ],
|
||||
note: [ '', Validators.nullValidator ],
|
||||
});
|
||||
|
||||
this.invite = data;
|
||||
this.form.get("message").setValue(this.invite.message);
|
||||
this.form.get("note").setValue(this.invite.note);
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.form.valid && !this.working) {
|
||||
this.working = true;
|
||||
|
||||
this.invite.message = this.form.get("message").value;
|
||||
this.invite.note = this.form.get("note").value;
|
||||
|
||||
this.inviteService.update(this.invite).subscribe((result: any) => {
|
||||
this.working = false;
|
||||
this.dialogRef.close(result);
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if (error.status == 406) {
|
||||
this.error = "INVALID_CODE";
|
||||
} if (error.status == 410) {
|
||||
this.error = "ALREADY_REDEEMED";
|
||||
} else if (error.status == 409) {
|
||||
let errors = {};
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
|
||||
for (let code in errors) {
|
||||
this.form.get(code).setErrors(errors[ code ]);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,17 +37,17 @@
|
||||
<td mat-cell *matCellDef="let invite"> {{ invite.expires | date:datetimeformat}} </td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<ng-container matColumnDef="link">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'invite.link' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let invite"> <a href="{{ invite.codeLink}}" target="_blank" mat-button color="accent">
|
||||
{{
|
||||
invite.code}}</a> </td>
|
||||
<td mat-cell *matCellDef="let invite">
|
||||
<a href="{{ invite.codeLink }}" target="_blank" mat-button color="accent">{{invite.code}}</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="note">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let invite"> <span *ngIf="invite.note">{{ invite.note}}</span> <i *ngIf="!invite.note">{{ 'invite.noNote' | i18n}}</i>
|
||||
<td mat-cell *matCellDef="let invite"> <span *ngIf="invite.note">{{ invite.note}}</span> <i
|
||||
*ngIf="!invite.note">{{ 'invite.noNote' | i18n}}</i>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
@@ -65,10 +65,20 @@
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'invite.actions' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let invite">
|
||||
<a mat-icon-button>
|
||||
<mat-icon (click)="edit(invite)">edit</mat-icon>
|
||||
</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="inviteColumns"></tr>
|
||||
<tr mat-row *matRowDef="let myRowData; columns: inviteColumns"></tr>
|
||||
</table>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="invites.totalElements" [pageSize]="invites.size" (page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="invites.totalElements" [pageSize]="invites.size"
|
||||
(page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||
</div>
|
||||
|
||||
<mat-card>
|
||||
@@ -117,7 +127,8 @@
|
||||
<table mat-table matSort [dataSource]="others.content">
|
||||
<ng-container matColumnDef="note">
|
||||
<th mat-header-cell *matHeaderCellDef> {{'invite.note' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let invite"> <span *ngIf="invite.note">{{ invite.note}}</span> <i *ngIf="!invite.note">{{ 'invite.noNote' | i18n}}</i> </td>
|
||||
<td mat-cell *matCellDef="let invite"> <span *ngIf="invite.note">{{ invite.note}}</span> <i
|
||||
*ngIf="!invite.note">{{ 'invite.noNote' | i18n}}</i> </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="redeemed">
|
||||
@@ -130,5 +141,6 @@
|
||||
<tr mat-header-row *matHeaderRowDef="otherColumns"></tr>
|
||||
<tr mat-row *matRowDef="let myRowData; columns: otherColumns"></tr>
|
||||
</table>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="others.totalElements" [pageSize]="others.size" (page)="updateOthers($event)" showFirstLastButtons></mat-paginator>
|
||||
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="others.totalElements" [pageSize]="others.size"
|
||||
(page)="updateOthers($event)" showFirstLastButtons></mat-paginator>
|
||||
</div>
|
||||
@@ -1,19 +1,21 @@
|
||||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {PageEvent} from '@angular/material/paginator';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
|
||||
import {AuthService} from '../../services/auth.service';
|
||||
import {I18nService} from '../../services/i18n.service';
|
||||
import {QuotaService} from '../../services/quota.service';
|
||||
import {InviteService} from '../../services/invites.service';
|
||||
import {FormControl} from '@angular/forms';
|
||||
import {debounceTime} from 'rxjs/operators';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { I18nService } from '../../services/i18n.service';
|
||||
import { QuotaService } from '../../services/quota.service';
|
||||
import { InviteService } from '../../services/invites.service';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { InviteEditComponent } from './edit/invite.edit';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-invites',
|
||||
templateUrl: './invites.component.html',
|
||||
styleUrls: ['./invites.component.scss']
|
||||
styleUrls: [ './invites.component.scss' ]
|
||||
})
|
||||
export class InvitesComponent implements OnInit {
|
||||
|
||||
@@ -24,21 +26,20 @@ export class InvitesComponent implements OnInit {
|
||||
success: boolean;
|
||||
working: boolean;
|
||||
datetimeformat: string;
|
||||
pageSizeOptions: number[] = [5, 10, 25, 50];
|
||||
pageSizeOptions: number[] = [ 5, 10, 25, 50 ];
|
||||
searchFormControl = new FormControl();
|
||||
redeemedFormControl = new FormControl();
|
||||
searchOthersFormControl = new FormControl();
|
||||
redeemedOthersFormControl = new FormControl();
|
||||
|
||||
inviteColumns = ["starts", "expires", "link", "note", "message", "redeemed"];
|
||||
otherColumns = ["note", "redeemed"];
|
||||
inviteColumns = [ "starts", "expires", "link", "note", "message", "redeemed", "actions" ];
|
||||
otherColumns = [ "note", "redeemed" ];
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private inviteService: InviteService,
|
||||
private i18n: I18nService,
|
||||
private quotaService: QuotaService,
|
||||
private router: Router,
|
||||
public dialog: MatDialog,
|
||||
private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
@@ -49,23 +50,23 @@ export class InvitesComponent implements OnInit {
|
||||
this.searchFormControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
|
||||
this.inviteService.getPages(this.quota, 0, this.invites.size, value, this.redeemedFormControl.value).subscribe((data: any) => {
|
||||
this.invites = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
})
|
||||
this.redeemedFormControl.valueChanges.subscribe(value => {
|
||||
this.inviteService.getPages(this.quota, 0, this.invites.size, this.searchFormControl.value ? this.searchFormControl.value : "", value).subscribe((data: any) => {
|
||||
this.invites = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
})
|
||||
|
||||
this.searchOthersFormControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
|
||||
this.inviteService.getOthersPages(this.quota, 0, this.others.size, value, this.redeemedOthersFormControl.value).subscribe((data: any) => {
|
||||
this.others = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
})
|
||||
this.redeemedOthersFormControl.valueChanges.subscribe(value => {
|
||||
this.inviteService.getOthersPages(this.quota, 0, this.others.size, this.searchOthersFormControl.value ? this.searchOthersFormControl.value : "", value).subscribe((data: any) => {
|
||||
this.others = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
})
|
||||
|
||||
this.update();
|
||||
@@ -75,56 +76,67 @@ export class InvitesComponent implements OnInit {
|
||||
update(): void {
|
||||
this.inviteQuota = 0;
|
||||
this.quotaService.quotas().subscribe((data: any) => {
|
||||
for(let quota of data) {
|
||||
if(quota.name == "invite_" + this.quota) {
|
||||
for (let quota of data) {
|
||||
if (quota.name == "invite_" + this.quota) {
|
||||
this.inviteQuota = quota.value;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if(!this.invites) {
|
||||
if (!this.invites) {
|
||||
this.inviteService.get(this.quota).subscribe((data: any) => {
|
||||
this.invites = data;
|
||||
})
|
||||
} else {
|
||||
this.inviteService.getPages(this.quota, this.invites.number || 0, this.invites.size || 10, this.searchFormControl.value ? this.searchFormControl.value : "", this.redeemedFormControl.value).subscribe((data: any) => {
|
||||
this.invites = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
this.inviteService.getOthers(this.quota).subscribe((data: any) => {
|
||||
this.others = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
updatePages(event: PageEvent) {
|
||||
this.inviteService.getPages(this.quota, event.pageIndex, event.pageSize, this.searchFormControl.value ? this.searchFormControl.value : "", this.redeemedFormControl.value).subscribe((data: any) => {
|
||||
this.invites = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
}
|
||||
|
||||
create(): void {
|
||||
this.working = true;
|
||||
|
||||
|
||||
this.inviteService.create(this.quota, {}).subscribe(response => {
|
||||
this.update();
|
||||
this.working = false;
|
||||
}, (error) => {
|
||||
this.working = false;
|
||||
if(error.status == 409) {
|
||||
if (error.status == 409) {
|
||||
let errors = {};
|
||||
for(let code of error.error) {
|
||||
errors[code.field] = errors[code.field] || {};
|
||||
errors[code.field][code.code] = true;
|
||||
for (let code of error.error) {
|
||||
errors[ code.field ] = errors[ code.field ] || {};
|
||||
errors[ code.field ][ code.code ] = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
edit(invite) {
|
||||
const dialogRef = this.dialog.open(InviteEditComponent, {
|
||||
data: invite,
|
||||
minWidth: "400px"
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateOthers(event: PageEvent) {
|
||||
this.inviteService.getOthersPages(this.quota, event.pageIndex, event.pageSize, this.searchOthersFormControl.value ? this.searchOthersFormControl.value : "", this.redeemedOthersFormControl.value).subscribe((data: any) => {
|
||||
this.others = data;
|
||||
}, (error) => {})
|
||||
}, (error) => { })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
<p *ngIf="!tags || tags.length == 0">{{'partey.tags.none' | i18n}}</p>
|
||||
|
||||
<mat-chip-list>
|
||||
<mat-chip *ngFor="let tag of tags">{{tag.tag}}</mat-chip>
|
||||
<ng-container *ngFor="let tag of tags">
|
||||
<mat-chip *ngIf="activeTag(tag)" color="accent" selected>{{tag.tag}}</mat-chip>
|
||||
<mat-chip *ngIf="upcomingTag(tag)" disabled matTooltip="{{'partey.tags.upcoming' | i18n:(tag.starts | datef)}}" selected>{{tag.tag}}</mat-chip>
|
||||
<mat-chip *ngIf="expiringTag(tag)" color="primary" matTooltip="{{'partey.tags.expires' | i18n:(tag.expires | datef)}}" selected>{{tag.tag}}</mat-chip>
|
||||
</ng-container>
|
||||
</mat-chip-list>
|
||||
|
||||
|
||||
|
||||
@@ -21,4 +21,20 @@ export class ParteyComponent implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
toDate(value : string) {
|
||||
return new Date(value);
|
||||
}
|
||||
|
||||
activeTag(tag: any) {
|
||||
return !tag.starts && !tag.expires;
|
||||
}
|
||||
|
||||
upcomingTag(tag: any) {
|
||||
return tag.starts && (new Date() < this.toDate(tag.starts));
|
||||
}
|
||||
|
||||
expiringTag(tag: any) {
|
||||
return !(this.upcomingTag(tag)) && tag.expires;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,17 +9,17 @@
|
||||
</mat-error>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="{{'username' | i18n}}" formControlName="username"
|
||||
[(ngModel)]="model.username" required matAutofocus>
|
||||
[(ngModel)]="model.username" required matAutofocus tabindex="1">
|
||||
<mat-error>
|
||||
{{'username.error' | i18n}}
|
||||
</mat-error>
|
||||
<a mat-button matSuffix mat-icon-button (click)="genUsername()">
|
||||
<a mat-button matSuffix mat-icon-button (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"
|
||||
[(ngModel)]="model.password" required>
|
||||
[(ngModel)]="model.password" required tabindex="2">
|
||||
<mat-error>
|
||||
<div *ngFor="let error of form.get('password').errors | keyvalue">
|
||||
{{'password.error.' + error.key | i18n}}<br>
|
||||
@@ -28,14 +28,14 @@
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput type="password" placeholder="{{'password.confirm' | i18n}}" formControlName="password2"
|
||||
[(ngModel)]="model.password2" required>
|
||||
[(ngModel)]="model.password2" required tabindex="3">
|
||||
<mat-error>
|
||||
{{'password.not-match' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-slide-toggle formControlName="primaryEmail" [(ngModel)]="model.primaryEmail"
|
||||
(change)="onPrimaryChange()">
|
||||
(change)="onPrimaryChange()" tabindex="4">
|
||||
{{'email.primary' | i18n}}
|
||||
</mat-slide-toggle>
|
||||
<mat-icon #primaryHint="matTooltip" (click)="primaryHint.toggle()" inline="true"
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<mat-form-field *ngIf="model.primaryEmail">
|
||||
<input matInput type="email" placeholder="{{'email' | i18n}}" formControlName="email"
|
||||
[(ngModel)]="model.email" required>
|
||||
[(ngModel)]="model.email" required tabindex="5">
|
||||
<mat-error>
|
||||
{{'email.invalid' | i18n}}
|
||||
</mat-error>
|
||||
@@ -59,14 +59,16 @@
|
||||
<mat-divider></mat-divider>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||
<button *ngIf="!working" mat-raised-button color="primary" [disabled]="form.invalid" tabindex="6">
|
||||
{{'register' | i18n}}
|
||||
</button>
|
||||
|
||||
<mat-progress-bar *ngIf="working" mode="indeterminate"></mat-progress-bar>
|
||||
</mat-card-actions>
|
||||
<mat-card-footer>
|
||||
<a href="https://wiki.bstly.de/services/webstly#registration" class="help-button" matTooltip="{{'help-button' | i18n}}" matTooltipPosition="above" target="_blank" mat-fab color="accent">
|
||||
<a href="https://wiki.bstly.de/services/webstly#registration" class="help-button"
|
||||
matTooltip="{{'help-button' | i18n}}" matTooltipPosition="above" target="_blank" mat-fab color="accent"
|
||||
tabindex="7">
|
||||
<mat-icon>contact_support</mat-icon>
|
||||
</a>
|
||||
</mat-card-footer>
|
||||
|
||||
Reference in New Issue
Block a user