+|
+
+ {{ displayValue(page) }}
+
+|
+
diff --git a/apps/red-ui/src/app/components/pagination/pagination.component.scss b/apps/red-ui/src/app/components/pagination/pagination.component.scss
new file mode 100644
index 000000000..e2061e12a
--- /dev/null
+++ b/apps/red-ui/src/app/components/pagination/pagination.component.scss
@@ -0,0 +1,28 @@
+@import '../../../assets/styles/red-variables';
+
+:host {
+ display: flex;
+
+ > *:not(:last-child) {
+ margin-right: 12px;
+ }
+
+ .disabled,
+ span {
+ opacity: 0.5;
+ }
+
+ .page {
+ cursor: pointer;
+
+ &.disabled,
+ &.dots {
+ cursor: default;
+ }
+
+ &.active {
+ color: $primary;
+ font-weight: bold;
+ }
+ }
+}
diff --git a/apps/red-ui/src/app/components/pagination/pagination.component.ts b/apps/red-ui/src/app/components/pagination/pagination.component.ts
new file mode 100644
index 000000000..e4472a8c0
--- /dev/null
+++ b/apps/red-ui/src/app/components/pagination/pagination.component.ts
@@ -0,0 +1,68 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+
+const DISPLAYED_ITEMS = 5;
+
+@Component({
+ selector: 'redaction-pagination',
+ templateUrl: './pagination.component.html',
+ styleUrls: ['./pagination.component.scss']
+})
+export class PaginationComponent implements OnInit {
+ private _currentPage: number;
+ private _totalPages: number;
+
+ public displayedPages: (number | string)[];
+
+ @Input()
+ public set settings(value: { currentPage: number; totalPages: number }) {
+ this._currentPage = value.currentPage;
+ this._totalPages = value.totalPages;
+ this._updatePagesArray();
+ }
+
+ public get currentPage() {
+ return this._currentPage;
+ }
+
+ public get totalPages() {
+ return this._totalPages;
+ }
+
+ @Output() pageChanged = new EventEmitter();
+
+ public displayed;
+
+ constructor() {}
+
+ ngOnInit(): void {}
+
+ private _updatePagesArray() {
+ this.displayedPages = [0];
+ if (Math.max(1, this.currentPage - 1) > 1) {
+ this.displayedPages.push('...');
+ }
+ for (let page = Math.max(1, this.currentPage - 1); page <= Math.min(this.currentPage + 1, this.totalPages - 1); ++page) {
+ this.displayedPages.push(page);
+ }
+ if (Math.min(this.currentPage + 1, this.totalPages - 1) !== this.totalPages - 1) {
+ if (this.currentPage + 1 < this.totalPages - 2) {
+ this.displayedPages.push('...');
+ }
+ this.displayedPages.push(this.totalPages - 1);
+ }
+ }
+
+ public get allDisplayed(): boolean {
+ return this.totalPages > DISPLAYED_ITEMS;
+ }
+
+ public selectPage(page: number | string) {
+ if (page !== '...') {
+ this.pageChanged.emit(page as number);
+ }
+ }
+
+ public displayValue(page: number | string) {
+ return page === '...' ? page : (page as number) + 1;
+ }
+}
diff --git a/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.html b/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.html
new file mode 100644
index 000000000..651646ca9
--- /dev/null
+++ b/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ log.message }}
+
+
+ {{ log.recordDate }}
+
+
+ {{ log.userId }}
+
+
+ {{ log.category }}
+
+
+
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.scss b/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.scss
new file mode 100644
index 000000000..03177cc29
--- /dev/null
+++ b/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.scss
@@ -0,0 +1,37 @@
+.left-container {
+ width: 100vw;
+
+ .header-item {
+ justify-content: space-between;
+ }
+
+ .actions-wrapper {
+ display: flex;
+ align-items: center;
+
+ .separator {
+ margin: 0 20px;
+ font-weight: bold;
+ font-size: 16px;
+ opacity: 0.7;
+ }
+ }
+
+ cdk-virtual-scroll-viewport {
+ ::ng-deep.cdk-virtual-scroll-content-wrapper {
+ grid-template-columns: 1fr 1fr 1fr 1fr 11px;
+
+ .table-item {
+ > div {
+ padding: 0 24px;
+ }
+ }
+ }
+
+ &.has-scrollbar:hover {
+ ::ng-deep.cdk-virtual-scroll-content-wrapper {
+ grid-template-columns: 1fr 1fr 1fr 1fr;
+ }
+ }
+ }
+}
diff --git a/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.ts b/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.ts
new file mode 100644
index 000000000..524a52280
--- /dev/null
+++ b/apps/red-ui/src/app/screens/admin/audit-screen/audit-screen.component.ts
@@ -0,0 +1,90 @@
+import { Component, OnInit } from '@angular/core';
+import { PermissionsService } from '../../../common/service/permissions.service';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { debounce } from '../../../utils/debounce';
+import { AuditControllerService, AuditModel, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http';
+import { TranslateService } from '@ngx-translate/core';
+
+const PAGE_SIZE = 5;
+let ALL_CATEGORIES: string;
+
+@Component({
+ selector: 'redaction-audit-screen',
+ templateUrl: './audit-screen.component.html',
+ styleUrls: ['./audit-screen.component.scss']
+})
+export class AuditScreenComponent implements OnInit {
+ public categoryForm: FormGroup;
+ public viewReady = false;
+ public categories: string[] = [];
+ public logs: AuditResponse;
+ public currentPage = 1;
+
+ constructor(
+ public readonly permissionsService: PermissionsService,
+ private readonly _formBuilder: FormBuilder,
+ private readonly _auditControllerService: AuditControllerService,
+ private readonly _translateService: TranslateService
+ ) {
+ ALL_CATEGORIES = this._translateService.instant('audit-screen.all-categories');
+
+ this.categoryForm = this._formBuilder.group({
+ category: [ALL_CATEGORIES]
+ });
+
+ this.categoryForm.valueChanges.subscribe((value) => this._filterByCategories(value));
+ }
+
+ ngOnInit(): void {
+ this._fetchAllData().then(() => {
+ this.viewReady = true;
+ });
+ }
+
+ private _getAuditLogRequestBody(config?: { page: number }) {
+ const category = this.categoryForm.get('category').value;
+ return { pageSize: PAGE_SIZE, withTotalHits: true, page: config?.page, category: category === ALL_CATEGORIES ? undefined : category };
+ }
+
+ private _fetchAllData(): Promise {
+ const promises = [];
+
+ promises.push(this._auditControllerService.getAuditCategories().toPromise());
+ promises.push(this._auditControllerService.searchAuditLog(this._getAuditLogRequestBody()).toPromise());
+
+ return Promise.all(promises).then((data) => {
+ this.categories = data[0].map((c) => c.category);
+ this.categories.splice(0, 0, ALL_CATEGORIES);
+ this.logs = data[1];
+ });
+ }
+
+ private _filterByCategories(value: { category: string }) {
+ this.viewReady = false;
+ this._auditControllerService
+ .searchAuditLog(this._getAuditLogRequestBody())
+ .toPromise()
+ .then((data) => {
+ this.logs = data;
+ this.viewReady = true;
+ });
+ }
+
+ public get totalPages(): number {
+ if (!this.logs) {
+ return 0;
+ }
+ return Math.ceil(this.logs.totalHits / PAGE_SIZE);
+ }
+
+ public pageChanged(page: number) {
+ this.viewReady = false;
+ this._auditControllerService
+ .searchAuditLog(this._getAuditLogRequestBody({ page }))
+ .toPromise()
+ .then((data) => {
+ this.logs = data;
+ this.viewReady = true;
+ });
+ }
+}
diff --git a/apps/red-ui/src/app/screens/admin/rule-sets-listing-screen/rule-sets-listing-screen.component.ts b/apps/red-ui/src/app/screens/admin/rule-sets-listing-screen/rule-sets-listing-screen.component.ts
index 07005bcac..d9824c4d5 100644
--- a/apps/red-ui/src/app/screens/admin/rule-sets-listing-screen/rule-sets-listing-screen.component.ts
+++ b/apps/red-ui/src/app/screens/admin/rule-sets-listing-screen/rule-sets-listing-screen.component.ts
@@ -6,8 +6,6 @@ import { PermissionsService } from '../../../common/service/permissions.service'
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounce } from '../../../utils/debounce';
import { RuleSetModel } from '@redaction/red-ui-http';
-import { tap } from 'rxjs/operators';
-import { forkJoin } from 'rxjs';
@Component({
selector: 'redaction-rule-sets-listing-screen',
diff --git a/apps/red-ui/src/app/screens/admin/users/user-listing-screen.component.html b/apps/red-ui/src/app/screens/admin/user-listing-screen/user-listing-screen.component.html
similarity index 100%
rename from apps/red-ui/src/app/screens/admin/users/user-listing-screen.component.html
rename to apps/red-ui/src/app/screens/admin/user-listing-screen/user-listing-screen.component.html
diff --git a/apps/red-ui/src/app/screens/admin/users/user-listing-screen.component.scss b/apps/red-ui/src/app/screens/admin/user-listing-screen/user-listing-screen.component.scss
similarity index 100%
rename from apps/red-ui/src/app/screens/admin/users/user-listing-screen.component.scss
rename to apps/red-ui/src/app/screens/admin/user-listing-screen/user-listing-screen.component.scss
diff --git a/apps/red-ui/src/app/screens/admin/users/user-listing-screen.component.ts b/apps/red-ui/src/app/screens/admin/user-listing-screen/user-listing-screen.component.ts
similarity index 100%
rename from apps/red-ui/src/app/screens/admin/users/user-listing-screen.component.ts
rename to apps/red-ui/src/app/screens/admin/user-listing-screen/user-listing-screen.component.ts
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index 01530fb08..47543d156 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -790,6 +790,23 @@
"legend": "Legend"
}
},
+ "audit": "Audit",
+ "audit-screen": {
+ "table-header": {
+ "title": "{{length}} Logs"
+ },
+ "table-col-names": {
+ "message": "Message",
+ "user": "User",
+ "date": "Date",
+ "category": "Category"
+ },
+ "all-categories": "All Categories"
+ },
+ "pagination": {
+ "previous": "Prev",
+ "next": "Next"
+ },
"default-colors": "Default Colors",
"default-colors-screen": {
"table-header": {
diff --git a/apps/red-ui/src/assets/styles/red-select.scss b/apps/red-ui/src/assets/styles/red-select.scss
index 64853a80a..b43b61ba5 100644
--- a/apps/red-ui/src/assets/styles/red-select.scss
+++ b/apps/red-ui/src/assets/styles/red-select.scss
@@ -13,3 +13,8 @@
color: $grey-1;
}
}
+
+.mat-form-field.no-label .mat-form-field-infix {
+ padding: 0 !important;
+ border-top: 0 !important;
+}