event filters and paging
This commit is contained in:
parent
15cc46bba5
commit
e9da73b930
2
jslib
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit c44e633f42b9972386fb4f3aaff43a0d8506781e
|
||||
Subproject commit 1b7ace04951997ec07d7ffe412dc052df12b7a69
|
|
@ -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() {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 },
|
||||
|
|
|
@ -1732,5 +1732,20 @@
|
|||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
},
|
||||
"refresh": {
|
||||
"message": "Refresh"
|
||||
},
|
||||
"timestamp": {
|
||||
"message": "Timestamp"
|
||||
},
|
||||
"event": {
|
||||
"message": "Event"
|
||||
},
|
||||
"unknown": {
|
||||
"message": "Unknown"
|
||||
},
|
||||
"loadMore": {
|
||||
"message": "Load More"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue