1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[EC-551] Update Event Logs Client Column (#3572)

* [EC-551] Fix RxJS warnings

* [EC-551] Update page to use CL components and Tailwind classes

* [EC-551] Update Client column to use text instead of icon. Update language and i18n.
This commit is contained in:
Shane Melton
2022-10-03 11:51:17 -07:00
committed by GitHub
parent efc68a7613
commit 5f5cd47474
6 changed files with 100 additions and 71 deletions

View File

@@ -464,16 +464,14 @@ export class EventService {
private formatGroupId(ev: EventResponse) { private formatGroupId(ev: EventResponse) {
const shortId = this.getShortId(ev.groupId); const shortId = this.getShortId(ev.groupId);
const a = this.makeAnchor(shortId); const a = this.makeAnchor(shortId);
a.setAttribute( a.setAttribute("href", "#/organizations/" + ev.organizationId + "/groups?search=" + shortId);
"href",
"#/organizations/" + ev.organizationId + "/manage/groups?search=" + shortId
);
return a.outerHTML; return a.outerHTML;
} }
private formatCollectionId(ev: EventResponse) { private formatCollectionId(ev: EventResponse) {
const shortId = this.getShortId(ev.collectionId); const shortId = this.getShortId(ev.collectionId);
const a = this.makeAnchor(shortId); const a = this.makeAnchor(shortId);
// TODO: Update view/edit collection link after EC-14 is completed
a.setAttribute( a.setAttribute(
"href", "href",
"#/organizations/" + ev.organizationId + "/manage/collections?search=" + shortId "#/organizations/" + ev.organizationId + "/manage/collections?search=" + shortId
@@ -488,7 +486,7 @@ export class EventService {
"href", "href",
"#/organizations/" + "#/organizations/" +
ev.organizationId + ev.organizationId +
"/manage/people?search=" + "/members?search=" +
shortId + shortId +
"&viewEvents=" + "&viewEvents=" +
ev.organizationUserId ev.organizationUserId

View File

@@ -1,54 +1,57 @@
<div class="page-header d-flex"> <div class="tw-mb-4">
<h1>{{ "eventLogs" | i18n }}</h1> <h1>{{ "eventLogs" | i18n }}</h1>
<div class="ml-auto d-flex"> <div class="tw-mt-4 tw-flex tw-items-center">
<div class="form-inline"> <bit-form-field>
<label class="sr-only" for="start">{{ "startDate" | i18n }}</label> <bit-label>{{ "from" | i18n }}</bit-label>
<input <input
bitInput
type="datetime-local" type="datetime-local"
class="form-control form-control-sm"
id="start"
placeholder="{{ 'startDate' | i18n }}" placeholder="{{ 'startDate' | i18n }}"
[(ngModel)]="start" [(ngModel)]="start"
placeholder="YYYY-MM-DDTHH:MM"
(change)="dirtyDates = true" (change)="dirtyDates = true"
/> />
<span class="mx-2">-</span> </bit-form-field>
<label class="sr-only" for="end">{{ "endDate" | i18n }}</label> <span class="tw-mx-2">-</span>
<bit-form-field>
<bit-label>{{ "to" | i18n }}</bit-label>
<input <input
bitInput
type="datetime-local" type="datetime-local"
class="form-control form-control-sm"
id="end"
placeholder="{{ 'endDate' | i18n }}" placeholder="{{ 'endDate' | i18n }}"
[(ngModel)]="end" [(ngModel)]="end"
placeholder="YYYY-MM-DDTHH:MM"
(change)="dirtyDates = true" (change)="dirtyDates = true"
/> />
</div> </bit-form-field>
<form #refreshForm [appApiAction]="refreshPromise" class="d-inline"> <form #refreshForm [appApiAction]="refreshPromise">
<button <button
class="tw-mx-3 tw-mt-1"
type="button" type="button"
class="btn btn-sm btn-outline-primary ml-3" bitButton
buttonType="primary"
(click)="loadEvents(true)" (click)="loadEvents(true)"
[disabled]="loaded && refreshForm.loading" [disabled]="loaded && refreshForm.loading"
> >
<i {{ "update" | i18n }}
class="bwi bwi-refresh bwi-fw"
aria-hidden="true"
[ngClass]="{ 'bwi-spin': loaded && refreshForm.loading }"
></i>
{{ "refresh" | i18n }}
</button> </button>
</form> </form>
<form #exportForm [appApiAction]="exportPromise" class="d-inline"> <form #exportForm [appApiAction]="exportPromise">
<button <button
type="button" type="button"
class="btn btn-sm btn-outline-primary btn-submit manual ml-3" class="tw-mt-1"
bitButton
[ngClass]="{ loading: exportForm.loading }" [ngClass]="{ loading: exportForm.loading }"
(click)="exportEvents()" (click)="exportEvents()"
[disabled]="(loaded && exportForm.loading) || dirtyDates" [disabled]="(loaded && exportForm.loading) || dirtyDates"
> >
<i class="bwi bwi-spinner bwi-spin" aria-hidden="true"></i>
<span>{{ "export" | i18n }}</span> <span>{{ "export" | i18n }}</span>
<i
class="bwi bwi-fw"
aria-hidden="true"
[ngClass]="{
'bwi-sign-in': !exportForm.loading,
'bwi-spinner bwi-spin': exportForm.loading
}"
></i>
</button> </button>
</form> </form>
</div> </div>
@@ -63,45 +66,44 @@
</ng-container> </ng-container>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<p *ngIf="!events || !events.length">{{ "noEventsInList" | i18n }}</p> <p *ngIf="!events || !events.length">{{ "noEventsInList" | i18n }}</p>
<table class="table table-hover" *ngIf="events && events.length"> <bit-table *ngIf="events && events.length">
<thead> <ng-container header>
<tr> <tr>
<th class="border-top-0" width="210">{{ "timestamp" | i18n }}</th> <th bitCell style="width: 210px">{{ "timestamp" | i18n }}</th>
<th class="border-top-0" width="40"> <th bitCell style="width: 100px">{{ "client" | i18n }}</th>
<span class="sr-only">{{ "device" | i18n }}</span> <th bitCell style="width: 150px">{{ "member" | i18n }}</th>
</th> <th bitCell>{{ "event" | i18n }}</th>
<th class="border-top-0" width="150">{{ "user" | i18n }}</th>
<th class="border-top-0">{{ "event" | i18n }}</th>
</tr> </tr>
</thead> </ng-container>
<tbody> <ng-container body>
<tr *ngFor="let e of events"> <tr bitRow *ngFor="let e of events">
<td>{{ e.date | date: "medium" }}</td> <td bitCell>{{ e.date | date: "medium" }}</td>
<td> <td bitCell>
<i <span title="{{ e.appName }}, {{ e.ip }}">{{ e.appName }}</span>
class="text-muted bwi bwi-lg {{ e.appIcon }}"
title="{{ e.appName }}, {{ e.ip }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ e.appName }}, {{ e.ip }}</span>
</td> </td>
<td> <td bitCell>
<span title="{{ e.userEmail }}">{{ e.userName }}</span> <span title="{{ e.userEmail }}">{{ e.userName }}</span>
</td> </td>
<td [innerHTML]="e.message"></td> <td bitCell [innerHTML]="e.message"></td>
</tr> </tr>
</tbody> </ng-container>
</table> </bit-table>
<button <button
#moreBtn #moreBtn
[appApiAction]="morePromise" [appApiAction]="morePromise"
type="button" type="button"
class="btn btn-block btn-link btn-submit" bitButton
buttonType="primary"
(click)="loadEvents(false)" (click)="loadEvents(false)"
[disabled]="loaded && moreBtn.loading" [disabled]="loaded && moreBtn.loading"
*ngIf="continuationToken" *ngIf="continuationToken"
> >
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i> <i
class="bwi bwi-spinner bwi-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
*ngIf="moreBtn.loading"
></i>
<span>{{ "loadMore" | i18n }}</span> <span>{{ "loadMore" | i18n }}</span>
</button> </button>
</ng-container> </ng-container>

View File

@@ -1,5 +1,6 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { concatMap, Subject, takeUntil } from "rxjs";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
@@ -20,13 +21,13 @@ import { EventService } from "../../core";
selector: "app-org-events", selector: "app-org-events",
templateUrl: "events.component.html", templateUrl: "events.component.html",
}) })
// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class EventsComponent extends BaseEventsComponent implements OnInit, OnDestroy {
export class EventsComponent extends BaseEventsComponent implements OnInit {
exportFileName = "org-events"; exportFileName = "org-events";
organizationId: string; organizationId: string;
organization: Organization; organization: Organization;
private orgUsersUserIdMap = new Map<string, any>(); private orgUsersUserIdMap = new Map<string, any>();
private destroy$ = new Subject<void>();
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
@@ -53,17 +54,20 @@ export class EventsComponent extends BaseEventsComponent implements OnInit {
} }
async ngOnInit() { async ngOnInit() {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.params
this.route.parent.parent.params.subscribe(async (params) => { .pipe(
this.organizationId = params.organizationId; concatMap(async (params) => {
this.organization = await this.organizationService.get(this.organizationId); this.organizationId = params.organizationId;
if (this.organization == null || !this.organization.useEvents) { this.organization = await this.organizationService.get(this.organizationId);
this.router.navigate(["/organizations", this.organizationId]); if (this.organization == null || !this.organization.useEvents) {
return; await this.router.navigate(["/organizations", this.organizationId]);
} return;
}
await this.load(); await this.load();
}); }),
takeUntil(this.destroy$)
)
.subscribe();
} }
async load() { async load() {
@@ -126,4 +130,9 @@ export class EventsComponent extends BaseEventsComponent implements OnInit {
return null; return null;
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
} }

View File

@@ -1,5 +1,5 @@
import { DragDropModule } from "@angular/cdk/drag-drop"; import { DragDropModule } from "@angular/cdk/drag-drop";
import { DatePipe, CommonModule } from "@angular/common"; import { CommonModule, DatePipe } from "@angular/common";
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { RouterModule } from "@angular/router"; import { RouterModule } from "@angular/router";
@@ -12,9 +12,10 @@ import {
ButtonModule, ButtonModule,
CalloutModule, CalloutModule,
FormFieldModule, FormFieldModule,
MenuModule,
TabsModule,
IconModule, IconModule,
MenuModule,
TableModule,
TabsModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
// Register the locales for the application // Register the locales for the application
@@ -46,6 +47,7 @@ import "./locales";
FormFieldModule, FormFieldModule,
IconModule, IconModule,
TabsModule, TabsModule,
TableModule,
], ],
exports: [ exports: [
CommonModule, CommonModule,
@@ -65,6 +67,7 @@ import "./locales";
FormFieldModule, FormFieldModule,
IconModule, IconModule,
TabsModule, TabsModule,
TableModule,
], ],
providers: [DatePipe], providers: [DatePipe],
bootstrap: [], bootstrap: [],

View File

@@ -4512,6 +4512,10 @@
"clients": { "clients": {
"message": "Clients" "message": "Clients"
}, },
"client": {
"message": "Client",
"description": "This is used as a table header to describe which client application created an event log."
},
"providerAdmin": { "providerAdmin": {
"message": "Provider Admin" "message": "Provider Admin"
}, },
@@ -5431,5 +5435,17 @@
}, },
"numberOfUsers": { "numberOfUsers": {
"message": "Number of users" "message": "Number of users"
},
"from": {
"message": "From"
},
"to": {
"message": "To"
},
"member": {
"message": "Member"
},
"update": {
"message": "Update"
} }
} }

View File

@@ -9,5 +9,6 @@ export * from "./menu";
export * from "./dialog"; export * from "./dialog";
export * from "./link"; export * from "./link";
export * from "./tabs"; export * from "./tabs";
export * from "./table";
export * from "./toggle-group"; export * from "./toggle-group";
export * from "./utils/i18n-mock.service"; export * from "./utils/i18n-mock.service";