Skip to content

Commit

Permalink
Modify OTAC Modal to handle durations (#193)
Browse files Browse the repository at this point in the history
* Modify OTAC Modal to handle durations

* Remove combinations
  • Loading branch information
jggoebel authored Jan 31, 2024
1 parent 401f89b commit e4c9bdb
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 77 deletions.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"ng2-dragula": "^2.1.1",
"ngx-dynamic-hooks": "^1.7.2",
"ngx-markdown": "^12.1.0",
"parse-duration": "^1.1.0",
"rxjs": "^6.6.7",
"tslib": "^2.0.0",
"xterm": "^4.13.0",
Expand Down
9 changes: 5 additions & 4 deletions src/app/data/otac.type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type OTAC = {
name: string;
user?: string;
redeemed_timestamp?: string
}
name: string;
user?: string;
redeemed_timestamp?: string;
max_duration?: string;
};
13 changes: 10 additions & 3 deletions src/app/data/scheduledevent.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,17 @@ export class ScheduledeventService extends ListableResourceClient<ScheduledEvent
);
}

public addOtacs(seId: string, count: number) {
public addOtacs(seId: string, count: number, duration: string = '') {
var params = new HttpParams().set('max_duration', duration);

return this.http
.get(
environment.server + '/a/scheduledevent/' + seId + '/otacs/add/' + count
.post(
environment.server +
'/a/scheduledevent/' +
seId +
'/otacs/add/' +
count,
params
)
.pipe(
switchMap((s: ServerResponse) => {
Expand Down
158 changes: 109 additions & 49 deletions src/app/event/otacmanagement/otacmanagement.component.html
Original file line number Diff line number Diff line change
@@ -1,53 +1,113 @@
<clr-modal [(clrModalOpen)]="open" [clrModalSize]="'xl'" [clrModalClosable]="false">
<h3 class="modal-title" *ngIf="open">
OTACs for {{ currentScheduledEvent.event_name }}
</h3>
<div class="modal-body">
<form clrForm [formGroup]="amountInputForm" class="flexbox" *rbac="['scheduledevents.update']">
<div>New OTACs:</div>
<clr-input-container class="input-container">
<input formControlName="amountInput" type="number" clrInput name="amountNewOtacsInput"
[(ngModel)]="amountNewOtacs" />
<clr-control-error>Input must be a positive Number</clr-control-error>
</clr-input-container>
<button [disabled]="!amountInputForm.valid" type="button" class="btn btn-primary" (click)="createOtacs()">
generate
</button>
</form>
<clr-modal
[(clrModalOpen)]="open"
[clrModalSize]="'xl'"
[clrModalClosable]="false"
>
<h3 class="modal-title" *ngIf="open">
OTACs for {{ currentScheduledEvent.event_name }}
</h3>
<div class="modal-body">
<form
clrForm
[formGroup]="amountInputForm"
class="flexbox"
*rbac="['scheduledevents.update']"
>
<div>New OTACs:</div>
<clr-input-container class="input-container">
<label class="otac-label">Count</label>
<input
formControlName="amountInput"
type="number"
clrInput
name="amountNewOtacsInput"
[(ngModel)]="amountNewOtacs"
/>
<clr-control-error>Input must be a positive Number</clr-control-error>
</clr-input-container>

<div class="datagrid-container">
<clr-datagrid>
<clr-dg-column [clrDgField]="'status'" [clrDgSortOrder]="descSort">Status</clr-dg-column>
<clr-dg-column>OTAC</clr-dg-column>
<clr-dg-column [clrDgField]="'userEmail'">Claimed by</clr-dg-column>
<clr-dg-column>Claimed at</clr-dg-column>
<clr-dg-column *rbac="['scheduledevents.update']">Delete</clr-dg-column>
<clr-input-container class="input-container">
<label class="otac-label">Duration</label>
<input
formControlName="duration"
type="text"
clrInput
name="duration"
placeholder="e.g. 1d, 24h, 60m"
title="Duration"
[(ngModel)]="duration"
/>
<clr-control-error
>Input must be a valid duration consisting of a number followed by
(d,h,m).</clr-control-error
>
</clr-input-container>
<button
[disabled]="!amountInputForm.valid"
type="button"
class="btn btn-primary"
(click)="createOtacs()"
>
generate
</button>
</form>

<clr-dg-row *clrDgItems="let otac of otacs">
<clr-dg-cell>
<span class="badge badge-success" *ngIf="otac.status == 'free'">free</span>
<span class="badge badge-info" *ngIf="otac.status == 'taken'">taken</span>
</clr-dg-cell>
<clr-dg-cell>{{ otac.name }}</clr-dg-cell>
<clr-dg-cell>{{ otac.userEmail }}</clr-dg-cell>
<clr-dg-cell>{{ otac.redeemed_timestamp }}</clr-dg-cell>
<clr-dg-cell *rbac="['scheduledevents.update']">
<button type="button" class="btn btn-warning-outline" (click)="deleteOtac(otac)">
Delete
</button>
</clr-dg-cell>
</clr-dg-row>
<div class="datagrid-container">
<clr-datagrid>
<clr-dg-column [clrDgField]="'status'" [clrDgSortOrder]="descSort"
>Status</clr-dg-column
>
<clr-dg-column>OTAC</clr-dg-column>
<clr-dg-column [clrDgField]="'userEmail'">Claimed by</clr-dg-column>
<clr-dg-column>Claimed at</clr-dg-column>
<clr-dg-column>Max. Duration</clr-dg-column>
<clr-dg-column *rbac="['scheduledevents.update']">Delete</clr-dg-column>

<clr-dg-footer>{{ getOverallInfo() }}</clr-dg-footer>
</clr-datagrid>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" (click)="exportCSV()">
Export to csv
</button>
<button type="button" class="btn btn-flat-primary" (click)="close()">
Close
</button>
<clr-dg-row *clrDgItems="let otac of otacs">
<clr-dg-cell>
<span class="badge badge-success" *ngIf="otac.status == 'free'"
>free</span
>
<span class="badge badge-info" *ngIf="otac.status == 'taken'"
>taken</span
>
<span
class="badge badge-danger"
*ngIf="otac.status == 'out-of-time'"
>out-of-time</span
>
</clr-dg-cell>
<clr-dg-cell>{{ otac.name }}</clr-dg-cell>
<clr-dg-cell>{{ otac.userEmail }}</clr-dg-cell>
<clr-dg-cell>{{ otac.redeemed_timestamp }}</clr-dg-cell>
<clr-dg-cell *ngIf="hasMaxDuration(otac)"
><clr-icon shape="clock"></clr-icon>
{{ otac.max_duration }}</clr-dg-cell
>
<clr-dg-cell *ngIf="!hasMaxDuration(otac)"
>Until Event ends</clr-dg-cell
>
<clr-dg-cell *rbac="['scheduledevents.update']">
<button
type="button"
class="btn btn-warning-outline"
(click)="deleteOtac(otac)"
>
Delete
</button>
</clr-dg-cell>
</clr-dg-row>

<clr-dg-footer>{{ getOverallInfo() }}</clr-dg-footer>
</clr-datagrid>
</div>
</clr-modal>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" (click)="exportCSV()">
Export to csv
</button>
<button type="button" class="btn btn-flat-primary" (click)="close()">
Close
</button>
</div>
</clr-modal>
30 changes: 17 additions & 13 deletions src/app/event/otacmanagement/otacmanagement.component.scss
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
.datagrid-container {
height: 60vh;
max-height: 60vh
height: 60vh;
max-height: 60vh;
}

.datagrid-container clr-datagrid {
max-height: 100%;
max-height: 100%;
}

.flexbox {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
height: fit-content;
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
height: fit-content;
width: 100%;
}

.input-container {
width: min-content;
padding-right: 2vw;
width: min-content;
padding-right: 2vw;
}

.flexbox * {
margin: 0;
}
margin: 0;
}

.otac-label.clr-col-md-2 {
max-width: fit-content;
}
44 changes: 36 additions & 8 deletions src/app/event/otacmanagement/otacmanagement.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import {
Output,
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import {
ClrDatagridSortOrder
} from '@clr/angular';
import { ClrDatagridSortOrder } from '@clr/angular';
import { Observable, Subject } from 'rxjs';
import { switchMap, takeUntil, tap } from 'rxjs/operators';
import { OTAC } from 'src/app/data/otac.type';
Expand All @@ -18,10 +16,11 @@ import { ScheduledeventService } from 'src/app/data/scheduledevent.service';
import { ServerResponse } from 'src/app/data/serverresponse';
import { User } from 'src/app/data/user';
import { UserService } from 'src/app/data/user.service';
import parse from 'parse-duration';

interface iOTAC extends OTAC {
userEmail?: string;
status: 'free' | 'taken';
status: 'free' | 'taken' | 'out-of-time';
}

@Component({
Expand All @@ -40,11 +39,15 @@ export class OTACManagementComponent implements OnInit, OnDestroy {

currentScheduledEvent: ScheduledEvent = null;

// The Validator Pattern for the duration only accepts strings that make up a valid duration
// For example "1d" for 1 day, "24h" for 24 Hours, "60m" for 60 minutes.
amountInputForm: FormGroup = new FormGroup({
amountInput: new FormControl(1, [Validators.required, Validators.min(1)]),
duration: new FormControl('', [Validators.pattern(/^(\d+[dhm]){1}$/)]),
});

amountNewOtacs: number = 1;
duration: string = '';

otacs: iOTAC[] = [];

Expand All @@ -59,8 +62,8 @@ export class OTACManagementComponent implements OnInit, OnDestroy {

ngOnInit(): void {
this.userService.list().subscribe({
next: (users) => this.users = users,
error: () => this.users = []
next: (users) => (this.users = users),
error: () => (this.users = []),
});

this.scheduledEvents
Expand All @@ -83,7 +86,7 @@ export class OTACManagementComponent implements OnInit, OnDestroy {
if (otac.user) {
return {
...otac,
status: 'taken',
status: this.hasRunOutOfTime(otac) ? 'taken' : 'out-of-time',
userEmail: this.getUsername(otac.user),
};
}
Expand All @@ -96,7 +99,11 @@ export class OTACManagementComponent implements OnInit, OnDestroy {

createOtacs() {
this.seService
.addOtacs(this.currentScheduledEvent.id, this.amountNewOtacs)
.addOtacs(
this.currentScheduledEvent.id,
this.amountNewOtacs,
this.duration
)
.subscribe((newOtacs: OTAC[]) => {
this.otacs.push(
...newOtacs.map((otac) => this.addUserinformation(otac))
Expand Down Expand Up @@ -131,6 +138,10 @@ export class OTACManagementComponent implements OnInit, OnDestroy {
this.getUsername(otac.user) +
', ' +
otac.redeemed_timestamp +
', ' +
otac.max_duration +
', ' +
otac.status +
'\n'
);
});
Expand All @@ -157,4 +168,21 @@ export class OTACManagementComponent implements OnInit, OnDestroy {
this.onDestroy.next('');
this.onDestroy.complete();
}

hasMaxDuration(otac: OTAC) {
return otac.max_duration != '';
}

hasRunOutOfTime(otac: OTAC) {
if (!otac.user || !otac.redeemed_timestamp || !otac.max_duration) {
return false;
}
const redeemedTimestamp = Date.parse(otac.redeemed_timestamp);
const duration = parse(otac.max_duration);
if (redeemedTimestamp + duration > Date.now()) {
return true;
}

return false;
}
}

0 comments on commit e4c9bdb

Please sign in to comment.