event filters and paging

This commit is contained in:
Kyle Spearrin 2018-07-06 23:08:10 -04:00
parent 15cc46bba5
commit e9da73b930
9 changed files with 191 additions and 7 deletions

2
jslib

@ -1 +1 @@
Subproject commit c44e633f42b9972386fb4f3aaff43a0d8506781e
Subproject commit 1b7ace04951997ec07d7ffe412dc052df12b7a69

View File

@ -28,8 +28,8 @@ export class CollectionsComponent implements OnInit {
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
await this.load();
});
await this.load();
}
async load() {

View File

@ -1,3 +1,46 @@
<div class="page-header">
<div class="page-header d-flex">
<h1>{{'eventLogs' | i18n}}</h1>
<div class="ml-auto d-flex">
<div class="form-inline">
<label class="sr-only" for="start">{{'startDate' | i18n}}</label>
<input type="datetime-local" class="form-control form-control-sm" id="start" placeholder="{{'startDate' | i18n}}" [(ngModel)]="start">
<span class="mx-2">-</span>
<label class="sr-only" for="end">{{'endDate' | i18n}}</label>
<input type="datetime-local" class="form-control form-control-sm" id="end" placeholder="{{'endDate' | i18n}}" [(ngModel)]="end">
</div>
<button #refreshBtn [appApiAction]="refreshPromise" type="button" class="btn btn-sm btn-outline-primary ml-3" (click)="loadEvents(true)"
[disabled]="loaded && refreshBtn.loading">
<i class="fa fa-refresh fa-fw" [ngClass]="{'fa-spin': loaded && refreshBtn.loading}"></i>
{{'refresh' | i18n}}
</button>
</div>
</div>
<i class="fa fa-spinner fa-spin text-muted" *ngIf="!loaded"></i>
<ng-container *ngIf="loaded">
<p *ngIf="!events || !events.length">{{'noItemsInList' | i18n}}</p>
<table class="table table-hover" *ngIf="events && events.length">
<thead>
<tr>
<th class="border-top-0">{{'timestamp' | i18n}}</th>
<th class="border-top-0">{{'user' | i18n}}</th>
<th class="border-top-0">{{'event' | i18n}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let e of events">
<td>{{e.date | date:'medium'}}</td>
<td>
{{e.userId}}
</td>
<td>
{{e.type}}
</td>
</tr>
</tbody>
</table>
<button #moreBtn [appApiAction]="morePromise" type="button" class="btn btn-block btn-link btn-submit" (click)="loadEvents(false)"
[disabled]="loaded && moreBtn.loading" *ngIf="continuationToken">
<i class="fa fa-spinner fa-spin"></i>
<span>{{'loadMore' | i18n}}</span>
</button>
</ng-container>

View File

@ -1,7 +1,93 @@
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ApiService } from 'jslib/abstractions/api.service';
import { EventService } from '../../services/event.service';
@Component({
selector: 'app-org-events',
templateUrl: 'events.component.html',
})
export class EventsComponent { }
export class EventsComponent implements OnInit {
loading = true;
loaded = false;
organizationId: string;
events: any[];
start: string;
end: string;
continuationToken: string;
refreshPromise: Promise<any>;
morePromise: Promise<any>;
constructor(private apiService: ApiService, private route: ActivatedRoute,
private eventService: EventService) { }
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
const defaultDates = this.eventService.getDefaultDateFilters();
this.start = defaultDates[0];
this.end = defaultDates[1];
await this.load();
});
}
async load() {
// TODO: load users
await this.loadEvents(true);
this.loaded = true;
}
async loadEvents(clearExisting: boolean) {
if (this.refreshPromise != null || this.morePromise != null) {
return;
}
let dates: string[] = null;
try {
dates = this.eventService.formatDateFilters(this.start, this.end);
} catch (e) {
// TODO: error toast
return;
}
this.loading = true;
try {
const promise = this.apiService.getEventsOrganization(this.organizationId, dates[0], dates[1],
clearExisting ? null : this.continuationToken);
if (clearExisting) {
this.refreshPromise = promise;
} else {
this.morePromise = promise;
}
const response = await promise;
this.continuationToken = response.continuationToken;
const events = response.data.map((r) => {
const userId = r.actingUserId == null ? r.userId : r.actingUserId;
const eventInfo: any = {};
const htmlMessage = '';
return {
message: htmlMessage,
appIcon: eventInfo.appIcon,
appName: eventInfo.appName,
userId: userId,
userName: userId != null ? 'user' : '-',
date: r.date,
ip: r.ipAddress,
};
});
if (!clearExisting && this.events != null && this.events.length > 0) {
this.events = this.events.concat(events);
} else {
this.events = events;
}
} catch { }
this.loading = false;
this.morePromise = null;
this.refreshPromise = null;
}
}

View File

@ -27,8 +27,8 @@ export class GroupsComponent implements OnInit {
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
await this.load();
});
await this.load();
}
async load() {

View File

@ -32,8 +32,8 @@ export class PeopleComponent implements OnInit {
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
await this.load();
});
await this.load();
}
async load() {

View File

@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import { I18nService } from 'jslib/abstractions/i18n.service';
@Injectable()
export class EventService {
constructor(private i18nService: I18nService) { }
getDefaultDateFilters() {
const d = new Date();
const end = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59);
d.setDate(d.getDate() - 30);
const start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0);
return [this.toDateTimeLocalString(start), this.toDateTimeLocalString(end)];
}
formatDateFilters(filterStart: string, filterEnd: string) {
const start: Date = new Date(filterStart);
const end: Date = new Date(filterEnd + ':59.999');
if (isNaN(start.getTime()) || isNaN(end.getTime()) || end < start) {
throw new Error('Invalid date range.');
}
return [start.toISOString(), end.toISOString()];
}
private toDateTimeLocalString(date: Date) {
return date.getFullYear() +
'-' + this.pad(date.getMonth() + 1) +
'-' + this.pad(date.getDate()) +
'T' + this.pad(date.getHours()) +
':' + this.pad(date.getMinutes());
}
private pad(num: number) {
const norm = Math.floor(Math.abs(num));
return (norm < 10 ? '0' : '') + norm;
}
}

View File

@ -11,6 +11,7 @@ import { I18nService } from '../../services/i18n.service';
import { MemoryStorageService } from '../../services/memoryStorage.service';
import { WebPlatformUtilsService } from '../../services/webPlatformUtils.service';
import { EventService } from './event.service';
import { OrganizationGuardService } from './organization-guard.service';
import { OrganizationTypeGuardService } from './organization-type-guard.service';
import { RouterService } from './router.service';
@ -148,6 +149,7 @@ export function initFactory(): Function {
OrganizationTypeGuardService,
UnauthGuardService,
RouterService,
EventService,
{ provide: AuditServiceAbstraction, useValue: auditService },
{ provide: AuthServiceAbstraction, useValue: authService },
{ provide: CipherServiceAbstraction, useValue: cipherService },

View File

@ -1732,5 +1732,20 @@
},
"all": {
"message": "All"
},
"refresh": {
"message": "Refresh"
},
"timestamp": {
"message": "Timestamp"
},
"event": {
"message": "Event"
},
"unknown": {
"message": "Unknown"
},
"loadMore": {
"message": "Load More"
}
}