+
diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss
index dc3ae3d31..89360c074 100644
--- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss
+++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.scss
@@ -28,6 +28,15 @@
display: flex;
width: 353px;
min-width: 353px;
+ padding: 16px 16px 16px 24px;
+
+ &.has-scrollbar:hover {
+ padding-right: 5px;
+ }
+
+ redaction-users-stats {
+ width: 100%;
+ }
}
.page-header .actions {
diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts
index dd09f094d..d57f90fa6 100644
--- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts
+++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts
@@ -6,6 +6,8 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { debounce } from '../../../../utils/debounce';
import { AdminDialogService } from '../../services/admin-dialog-service.service';
import { TranslateService } from '@ngx-translate/core';
+import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
+import { TranslateChartService } from '../../../../services/translate-chart.service';
@Component({
selector: 'redaction-user-listing-screen',
@@ -14,7 +16,8 @@ import { TranslateService } from '@ngx-translate/core';
})
export class UserListingScreenComponent implements OnInit {
public viewReady = false;
-
+ public collapsedDetails = false;
+ public chartData: DoughnutChartConfig[];
public users: User[];
public displayedUsers: User[] = [];
public searchForm: FormGroup;
@@ -25,7 +28,8 @@ export class UserListingScreenComponent implements OnInit {
private readonly _formBuilder: FormBuilder,
private readonly _translateService: TranslateService,
private readonly _adminDialogService: AdminDialogService,
- private readonly _userControllerService: UserControllerService
+ private readonly _userControllerService: UserControllerService,
+ private readonly _translateChartService: TranslateChartService
) {
this.searchForm = this._formBuilder.group({
query: ['']
@@ -46,10 +50,16 @@ export class UserListingScreenComponent implements OnInit {
public openAddEditUserDialog($event: MouseEvent, user?: User) {
$event.stopPropagation();
- this._adminDialogService.openAddEditUserDialog(user, async (action) => {
- if (action === 'DELETE') {
+ this._adminDialogService.openAddEditUserDialog(user, async (result) => {
+ if (result === 'DELETE') {
this.openDeleteUserDialog(user);
} else {
+ this.viewReady = false;
+ if (result.action === 'CREATE') {
+ await this._userControllerService.createUser(result.user).toPromise();
+ } else if (result.action === 'UPDATE') {
+ await this._userControllerService.updateProfile(result.user).toPromise();
+ }
await this._loadData();
}
});
@@ -58,6 +68,8 @@ export class UserListingScreenComponent implements OnInit {
public openDeleteUserDialog(user: User, $event?: MouseEvent) {
$event?.stopPropagation();
this._adminDialogService.openConfirmDeleteUserDialog(user, async () => {
+ this.viewReady = false;
+ await this._userControllerService.deleteUser(user.userId).toPromise();
await this._loadData();
});
}
@@ -66,9 +78,47 @@ export class UserListingScreenComponent implements OnInit {
this.viewReady = false;
this.users = (await this._userControllerService.getAllUsers({}).toPromise()).users;
this._executeSearch();
+ this._computeStats();
this.viewReady = true;
}
+ private _computeStats() {
+ this.chartData = this._translateChartService.translateRoles(
+ [
+ {
+ value: this.users.filter((user) => !this.userService.isActive(user)).length,
+ color: 'INACTIVE',
+ label: 'INACTIVE'
+ },
+ {
+ value: this.users.filter((user) => user.roles.length === 1 && user.roles[0] === 'RED_USER').length,
+ color: 'REGULAR',
+ label: 'REGULAR'
+ },
+ {
+ value: this.users.filter((user) => this.userService.isManager(user) && !this.userService.isAdmin(user)).length,
+ color: 'MANAGER',
+ label: 'RED_MANAGER'
+ },
+ {
+ value: this.users.filter((user) => this.userService.isManager(user) && this.userService.isAdmin(user)).length,
+ color: 'MANAGER_ADMIN',
+ label: 'MANAGER_ADMIN'
+ },
+ {
+ value: this.users.filter((user) => this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)).length,
+ color: 'USER_ADMIN',
+ label: 'RED_USER_ADMIN'
+ },
+ {
+ value: this.users.filter((user) => this.userService.isAdmin(user) && !this.userService.isManager(user)).length,
+ color: 'ADMIN',
+ label: 'RED_ADMIN'
+ }
+ ].filter((type) => type.value > 0)
+ );
+ }
+
public getDisplayRoles(user: User) {
return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE');
}
@@ -79,4 +129,8 @@ export class UserListingScreenComponent implements OnInit {
await this._userControllerService.addRoleToUsers(user.roles, user.userId).toPromise();
await this._loadData();
}
+
+ public toggleCollapsedDetails() {
+ this.collapsedDetails = !this.collapsedDetails;
+ }
}
diff --git a/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.html b/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.html
index 6c27d7bc5..f92abf6a9 100644
--- a/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.html
+++ b/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.html
@@ -1,5 +1,5 @@
-
+
*:not(:last-child) {
- margin-right: 20px;
+ margin-right: 16px;
}
}
@@ -27,7 +27,6 @@
.text-container {
position: absolute;
- top: 0;
display: flex;
flex-direction: column;
justify-content: center;
diff --git a/apps/red-ui/src/app/services/translate-chart.service.ts b/apps/red-ui/src/app/services/translate-chart.service.ts
index 7e2b0f502..d1ea1582f 100644
--- a/apps/red-ui/src/app/services/translate-chart.service.ts
+++ b/apps/red-ui/src/app/services/translate-chart.service.ts
@@ -11,4 +11,8 @@ export class TranslateChartService {
public translateStatus(config: DoughnutChartConfig[]): DoughnutChartConfig[] {
return config.map((val) => ({ ...val, label: this._translateService.instant(val.label) }));
}
+
+ public translateRoles(config: DoughnutChartConfig[]): DoughnutChartConfig[] {
+ return config.map((val) => ({ ...val, label: this._translateService.instant(`roles.${val.label}`).toLowerCase() }));
+ }
}
diff --git a/apps/red-ui/src/app/services/user.service.ts b/apps/red-ui/src/app/services/user.service.ts
index fa07b9c20..bb174b215 100644
--- a/apps/red-ui/src/app/services/user.service.ts
+++ b/apps/red-ui/src/app/services/user.service.ts
@@ -114,6 +114,13 @@ export class UserService {
return user.roles?.indexOf('RED_USER') >= 0;
}
+ isUserAdmin(user?: User): boolean {
+ if (!user) {
+ user = this.user;
+ }
+ return user.roles?.indexOf('RED_USER_ADMIN') >= 0;
+ }
+
isAdmin(user?: User): boolean {
if (!user) {
user = this.user;
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index 4a312b587..b01f4e634 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -761,8 +761,8 @@
"confirm-delete-user": {
"title": "Delete User from Workspace",
"warning": "Warning: this cannot be undone!",
- "checkbox-1": "{{count}} projects will be impacted",
- "checkbox-2": "{{count}} documents waiting review will be impacted",
+ "checkbox-1": "{{projectsCount}} projects will be impacted",
+ "checkbox-2": "All documents waiting review from the user will be impacted",
"delete": "Delete User",
"cancel": "Keep User",
"toast-error": "Please confirm that you understand the ramifications of your action!"
@@ -807,6 +807,14 @@
"cancel": "Cancel"
}
},
+ "user-stats": {
+ "title": "Users",
+ "chart": {
+ "users": "Users in Workspace"
+ },
+ "expand": "Show Details",
+ "collapse": "Hide Details"
+ },
"rules-screen": {
"error": {
"generic": "Something went wrong... Rules update failed!"
@@ -1057,6 +1065,9 @@
"RED_MANAGER": "Manager",
"RED_USER_ADMIN": "Users Admin",
"RED_ADMIN": "Application Admin",
- "NO_ROLE": "No role defined"
+ "NO_ROLE": "No role defined",
+ "INACTIVE": "Inactive",
+ "MANAGER_ADMIN": "Manager & Admin",
+ "REGULAR": "Regular"
}
}
diff --git a/apps/red-ui/src/assets/styles/red-components.scss b/apps/red-ui/src/assets/styles/red-components.scss
index e0cef767b..ff2abf5c5 100644
--- a/apps/red-ui/src/assets/styles/red-components.scss
+++ b/apps/red-ui/src/assets/styles/red-components.scss
@@ -111,7 +111,8 @@
background-color: $grey-3;
}
-.UNDER_REVIEW {
+.UNDER_REVIEW,
+.REGULAR {
stroke: $yellow-1;
background-color: $yellow-1;
}
@@ -121,7 +122,8 @@
background-color: $blue-4;
}
-.APPROVED {
+.APPROVED,
+.ADMIN {
stroke: $blue-3;
background-color: $blue-3;
}
@@ -131,7 +133,8 @@
background-color: $grey-1;
}
-.OCR_PROCESSING {
+.OCR_PROCESSING,
+.USER_ADMIN {
stroke: $green-2;
background-color: $green-2;
}
@@ -161,6 +164,17 @@
background-color: rgba(#0389ec, 0.1);
}
+.INACTIVE {
+ stroke: $grey-5;
+ background-color: $grey-5;
+}
+
+.MANAGER,
+.MANAGER_ADMIN {
+ stroke: $red-1;
+ background-color: $red-1;
+}
+
.select-oval {
width: 20px;
height: 20px;