update partey timeslots + minetest
This commit is contained in:
parent
72178dd7c4
commit
a11c74b91b
@ -19,10 +19,12 @@ import {UnavailableComponent} from './pages/unavailable/unavailable.component';
|
|||||||
import {NotfoundComponent} from './pages/notfound/notfound.component';
|
import {NotfoundComponent} from './pages/notfound/notfound.component';
|
||||||
import {UserComponent} from './pages/user/user.component'
|
import {UserComponent} from './pages/user/user.component'
|
||||||
import {JitsiComponent} from './pages/jitsi/jitsi.component'
|
import {JitsiComponent} from './pages/jitsi/jitsi.component'
|
||||||
|
import {ParteyTimeslotsComponent} from './pages/partey/timeslots/timeslots.compontent'
|
||||||
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
||||||
import {DomainsComponent} from './pages/account/domains/domains.component';
|
import {DomainsComponent} from './pages/account/domains/domains.component';
|
||||||
import {InvitesComponent} from './pages/invites/invites.component';
|
import {InvitesComponent} from './pages/invites/invites.component';
|
||||||
import {UrlShortenerComponent, UrlShortenerPasswordComponent} from './pages/urlshortener/urlshortener.component';
|
import {UrlShortenerComponent, UrlShortenerPasswordComponent} from './pages/urlshortener/urlshortener.component';
|
||||||
|
import {MinetestAccountsComponent} from './pages/minetest/accounts/accounts.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', redirectTo: "/services", pathMatch: 'full'},
|
{path: '', redirectTo: "/services", pathMatch: 'full'},
|
||||||
@ -50,6 +52,8 @@ const routes: Routes = [
|
|||||||
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
||||||
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
|
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
|
||||||
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
|
||||||
|
{path: 'partey/timeslots', component: ParteyTimeslotsComponent, canActivate: [AuthenticatedGuard]},
|
||||||
|
{path: 'minetest/accounts', component: MinetestAccountsComponent, canActivate: [AuthenticatedGuard]},
|
||||||
{path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard]},
|
||||||
{path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard]},
|
{path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard]},
|
||||||
{path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]},
|
{path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]},
|
||||||
|
@ -7,10 +7,10 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
|||||||
import {HttpClientModule, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS} from '@angular/common/http';
|
import {HttpClientModule, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS} from '@angular/common/http';
|
||||||
import {MaterialModule} from './material/material.module';
|
import {MaterialModule} from './material/material.module';
|
||||||
import {QRCodeModule} from 'angularx-qrcode';
|
import {QRCodeModule} from 'angularx-qrcode';
|
||||||
import { DatePipe } from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
import { AutofocusDirective} from './material/autofocus';
|
import {AutofocusDirective} from './material/autofocus';
|
||||||
|
|
||||||
import {I18nPipe} from './utils/i18n.pipe';
|
import {I18nPipe} from './utils/i18n.pipe';
|
||||||
import {ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent} from './pages/general/general.component';
|
import {ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent} from './pages/general/general.component';
|
||||||
@ -43,10 +43,12 @@ import {HtmlComponent} from './utils/html/html.component';
|
|||||||
import {ConfirmDialog} from './ui/confirm/confirm.component'
|
import {ConfirmDialog} from './ui/confirm/confirm.component'
|
||||||
import {UserComponent} from './pages/user/user.component'
|
import {UserComponent} from './pages/user/user.component'
|
||||||
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
|
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
|
||||||
|
import {ParteyTimeslotsComponent, ParteyTimeslotDialog} from './pages/partey/timeslots/timeslots.compontent'
|
||||||
import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog} from './pages/urlshortener/urlshortener.component'
|
import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog} from './pages/urlshortener/urlshortener.component'
|
||||||
|
|
||||||
|
|
||||||
import {I18nService} from './services/i18n.service';
|
import {I18nService} from './services/i18n.service';
|
||||||
|
import {MinetestAccountsComponent} from './pages/minetest/accounts/accounts.component';
|
||||||
|
|
||||||
|
|
||||||
export function init_app(i18n: I18nService) {
|
export function init_app(i18n: I18nService) {
|
||||||
@ -99,6 +101,8 @@ export class XhrInterceptor implements HttpInterceptor {
|
|||||||
ConfirmDialog,
|
ConfirmDialog,
|
||||||
UserComponent,
|
UserComponent,
|
||||||
JitsiComponent, JitsiShareDialog,
|
JitsiComponent, JitsiShareDialog,
|
||||||
|
ParteyTimeslotsComponent, ParteyTimeslotDialog,
|
||||||
|
MinetestAccountsComponent,
|
||||||
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerPasswordComponent
|
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerPasswordComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
|
43
src/app/pages/minetest/accounts/accounts.component.html
Normal file
43
src/app/pages/minetest/accounts/accounts.component.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<h3>{{'minetest.accounts' | i18n}}</h3>
|
||||||
|
|
||||||
|
|
||||||
|
<table mat-table [dataSource]="minetestAccounts">
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'minetest.accounts.name' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let minetestAccount">
|
||||||
|
{{minetestAccount.name}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="minetestAccountsColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let myRowData; columns: minetestAccountsColumns"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{'minetest.accounts.info' | i18n}}</p>
|
||||||
|
<p *ngIf="!minetestAccountsQuota">{{'minetest.accounts.noQuota' | i18n}}</p>
|
||||||
|
<div *ngIf="minetestAccountsQuota">
|
||||||
|
<p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput placeholder="{{'minetest.accounts.name' | i18n}}" formControlName="name"
|
||||||
|
[(ngModel)]="minetestAccount.name" required pattern="[-_a-zA-Z0-9]+">
|
||||||
|
<mat-error>
|
||||||
|
{{'minetest.accounts.error.name' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-card class="warn">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-subtitle>{{'minetest.accounts.deletion' | i18n}}</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button *ngIf="minetestAccountsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid">
|
||||||
|
{{'minetest.accounts.create' | i18n}}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
</form>
|
30
src/app/pages/minetest/accounts/accounts.component.scss
Normal file
30
src/app/pages/minetest/accounts/accounts.component.scss
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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;
|
||||||
|
max-width: 200px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
77
src/app/pages/minetest/accounts/accounts.component.ts
Normal file
77
src/app/pages/minetest/accounts/accounts.component.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||||
|
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
|
||||||
|
|
||||||
|
import {QuotaService} from '../../../services/quota.service';
|
||||||
|
import {MinetestAccountsService} from '../../../services/minetest.accounts.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-minetest-accounts',
|
||||||
|
templateUrl: './accounts.component.html',
|
||||||
|
styleUrls: ['./accounts.component.scss']
|
||||||
|
})
|
||||||
|
export class MinetestAccountsComponent implements OnInit {
|
||||||
|
|
||||||
|
form: FormGroup;
|
||||||
|
@ViewChild('formDirective') private formDirective: NgForm;
|
||||||
|
minetestAccountsQuota: number = 0;
|
||||||
|
minetestAccounts: any;
|
||||||
|
minetestAccount: any = {};
|
||||||
|
success: boolean;
|
||||||
|
working: boolean;
|
||||||
|
|
||||||
|
minetestAccountsColumns = ["name"];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private quotaService: QuotaService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private minetestAccountsService: MinetestAccountsService) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
name: ['', Validators.required],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
create(): void {
|
||||||
|
this.working = true;
|
||||||
|
|
||||||
|
this.minetestAccountsService.create(this.minetestAccount).subscribe(response => {
|
||||||
|
this.update();
|
||||||
|
this.formDirective.resetForm();
|
||||||
|
this.minetestAccount = {};
|
||||||
|
this.working = false;
|
||||||
|
}, (error) => {
|
||||||
|
this.working = false;
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.minetestAccountsQuota = 0;
|
||||||
|
this.quotaService.quotas().subscribe((data: any) => {
|
||||||
|
for(let quota of data) {
|
||||||
|
if(quota.name == "minetest_accounts") {
|
||||||
|
this.minetestAccountsQuota = quota.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.minetestAccountsService.get().subscribe((data: any) => {
|
||||||
|
this.minetestAccounts = data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/app/pages/partey/timeslots/timeslot.dialog.html
Normal file
48
src/app/pages/partey/timeslots/timeslot.dialog.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<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.' +
|
||||||
|
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'">
|
||||||
|
<input matInput [(ngModel)]="timeslot.stream" formControlName="stream"
|
||||||
|
placeholder="{{'partey.timeslots.stream' | i18n}}">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field *ngIf="timeslot.type == 'AUDIO' && timeslot.secret">
|
||||||
|
<textarea [mat-autosize] matInput [value]="timeslot.secret" readonly placeholder="{{'partey.timeslots.secret' | i18n}}"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="timeslot.starts" formControlName="starts"
|
||||||
|
placeholder="{{'partey.timeslots.starts' | i18n}}">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
|
||||||
|
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
|
||||||
|
<mat-error>
|
||||||
|
{{'partey.timeslots.error.starts' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [ngxMatDatetimePicker]="endsPicker" [(ngModel)]="timeslot.ends" formControlName="ends"
|
||||||
|
placeholder="{{'partey.timeslots.ends' | i18n}}">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="endsPicker"></mat-datepicker-toggle>
|
||||||
|
<ngx-mat-datetime-picker #endsPicker></ngx-mat-datetime-picker>
|
||||||
|
<mat-error>
|
||||||
|
{{'partey.timeslots.error.ends' | i18n}}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [(ngModel)]="timeslot.title" formControlName="title"
|
||||||
|
placeholder="{{'partey.timeslots.title' | i18n}}">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<textarea matInput [(ngModel)]="timeslot.description" formControlName="description"
|
||||||
|
placeholder="{{'partey.timeslots.description' | i18n}}"></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)"
|
||||||
|
color="accent">{{ (timeslot.id ? 'partey.timeslots.save' : 'partey.timeslots.create') |
|
||||||
|
i18n}}</button>
|
||||||
|
</mat-dialog-actions>
|
7
src/app/pages/partey/timeslots/timeslot.dialog.scss
Normal file
7
src/app/pages/partey/timeslots/timeslot.dialog.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mat-form-field {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-slide-toggle {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
117
src/app/pages/partey/timeslots/timeslots.component.html
Normal file
117
src/app/pages/partey/timeslots/timeslots.component.html
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<h3>{{'partey.timeslots' | i18n}}</h3>
|
||||||
|
|
||||||
|
<div *ngIf="timeslots">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [formControl]="searchFormControl" placeholder="{{'partey.timeslots.filter.search' | i18n}}">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>{{'partey.timeslots.filter.owner' | i18n}}</mat-label>
|
||||||
|
<mat-select [formControl]="ownerFormControl">
|
||||||
|
<mat-option value="">{{'partey.timeslots.filter.owner.mine' | i18n}}</mat-option>
|
||||||
|
<mat-option value="others">{{'partey.timeslots.filter.owner.others' | i18n}} </mat-option>
|
||||||
|
<mat-option value="all">{{'partey.timeslots.filter.owner.all' | i18n}} </mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>{{'partey.timeslots.filter.type' | i18n}}</mat-label>
|
||||||
|
<mat-select [formControl]="typeFormControl">
|
||||||
|
<mat-option value="">{{'partey.timeslots.filter.type.all' | i18n}}</mat-option>
|
||||||
|
<mat-option *ngFor="let type of types" [value]="type">
|
||||||
|
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
|
||||||
|
{{'partey.timeslots.filter.type.' + type | i18n}}
|
||||||
|
</mat-option>
|
||||||
|
<mat-select-trigger>
|
||||||
|
<mat-icon inline="true" *ngIf="typeFormControl.value ">{{'partey.timeslots.type.' + typeFormControl.value +
|
||||||
|
'.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ? typeFormControl.value
|
||||||
|
: 'all') | i18n}}
|
||||||
|
</mat-select-trigger>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [ngxMatDatetimePicker]="afterPicker" [formControl]="afterFormControl"
|
||||||
|
placeholder="{{'partey.timeslots.filter.after' | i18n}}">
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="afterPicker"></mat-datepicker-toggle>
|
||||||
|
<ngx-mat-datetime-picker #afterPicker></ngx-mat-datetime-picker>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<table mat-table matSort [dataSource]="timeslots.content" (matSortChange)="updateSort($event)">
|
||||||
|
<ng-container matColumnDef="starts">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot">{{ timeslot.starts | date:datetimeformat}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="ends">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="ends"> {{'partey.timeslots.ends' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.ends | date:datetimeformat}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="title">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="title"> {{'partey.timeslots.title' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.title }} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="description">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="description"> {{'partey.timeslots.description' | i18n}}
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.description }} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="type">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="type"> {{'partey.timeslots.type' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot">
|
||||||
|
<mat-icon inline="true" matTooltip="{{'partey.timeslots.type.' + timeslot.type | i18n}}">
|
||||||
|
{{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="stream">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="stream"> {{'partey.timeslots.stream' | i18n}}
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot">
|
||||||
|
<button *ngIf="timeslot.type == 'AUDIO'" mat-raised-button
|
||||||
|
(click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</button>
|
||||||
|
<span *ngIf="timeslot.type == 'VIDEO'"> {{ timeslot.stream }} </span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="edit">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot" class="text-right">
|
||||||
|
<a mat-icon-button>
|
||||||
|
<mat-icon (click)="openEdit(timeslot)">edit</mat-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th>
|
||||||
|
<td mat-cell *matCellDef="let timeslot" class="text-right">
|
||||||
|
<a mat-icon-button>
|
||||||
|
<mat-icon (click)="confirmDelete(timeslot)">delete</mat-icon>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="timeslotsColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let myRowData; columns: timeslotsColumns"></tr>
|
||||||
|
</table>
|
||||||
|
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="timeslots.totalElements" [pageSize]="timeslots.size"
|
||||||
|
(page)="updatePages($event)" showFirstLastButtons></mat-paginator>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{'partey.timeslots.info' | i18n}}</p>
|
||||||
|
<p *ngIf="!timeslotsQuota">{{'partey.timeslots.noQuota' | i18n}}</p>
|
||||||
|
<div *ngIf="timeslotsQuota">
|
||||||
|
<p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions *ngIf="timeslotsQuota">
|
||||||
|
<button mat-button *ngFor="let type of types" (click)="openCreate(type)">
|
||||||
|
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
|
||||||
|
{{'partey.timeslots.create.' + type | i18n}}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
21
src/app/pages/partey/timeslots/timeslots.component.scss
Normal file
21
src/app/pages/partey/timeslots/timeslots.component.scss
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field+.mat-form-field {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
263
src/app/pages/partey/timeslots/timeslots.compontent.ts
Normal file
263
src/app/pages/partey/timeslots/timeslots.compontent.ts
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
import {Component, OnInit, ViewChild, Inject} from '@angular/core';
|
||||||
|
import {Sort} from '@angular/material/sort';
|
||||||
|
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||||
|
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
|
||||||
|
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
|
||||||
|
import {DatePipe} from '@angular/common';
|
||||||
|
import {FormControl} from '@angular/forms';
|
||||||
|
import {debounceTime} from 'rxjs/operators';
|
||||||
|
import {PageEvent} from '@angular/material/paginator';
|
||||||
|
|
||||||
|
import {ParteyTimeslotsService} from '../../../services/partey.timeslots.service';
|
||||||
|
import {ConfirmDialog} from '../../../ui/confirm/confirm.component';
|
||||||
|
import {I18nService} from './../../../services/i18n.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-partey-timeslots',
|
||||||
|
templateUrl: './timeslots.component.html',
|
||||||
|
styleUrls: ['./timeslots.component.scss']
|
||||||
|
})
|
||||||
|
export class ParteyTimeslotsComponent implements OnInit {
|
||||||
|
|
||||||
|
form: FormGroup;
|
||||||
|
@ViewChild('formDirective') private formDirective: NgForm;
|
||||||
|
timeslotsQuota: number = 0;
|
||||||
|
timeslots: any;
|
||||||
|
timeslot: any = {};
|
||||||
|
success: boolean;
|
||||||
|
working: boolean;
|
||||||
|
datetimeformat: string;
|
||||||
|
page: any = {page: 0, size: 10, sort: "id", desc: false};
|
||||||
|
filter: any = {};
|
||||||
|
pageSizeOptions: number[] = [5, 10, 25, 50];
|
||||||
|
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
||||||
|
types = ["VIDEO", "AUDIO"];
|
||||||
|
|
||||||
|
timeslotsColumns = ["type", "starts", "ends", "title", "description", "stream", "edit", "delete"];
|
||||||
|
|
||||||
|
searchFormControl = new FormControl();
|
||||||
|
ownerFormControl = new FormControl();
|
||||||
|
afterFormControl = new FormControl();
|
||||||
|
typeFormControl = new FormControl();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private parteyTimeslotsService: ParteyTimeslotsService,
|
||||||
|
private i18n: I18nService,
|
||||||
|
private datePipe: DatePipe,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
public dialog: MatDialog) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||||
|
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
room: ['', Validators.required],
|
||||||
|
starts: ['', Validators.nullValidator],
|
||||||
|
moderationStarts: ['', Validators.nullValidator],
|
||||||
|
expires: ['', Validators.nullValidator],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.filter.owner = "";
|
||||||
|
this.ownerFormControl.setValue("");
|
||||||
|
this.filter.type = "";
|
||||||
|
this.typeFormControl.setValue("");
|
||||||
|
this.filter.after = new Date().toUTCString();
|
||||||
|
this.afterFormControl.setValue(new Date());
|
||||||
|
|
||||||
|
this.searchFormControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
|
||||||
|
this.filter.search = value;
|
||||||
|
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = data;
|
||||||
|
}, (error) => {})
|
||||||
|
})
|
||||||
|
this.ownerFormControl.valueChanges.subscribe(value => {
|
||||||
|
this.filter.owner = value;
|
||||||
|
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = data;
|
||||||
|
}, (error) => {})
|
||||||
|
})
|
||||||
|
this.typeFormControl.valueChanges.subscribe(value => {
|
||||||
|
this.filter.type = value;
|
||||||
|
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = data;
|
||||||
|
}, (error) => {})
|
||||||
|
})
|
||||||
|
this.afterFormControl.valueChanges.subscribe((value: Date) => {
|
||||||
|
this.filter.after = value && value.toUTCString() || undefined;
|
||||||
|
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = data;
|
||||||
|
}, (error) => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.timeslotsQuota = 0;
|
||||||
|
|
||||||
|
this.parteyTimeslotsService.quota().subscribe((data: number) => {
|
||||||
|
this.timeslotsQuota = data;
|
||||||
|
})
|
||||||
|
|
||||||
|
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePages(event: PageEvent) {
|
||||||
|
this.page.page = event.pageIndex;
|
||||||
|
this.page.size = event.pageSize;
|
||||||
|
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = 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.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
|
||||||
|
this.timeslots = data;
|
||||||
|
}, (error) => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(timeslot) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||||
|
data: {
|
||||||
|
'label': 'partey.timeslots.confirmDelete',
|
||||||
|
'args': [timeslot.title, this.datePipe.transform(new Date(timeslot.starts), this.datetimeformat)]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if(result) {
|
||||||
|
this.parteyTimeslotsService.delete(timeslot.id).subscribe((result: any) => {
|
||||||
|
this.refresh();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openCreate(type) {
|
||||||
|
const dialogRef = this.dialog.open(ParteyTimeslotDialog, {
|
||||||
|
data: {"type": type ? type : "VIDEO", "visibility": "PROTECTED"},
|
||||||
|
minWidth: '400px'
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if(result) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openEdit(timeslot) {
|
||||||
|
const timeslotClone = JSON.parse(JSON.stringify(timeslot));
|
||||||
|
const dialogRef = this.dialog.open(ParteyTimeslotDialog, {
|
||||||
|
data: timeslotClone,
|
||||||
|
minWidth: '400px'
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if(result) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
copySecretToClipboard(secret) {
|
||||||
|
const selBox = document.createElement('textarea');
|
||||||
|
selBox.value = secret;
|
||||||
|
document.body.appendChild(selBox);
|
||||||
|
selBox.focus();
|
||||||
|
selBox.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(selBox);
|
||||||
|
this.snackBar.open(this.i18n.get("partey.timeslots.secret.copied", []), this.i18n.get("close", []), {
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-timeslot-dialog',
|
||||||
|
templateUrl: 'timeslot.dialog.html',
|
||||||
|
styleUrls: ['./timeslot.dialog.scss']
|
||||||
|
})
|
||||||
|
export class ParteyTimeslotDialog {
|
||||||
|
|
||||||
|
form: FormGroup;
|
||||||
|
timeslot;
|
||||||
|
|
||||||
|
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
||||||
|
types = ["VIDEO", "AUDIO"];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private parteyTimeslotsService: ParteyTimeslotsService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
public dialogRef: MatDialogRef<ParteyTimeslotDialog>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||||
|
this.timeslot = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
starts: ['', Validators.required],
|
||||||
|
ends: ['', Validators.required],
|
||||||
|
title: ['', Validators.nullValidator],
|
||||||
|
description: ['', Validators.nullValidator],
|
||||||
|
stream: ['', this.timeslot.type == 'VIDEO' ? Validators.required : Validators.nullValidator],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
create(timeslot) {
|
||||||
|
this.parteyTimeslotsService.create(timeslot).subscribe((result: any) => {
|
||||||
|
this.dialogRef.close(timeslot);
|
||||||
|
}, (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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save(timeslot) {
|
||||||
|
this.parteyTimeslotsService.update(timeslot).subscribe((result: any) => {
|
||||||
|
this.dialogRef.close(timeslot);
|
||||||
|
}, (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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -12,7 +12,22 @@ export class JitsiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(page: number, size: number, sort: string, desc: boolean) {
|
get(page: number, size: number, sort: string, desc: boolean) {
|
||||||
const httpParams = new HttpParams().set("page", "" + page).set("size", "" + size).set("sort", sort).set("desc", "" + desc);
|
let httpParams = new HttpParams();
|
||||||
|
if(page != undefined) {
|
||||||
|
httpParams = httpParams.set("page", "" + page);
|
||||||
|
}
|
||||||
|
if(size != undefined) {
|
||||||
|
httpParams = httpParams.set("size", "" + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sort != undefined) {
|
||||||
|
httpParams = httpParams.set("sort", sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(desc != undefined) {
|
||||||
|
httpParams = httpParams.set("desc", "" + desc);
|
||||||
|
}
|
||||||
|
|
||||||
return this.http.get(environment.apiUrl + "/jitsi/rooms", {params: httpParams});
|
return this.http.get(environment.apiUrl + "/jitsi/rooms", {params: httpParams});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
src/app/services/minetest.accounts.service.ts
Normal file
22
src/app/services/minetest.accounts.service.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
|
|
||||||
|
import {environment} from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class MinetestAccountsService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.http.get(environment.apiUrl + "/minetest/accounts");
|
||||||
|
}
|
||||||
|
|
||||||
|
create(minetestAccount) {
|
||||||
|
return this.http.post(environment.apiUrl + "/minetest/accounts", minetestAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
src/app/services/partey.timeslots.service.ts
Normal file
69
src/app/services/partey.timeslots.service.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
|
|
||||||
|
import {environment} from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class ParteyTimeslotsService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
get(page: number, size: number, sort: string, desc: boolean, filter : any) {
|
||||||
|
let httpParams = new HttpParams();
|
||||||
|
if(page != undefined) {
|
||||||
|
httpParams = httpParams.set("page", "" + page);
|
||||||
|
}
|
||||||
|
if(size != undefined) {
|
||||||
|
httpParams = httpParams.set("size", "" + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sort != undefined) {
|
||||||
|
httpParams = httpParams.set("sort", sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(desc != undefined) {
|
||||||
|
httpParams = httpParams.set("desc", "" + desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
if(filter.owner != undefined && filter.owner != "") {
|
||||||
|
httpParams = httpParams.set("owner", "" + filter.owner);
|
||||||
|
}
|
||||||
|
if(filter.after != undefined && filter.after != "") {
|
||||||
|
httpParams = httpParams.set("after", "" + filter.after);
|
||||||
|
}
|
||||||
|
if(filter.type != undefined && filter.type != "") {
|
||||||
|
httpParams = httpParams.set("type", "" + filter.type);
|
||||||
|
}
|
||||||
|
if(filter.visibility != undefined && filter.visibility != "") {
|
||||||
|
httpParams = httpParams.set("visibility", "" + filter.visibility);
|
||||||
|
}
|
||||||
|
if(filter.search != undefined && filter.search != "") {
|
||||||
|
httpParams = httpParams.set("search", "" + filter.search);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.http.get(environment.apiUrl + "/partey/timeslots", {params: httpParams});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
quota() {
|
||||||
|
return this.http.get(environment.apiUrl + "/partey/timeslots/quota");
|
||||||
|
}
|
||||||
|
|
||||||
|
create(timeslot) {
|
||||||
|
return this.http.post(environment.apiUrl + "/partey/timeslots", timeslot);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(timeslot) {
|
||||||
|
return this.http.patch(environment.apiUrl + "/partey/timeslots", timeslot);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id) {
|
||||||
|
return this.http.delete(environment.apiUrl + "/partey/timeslots/" + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user