From 6d225beb46c460d17f1b29806df76955b718c83a Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 11 Jul 2018 14:43:00 -0400 Subject: [PATCH] user events --- src/app/app.module.ts | 3 + .../manage/events.component.html | 2 +- .../manage/people.component.html | 5 +- .../organizations/manage/people.component.ts | 26 ++++- .../manage/user-events.component.html | 68 +++++++++++++ .../manage/user-events.component.ts | 98 +++++++++++++++++++ src/locales/en/messages.json | 3 + 7 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 src/app/organizations/manage/user-events.component.html create mode 100644 src/app/organizations/manage/user-events.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index eeab584e40..39a929d42d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -43,6 +43,7 @@ import { GroupsComponent as OrgGroupsComponent } from './organizations/manage/gr import { ManageComponent as OrgManageComponent } from './organizations/manage/manage.component'; import { PeopleComponent as OrgPeopleComponent } from './organizations/manage/people.component'; import { UserAddEditComponent as OrgUserAddEditComponent } from './organizations/manage/user-add-edit.component'; +import { UserEventsComponent as OrgUserEventsComponent } from './organizations/manage/user-events.component'; import { UserGroupsComponent as OrgUserGroupsComponent } from './organizations/manage/user-groups.component'; import { ExportComponent as OrgExportComponent } from './organizations/tools/export.component'; @@ -190,6 +191,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe'; OrgPeopleComponent, OrgToolsComponent, OrgUserAddEditComponent, + OrgUserEventsComponent, OrgUserGroupsComponent, OrganizationsComponent, OrganizationLayoutComponent, @@ -242,6 +244,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe'; OrgEntityUsersComponent, OrgGroupAddEditComponent, OrgUserAddEditComponent, + OrgUserEventsComponent, OrgUserGroupsComponent, PasswordGeneratorHistoryComponent, PurgeVaultComponent, diff --git a/src/app/organizations/manage/events.component.html b/src/app/organizations/manage/events.component.html index a9ea1ded78..93da57fddf 100644 --- a/src/app/organizations/manage/events.component.html +++ b/src/app/organizations/manage/events.component.html @@ -17,7 +17,7 @@ -

{{'noItemsInList' | i18n}}

+

{{'noEventsInList' | i18n}}

diff --git a/src/app/organizations/manage/people.component.html b/src/app/organizations/manage/people.component.html index 552205e9d0..baed0e9277 100644 --- a/src/app/organizations/manage/people.component.html +++ b/src/app/organizations/manage/people.component.html @@ -58,11 +58,11 @@ {{'confirm' | i18n}} - + {{'groups' | i18n}} - + {{'eventLogs' | i18n}} @@ -79,3 +79,4 @@ + diff --git a/src/app/organizations/manage/people.component.ts b/src/app/organizations/manage/people.component.ts index f8f88c978c..7f1ee2274d 100644 --- a/src/app/organizations/manage/people.component.ts +++ b/src/app/organizations/manage/people.component.ts @@ -14,6 +14,7 @@ import { ApiService } from 'jslib/abstractions/api.service'; import { CryptoService } from 'jslib/abstractions/crypto.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { UserService } from 'jslib/abstractions/user.service'; import { OrganizationUserConfirmRequest } from 'jslib/models/request/organizationUserConfirmRequest'; @@ -26,6 +27,7 @@ import { Utils } from 'jslib/misc/utils'; import { ModalComponent } from '../../modal.component'; import { UserAddEditComponent } from './user-add-edit.component'; +import { UserEventsComponent } from './user-events.component'; import { UserGroupsComponent } from './user-groups.component'; @Component({ @@ -35,6 +37,7 @@ import { UserGroupsComponent } from './user-groups.component'; export class PeopleComponent implements OnInit { @ViewChild('addEdit', { read: ViewContainerRef }) addEditModalRef: ViewContainerRef; @ViewChild('groupsTemplate', { read: ViewContainerRef }) groupsModalRef: ViewContainerRef; + @ViewChild('eventsTemplate', { read: ViewContainerRef }) eventsModalRef: ViewContainerRef; loading = true; organizationId: string; @@ -43,17 +46,23 @@ export class PeopleComponent implements OnInit { organizationUserType = OrganizationUserType; organizationUserStatusType = OrganizationUserStatusType; actionPromise: Promise; + accessEvents = false; + accessGroups = false; private modal: ModalComponent = null; constructor(private apiService: ApiService, private route: ActivatedRoute, private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver, private platformUtilsService: PlatformUtilsService, private analytics: Angulartics2, - private toasterService: ToasterService, private cryptoService: CryptoService) { } + private toasterService: ToasterService, private cryptoService: CryptoService, + private userService: UserService) { } async ngOnInit() { this.route.parent.parent.params.subscribe(async (params) => { this.organizationId = params.organizationId; + const organization = await this.userService.getOrganization(this.organizationId); + this.accessEvents = organization.useEvents; + this.accessGroups = organization.useGroups; await this.load(); }); } @@ -159,7 +168,22 @@ export class PeopleComponent implements OnInit { } async events(user: OrganizationUserUserDetailsResponse) { + if (this.modal != null) { + this.modal.close(); + } + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.eventsModalRef.createComponent(factory).instance; + const childComponent = this.modal.show( + UserEventsComponent, this.eventsModalRef); + + childComponent.name = user != null ? user.name || user.email : null; + childComponent.organizationId = this.organizationId; + childComponent.organizationUserId = user != null ? user.id : null; + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); } private async doConfirmation(user: OrganizationUserUserDetailsResponse) { diff --git a/src/app/organizations/manage/user-events.component.html b/src/app/organizations/manage/user-events.component.html new file mode 100644 index 0000000000..4e10276885 --- /dev/null +++ b/src/app/organizations/manage/user-events.component.html @@ -0,0 +1,68 @@ +
+ + + + + + + + + + + + + + +
{{'timestamp' | i18n}} + {{'device' | i18n}} + {{'event' | i18n}}
{{e.date | date:'medium'}} + +
+ + + + + + diff --git a/src/app/organizations/manage/user-events.component.ts b/src/app/organizations/manage/user-events.component.ts new file mode 100644 index 0000000000..743523ba03 --- /dev/null +++ b/src/app/organizations/manage/user-events.component.ts @@ -0,0 +1,98 @@ +import { + Component, + Input, + OnInit, +} from '@angular/core'; + +import { ToasterService } from 'angular2-toaster'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { EventService } from '../../services/event.service'; + +import { EventResponse } from 'jslib/models/response/eventResponse'; +import { ListResponse } from 'jslib/models/response/listResponse'; + +@Component({ + selector: 'app-user-events', + templateUrl: 'user-events.component.html', +}) +export class UserEventsComponent implements OnInit { + @Input() name: string; + @Input() organizationUserId: string; + @Input() organizationId: string; + + loading = true; + loaded = false; + events: any[]; + start: string; + end: string; + continuationToken: string; + refreshPromise: Promise; + morePromise: Promise; + + constructor(private apiService: ApiService, private i18nService: I18nService, + private eventService: EventService, private toasterService: ToasterService) { } + + async ngOnInit() { + const defaultDates = this.eventService.getDefaultDateFilters(); + this.start = defaultDates[0]; + this.end = defaultDates[1]; + 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) { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('invalidDateRange')); + return; + } + + this.loading = true; + let response: ListResponse; + try { + const promise = this.apiService.getEventsOrganizationUser(this.organizationId, this.organizationUserId, + dates[0], dates[1], clearExisting ? null : this.continuationToken); + if (clearExisting) { + this.refreshPromise = promise; + } else { + this.morePromise = promise; + } + response = await promise; + } catch { } + + this.continuationToken = response.continuationToken; + const events = response.data.map((r) => { + const userId = r.actingUserId == null ? r.userId : r.actingUserId; + const eventInfo = this.eventService.getEventInfo(r); + return { + message: eventInfo.message, + appIcon: eventInfo.appIcon, + appName: eventInfo.appName, + userId: userId, + date: r.date, + ip: r.ipAddress, + type: r.type, + }; + }); + + if (!clearExisting && this.events != null && this.events.length > 0) { + this.events = this.events.concat(events); + } else { + this.events = events; + } + + this.loading = false; + this.morePromise = null; + this.refreshPromise = null; + } +} diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 41e556e12a..d5315d7eba 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -600,6 +600,9 @@ "noUsersInList": { "message": "There are no users to list." }, + "noEventsInList": { + "message": "There are no events to list." + }, "newOrganization": { "message": "New Organization" },