User management WIP
This commit is contained in:
parent
ac424134d0
commit
a4d0d15f65
@ -28,6 +28,8 @@ import { NgxChartsModule } from '@swimlane/ngx-charts';
|
||||
import { AdminDialogService } from './services/admin-dialog-service.service';
|
||||
import { SmtpConfigScreenComponent } from './screens/smtp-config/smtp-config-screen.component';
|
||||
import { SmtpAuthDialogComponent } from './dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
||||
import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
|
||||
import { ConfirmDeleteUserDialogComponent } from './dialogs/confirm-delete-user-dialog/confirm-delete-user-dialog.component';
|
||||
|
||||
const dialogs = [
|
||||
AddEditRuleSetDialogComponent,
|
||||
@ -35,7 +37,9 @@ const dialogs = [
|
||||
AddEditFileAttributeDialogComponent,
|
||||
ConfirmDeleteFileAttributeDialogComponent,
|
||||
EditColorDialogComponent,
|
||||
SmtpAuthDialogComponent
|
||||
SmtpAuthDialogComponent,
|
||||
AddEditUserDialogComponent,
|
||||
ConfirmDeleteUserDialogComponent
|
||||
];
|
||||
|
||||
const screens = [
|
||||
@ -49,7 +53,8 @@ const screens = [
|
||||
FileAttributesListingScreenComponent,
|
||||
LicenseInformationScreenComponent,
|
||||
UserListingScreenComponent,
|
||||
WatermarkScreenComponent
|
||||
WatermarkScreenComponent,
|
||||
SmtpConfigScreenComponent
|
||||
];
|
||||
|
||||
const components = [
|
||||
@ -58,7 +63,6 @@ const components = [
|
||||
TabsComponent,
|
||||
ComboChartComponent,
|
||||
ComboSeriesVerticalComponent,
|
||||
SmtpConfigScreenComponent,
|
||||
...dialogs,
|
||||
...screens
|
||||
];
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
<section class="dialog">
|
||||
<div class="dialog-header heading-l">
|
||||
{{ (user ? 'add-edit-user.title.edit' : 'add-edit-user.title.new') | translate }}
|
||||
</div>
|
||||
|
||||
<form (submit)="save()" [formGroup]="userForm">
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-user.form.first-name"></label>
|
||||
<input formControlName="firstName" name="firstName" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-user.form.last-name"></label>
|
||||
<input formControlName="lastName" name="lastName" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-user.form.email"></label>
|
||||
<input formControlName="email" name="email" type="email" />
|
||||
</div>
|
||||
|
||||
<!-- <div class="red-input-group required w-300">-->
|
||||
<!-- <label translate="add-edit-user.form.password"></label>-->
|
||||
<!-- <input formControlName="password" name="password" type="password" />-->
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="red-input-group">
|
||||
<label translate="add-edit-user.form.role"></label>
|
||||
<div class="roles-wrapper">
|
||||
<mat-checkbox
|
||||
*ngFor="let role of ROLES"
|
||||
color="primary"
|
||||
[checked]="getChecked(role)"
|
||||
[disabled]="getDisabled(role)"
|
||||
(change)="changeRoleValue(role, $event)"
|
||||
>
|
||||
{{ 'roles.' + role | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button [disabled]="userForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
||||
{{ (user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate }}
|
||||
</button>
|
||||
|
||||
<redaction-icon-button *ngIf="user" icon="red:trash" type="show-bg" (action)="delete()" text="add-edit-user.actions.delete"></redaction-icon-button>
|
||||
|
||||
<div class="all-caps-label cancel" mat-dialog-close translate="add-edit-user.actions.cancel"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
</section>
|
||||
@ -0,0 +1,7 @@
|
||||
.roles-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-row-gap: 8px;
|
||||
margin-top: 8px;
|
||||
width: 300px;
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { User, UserControllerService } from '@redaction/red-ui-http';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { AdminDialogService } from '../../services/admin-dialog-service.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-user-dialog',
|
||||
templateUrl: './add-edit-user-dialog.component.html',
|
||||
styleUrls: ['./add-edit-user-dialog.component.scss']
|
||||
})
|
||||
export class AddEditUserDialogComponent {
|
||||
public userForm: FormGroup;
|
||||
public ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _userControllerService: UserControllerService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
public dialogRef: MatDialogRef<AddEditUserDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public user: User
|
||||
) {
|
||||
this.userForm = this._formBuilder.group({
|
||||
firstName: [this.user?.firstName, Validators.required],
|
||||
lastName: [this.user?.lastName, Validators.required],
|
||||
email: [{ value: this.user?.email, disabled: !!user }, [Validators.required, Validators.email]],
|
||||
// password: [this.user?.password, Validators.required],
|
||||
admin: [user && this._userService.isAdmin(user)],
|
||||
roles: [user?.roles || []]
|
||||
});
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
if (!this.user) return true;
|
||||
|
||||
for (const key of Object.keys(this.userForm.getRawValue())) {
|
||||
if (this.user[key] !== this.userForm.get(key).value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async _updateProfile() {
|
||||
const profileKeys = ['firstName', 'lastName'];
|
||||
let profileChanged = false;
|
||||
for (const key of profileKeys) {
|
||||
if (this.userForm.get(key).value !== this.user[key]) {
|
||||
profileChanged = true;
|
||||
}
|
||||
}
|
||||
if (!profileChanged) {
|
||||
return;
|
||||
}
|
||||
await this._userControllerService.updateProfile(this.userForm.getRawValue()).toPromise();
|
||||
}
|
||||
|
||||
private async _updateRoles() {
|
||||
const newAdminValue = this.userForm.get('admin').value;
|
||||
if (newAdminValue === this._userService.isAdmin(this.user)) {
|
||||
return;
|
||||
}
|
||||
let roles = [...this.user.roles];
|
||||
if (newAdminValue) {
|
||||
roles.push('RED_ADMIN');
|
||||
} else {
|
||||
roles = roles.filter((role) => role !== 'RED_ADMIN');
|
||||
}
|
||||
await this._userControllerService.addRoleToUsers(roles, this.user.userId).toPromise();
|
||||
}
|
||||
|
||||
private async _update() {
|
||||
await this._updateProfile();
|
||||
await this._updateRoles();
|
||||
}
|
||||
|
||||
private async _create() {
|
||||
const roles = ['RED_USER'];
|
||||
if (this.userForm.get('admin').value) {
|
||||
roles.push('RED_ADMIN');
|
||||
}
|
||||
await this._userControllerService.createUser({ ...this.userForm.getRawValue(), roles: [] }, 'body').toPromise();
|
||||
}
|
||||
|
||||
public async save() {
|
||||
if (this.user) {
|
||||
await this._update();
|
||||
} else {
|
||||
await this._create();
|
||||
}
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
public async delete() {
|
||||
this._dialogService.openConfirmDeleteUserDialog(this.user);
|
||||
// this.dialogRef.close();
|
||||
}
|
||||
|
||||
public changeRoleValue(role: string, { checked }: MatCheckboxChange) {
|
||||
const roles = [...this.activeRoles];
|
||||
if (checked) {
|
||||
roles.push(role);
|
||||
} else {
|
||||
roles.splice(roles.indexOf(role), 1);
|
||||
}
|
||||
this.userForm.patchValue({ roles });
|
||||
}
|
||||
|
||||
public get activeRoles(): string[] {
|
||||
return this.userForm.get('roles').value;
|
||||
}
|
||||
|
||||
public getChecked(role: string) {
|
||||
return this.activeRoles.indexOf(role) !== -1;
|
||||
}
|
||||
|
||||
public getDisabled(role: string) {
|
||||
return this.getChecked(role) && role === 'RED_USER' && this.getChecked('RED_ADMIN');
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,10 @@
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
mat-checkbox:not(:last-of-type) {
|
||||
margin-bottom: 6px;
|
||||
mat-checkbox {
|
||||
width: 100%;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
<section class="dialog">
|
||||
<div class="dialog-header heading-l" translate="confirm-delete-user.title"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="heading" translate="confirm-delete-user.warning"></div>
|
||||
|
||||
<mat-checkbox *ngFor="let checkbox of checkboxes; let idx = index" [(ngModel)]="checkbox.value" color="primary">
|
||||
{{ 'confirm-delete-user.checkbox-' + (idx + 1) | translate: { count: 20 } }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button (click)="deleteUser()">
|
||||
{{ 'confirm-delete-user.delete' | translate }}
|
||||
</button>
|
||||
<div class="all-caps-label cancel" (click)="cancel()" translate="confirm-delete-user.cancel"></div>
|
||||
</div>
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
</section>
|
||||
@ -0,0 +1,17 @@
|
||||
@import '../../../../../assets/styles/red-variables';
|
||||
|
||||
.dialog-header {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.heading {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
mat-checkbox {
|
||||
width: 100%;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { User, UserControllerService } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-confirm-delete-user-dialog',
|
||||
templateUrl: './confirm-delete-user-dialog.component.html',
|
||||
styleUrls: ['./confirm-delete-user-dialog.component.scss']
|
||||
})
|
||||
export class ConfirmDeleteUserDialogComponent implements OnInit {
|
||||
public checkboxes = [{ value: false }, { value: false }];
|
||||
public showToast = false;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public user: User,
|
||||
private readonly _userControllerService: UserControllerService,
|
||||
public dialogRef: MatDialogRef<ConfirmDeleteUserDialogComponent>
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
async deleteUser() {
|
||||
if (this.valid) {
|
||||
await this._userControllerService.deleteUser(this.user.userId).toPromise();
|
||||
this.dialogRef.close(true);
|
||||
} else {
|
||||
this.showToast = true;
|
||||
}
|
||||
}
|
||||
|
||||
public get valid() {
|
||||
return this.checkboxes[0].value && this.checkboxes[1].value;
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@ -2,9 +2,15 @@
|
||||
<div class="page-header">
|
||||
<redaction-admin-breadcrumbs class="flex-1" [root]="true"></redaction-admin-breadcrumbs>
|
||||
|
||||
<redaction-search-input [form]="searchForm" [placeholder]="'user-listing.search'"></redaction-search-input>
|
||||
|
||||
<div class="actions">
|
||||
<redaction-search-input [form]="searchForm" [placeholder]="'user-listing.search'"></redaction-search-input>
|
||||
<redaction-icon-button
|
||||
(action)="openAddEditUserDialog($event)"
|
||||
*ngIf="permissionsService.isManager()"
|
||||
icon="red:plus"
|
||||
text="user-listing.add-new"
|
||||
type="primary"
|
||||
></redaction-icon-button>
|
||||
<redaction-circle-button
|
||||
class="ml-6"
|
||||
*ngIf="permissionsService.isUser()"
|
||||
@ -29,6 +35,11 @@
|
||||
|
||||
<redaction-table-col-name label="user-listing.table-col-names.email"></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="user-listing.table-col-names.active"></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="user-listing.table-col-names.roles"></redaction-table-col-name>
|
||||
|
||||
<div></div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
|
||||
@ -36,9 +47,32 @@
|
||||
<!-- Table lines -->
|
||||
<div class="table-item" *cdkVirtualFor="let user of displayedUsers">
|
||||
<div>
|
||||
<redaction-initials-avatar [userId]="user.userId" [withName]="true" size="large"></redaction-initials-avatar>
|
||||
<redaction-initials-avatar [user]="user" [withName]="true" [showYou]="true" [alwaysShowName]="true"></redaction-initials-avatar>
|
||||
</div>
|
||||
<div class="small-label">{{ user.email || '-' }}</div>
|
||||
<div>
|
||||
<mat-slide-toggle [checked]="userService.isActive(user)" color="primary" (toggleChange)="changeActive(user)"></mat-slide-toggle>
|
||||
</div>
|
||||
<div class="small-label">{{ getDisplayRoles(user) }}</div>
|
||||
<div class="actions-container">
|
||||
<div class="action-buttons">
|
||||
<redaction-circle-button
|
||||
(action)="openAddEditUserDialog($event, user)"
|
||||
tooltip="user-listing.action.edit"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
<redaction-circle-button
|
||||
(action)="openDeleteUserDialog($event, user)"
|
||||
tooltip="user-listing.action.delete"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
[disabled]="user.userId === userService.userId"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
<div>{{ user.email || '-' }}</div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
@ -47,3 +81,5 @@
|
||||
<div class="right-container"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
||||
|
||||
@ -3,18 +3,22 @@
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: 1fr 1fr 11px;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr auto 11px;
|
||||
|
||||
.table-item {
|
||||
> div {
|
||||
> div:not(.scrollbar-placeholder) {
|
||||
padding: 0 24px;
|
||||
|
||||
&.center {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover {
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,6 +31,13 @@
|
||||
}
|
||||
|
||||
.page-header .actions {
|
||||
width: calc(353px - 24px);
|
||||
justify-content: flex-end;
|
||||
|
||||
redaction-search-input:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
redaction-icon-button:not(:last-child) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,33 +1,75 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { User } from '@redaction/red-ui-http';
|
||||
import { User, UserControllerService } from '@redaction/red-ui-http';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-listing-screen',
|
||||
templateUrl: './user-listing-screen.component.html',
|
||||
styleUrls: ['./user-listing-screen.component.scss']
|
||||
})
|
||||
export class UserListingScreenComponent {
|
||||
export class UserListingScreenComponent implements OnInit {
|
||||
public viewReady = false;
|
||||
|
||||
public users: User[];
|
||||
public displayedUsers: User[];
|
||||
public displayedUsers: User[] = [];
|
||||
public searchForm: FormGroup;
|
||||
|
||||
constructor(public readonly permissionsService: PermissionsService, private readonly userService: UserService, private readonly _formBuilder: FormBuilder) {
|
||||
this.users = this.userService.allUsers;
|
||||
this.displayedUsers = [...this.users];
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly userService: UserService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _adminDialogService: AdminDialogService,
|
||||
private readonly _userControllerService: UserControllerService
|
||||
) {
|
||||
this.searchForm = this._formBuilder.group({
|
||||
query: ['']
|
||||
});
|
||||
|
||||
this.searchForm.valueChanges.subscribe((value) => this._executeSearch(value));
|
||||
this.searchForm.valueChanges.subscribe(() => this._executeSearch());
|
||||
}
|
||||
|
||||
public async ngOnInit() {
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
@debounce(200)
|
||||
private _executeSearch(value: { query: string }) {
|
||||
this.displayedUsers = this.users.filter((user) => this.userService.getName(user).toLowerCase().includes(value.query.toLowerCase()));
|
||||
private _executeSearch() {
|
||||
const value = this.searchForm.get('query').value;
|
||||
this.displayedUsers = this.users.filter((user) => this.userService.getName(user).toLowerCase().includes(value.toLowerCase()));
|
||||
}
|
||||
|
||||
public openAddEditUserDialog($event: MouseEvent, user?: User) {
|
||||
$event.stopPropagation();
|
||||
this._adminDialogService.openAddEditUserDialog(user, async () => {
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteUserDialog($event: MouseEvent, user: User) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
this.viewReady = false;
|
||||
this.users = (await this._userControllerService.getAllUsers({}).toPromise()).users;
|
||||
this._executeSearch();
|
||||
this.viewReady = true;
|
||||
}
|
||||
|
||||
public getDisplayRoles(user: User) {
|
||||
return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE');
|
||||
}
|
||||
|
||||
public async changeActive(user: User) {
|
||||
this.viewReady = false;
|
||||
user.roles = this.userService.isActive(user) ? [] : ['RED_USER'];
|
||||
await this._userControllerService.addRoleToUsers(user.roles, user.userId).toPromise();
|
||||
await this._loadData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,8 @@ import {
|
||||
RuleSetControllerService,
|
||||
RuleSetModel,
|
||||
SMTPConfigurationModel,
|
||||
TypeValue
|
||||
TypeValue,
|
||||
User
|
||||
} from '@redaction/red-ui-http';
|
||||
import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
|
||||
import { AddEditDictionaryDialogComponent } from '../dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
|
||||
@ -21,6 +22,8 @@ import { ConfirmDeleteFileAttributeDialogComponent } from '../dialogs/confirm-de
|
||||
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
||||
import { AddEditUserDialogComponent } from '../dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
|
||||
import { ConfirmDeleteUserDialogComponent } from '../dialogs/confirm-delete-user-dialog/confirm-delete-user-dialog.component';
|
||||
|
||||
const dialogConfig = {
|
||||
width: '662px',
|
||||
@ -168,4 +171,36 @@ export class AdminDialogService {
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAddEditUserDialog(user?: User, cb?: Function): MatDialogRef<AddEditUserDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditUserDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: user,
|
||||
autoFocus: true
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe((result) => {
|
||||
if (result && cb) {
|
||||
cb(result);
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openConfirmDeleteUserDialog(user?: User, cb?: Function): MatDialogRef<ConfirmDeleteUserDialogComponent> {
|
||||
const ref = this._dialog.open(ConfirmDeleteUserDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: user,
|
||||
autoFocus: true
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe((result) => {
|
||||
if (result && cb) {
|
||||
cb(result);
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div [className]="colorClass + ' oval ' + size + (hasBorder ? ' border' : '')" [matTooltipPosition]="'above'" [matTooltip]="displayName">
|
||||
{{ initials }}
|
||||
</div>
|
||||
<div *ngIf="withName" class="clamp-2 username">
|
||||
<div *ngIf="withName" class="clamp-2 username" [class.always-visible]="alwaysShowName" [class.disabled]="disabled">
|
||||
{{ displayName || ('initials-avatar.unassigned' | translate) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -7,6 +7,10 @@
|
||||
|
||||
.username {
|
||||
margin-left: 6px;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { User } from '@redaction/red-ui-http';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-initials-avatar',
|
||||
@ -9,11 +10,12 @@ import { User } from '@redaction/red-ui-http';
|
||||
})
|
||||
export class InitialsAvatarComponent implements OnInit, OnChanges {
|
||||
@Input() public userId: string;
|
||||
@Input() public user: User;
|
||||
@Input() public color = 'lightgray';
|
||||
@Input() public size: 'small' | 'large' = 'small';
|
||||
@Input() public withName = false;
|
||||
|
||||
public _user: User;
|
||||
@Input() public showYou = false;
|
||||
@Input() public alwaysShowName = false;
|
||||
|
||||
public displayName: string;
|
||||
|
||||
@ -21,17 +23,23 @@ export class InitialsAvatarComponent implements OnInit, OnChanges {
|
||||
|
||||
public colorClass: string;
|
||||
|
||||
constructor(private readonly _userService: UserService) {}
|
||||
constructor(private readonly _userService: UserService, private readonly _translateService: TranslateService) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
ngOnChanges(): void {
|
||||
const isSystemUser = this.userId && this.userId.toLowerCase() === 'system';
|
||||
if (!isSystemUser) {
|
||||
this._user = this._userService.getUserById(this.userId);
|
||||
this.displayName = this._userService.getName(this._user);
|
||||
if (this.user) {
|
||||
this.userId = this.user.userId;
|
||||
} else {
|
||||
this.user = this._userService.getUserById(this.userId);
|
||||
}
|
||||
this.displayName =
|
||||
this._userService.getName(this.user) +
|
||||
(this.showYou && this._userService.userId === this.userId ? ` (${this._translateService.instant('initials-avatar.you')})` : '');
|
||||
this.initials = this._getInitials();
|
||||
this.colorClass = this._colorClass();
|
||||
this.colorClass = this._colorClass;
|
||||
} else {
|
||||
this.displayName = 'System';
|
||||
this.initials = 'SY';
|
||||
@ -40,11 +48,11 @@ export class InitialsAvatarComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
private _getInitials() {
|
||||
if (!this._user) {
|
||||
if (!this.user) {
|
||||
return '?';
|
||||
} else {
|
||||
return this._userService
|
||||
.getName(this._user)
|
||||
.getName(this.user)
|
||||
.split(' ')
|
||||
.filter((value) => value !== ' ')
|
||||
.filter((value, idx) => idx < 2)
|
||||
@ -53,10 +61,13 @@ export class InitialsAvatarComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
private _colorClass() {
|
||||
private get _colorClass() {
|
||||
if (this._userService.userId === this.userId) {
|
||||
return 'red-white';
|
||||
}
|
||||
if (this.disabled) {
|
||||
return 'inactive';
|
||||
}
|
||||
if (this.color.includes('-')) {
|
||||
return this.color;
|
||||
}
|
||||
@ -64,6 +75,10 @@ export class InitialsAvatarComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
public get hasBorder(): boolean {
|
||||
return !!this._user && this._userService.userId !== this.userId && this._userService.isManager(this._user);
|
||||
return !!this.user && this._userService.userId !== this.userId && this._userService.isManager(this.user);
|
||||
}
|
||||
|
||||
public get disabled(): boolean {
|
||||
return !this._userService.isActive(this.user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export enum NotificationType {
|
||||
INFO = 'INFO'
|
||||
}
|
||||
|
||||
export class Action {
|
||||
export class ToastAction {
|
||||
title: string;
|
||||
action?: Function;
|
||||
}
|
||||
@ -31,7 +31,7 @@ export class NotificationService {
|
||||
message: string,
|
||||
title?: string,
|
||||
notificationType: NotificationType = NotificationType.INFO,
|
||||
options?: Partial<IndividualConfig> & { actions?: Action[] }
|
||||
options?: Partial<IndividualConfig> & { actions?: ToastAction[] }
|
||||
): ActiveToast<any> {
|
||||
switch (notificationType) {
|
||||
case NotificationType.ERROR:
|
||||
|
||||
@ -114,6 +114,20 @@ export class UserService {
|
||||
return user.roles?.indexOf('RED_USER') >= 0;
|
||||
}
|
||||
|
||||
isAdmin(user?: User): boolean {
|
||||
if (!user) {
|
||||
user = this.user;
|
||||
}
|
||||
return user.roles?.indexOf('RED_ADMIN') >= 0;
|
||||
}
|
||||
|
||||
isActive(user?: User): boolean {
|
||||
if (!user) {
|
||||
user = this.user;
|
||||
}
|
||||
return user.roles?.length > 0;
|
||||
}
|
||||
|
||||
private _hasAnyRedRole(u: User) {
|
||||
return u.roles.indexOf('RED_USER') >= 0 || u.roles.indexOf('RED_MANAGER') >= 0 || u.roles.indexOf('RED_ADMIN') >= 0;
|
||||
}
|
||||
|
||||
@ -23,7 +23,6 @@ import { tap } from 'rxjs/operators';
|
||||
import { humanize } from '../utils/functions';
|
||||
import { FileStatusWrapper } from '../models/file/file-status.wrapper';
|
||||
import { ProjectWrapper } from './model/project.wrapper';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
export interface AppState {
|
||||
projects: ProjectWrapper[];
|
||||
|
||||
@ -417,7 +417,8 @@
|
||||
"show": "Show"
|
||||
},
|
||||
"initials-avatar": {
|
||||
"unassigned": "Unassigned"
|
||||
"unassigned": "Unassigned",
|
||||
"you": "You"
|
||||
},
|
||||
"assign-file-owner": {
|
||||
"dialog": {
|
||||
@ -756,6 +757,14 @@
|
||||
"checkbox-1": "All documents it is used on will be impacted",
|
||||
"checkbox-2": "All inputted details on the documents will be lost"
|
||||
},
|
||||
"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",
|
||||
"delete": "Delete User",
|
||||
"cancel": "Keep User"
|
||||
},
|
||||
"document-info": {
|
||||
"title": "Introduce File Attributes",
|
||||
"save": "Save Document Info",
|
||||
@ -767,9 +776,34 @@
|
||||
},
|
||||
"table-col-names": {
|
||||
"name": "Name",
|
||||
"email": "Email"
|
||||
"email": "Email",
|
||||
"active": "Active",
|
||||
"roles": "Roles"
|
||||
},
|
||||
"search": "Search..."
|
||||
"action": {
|
||||
"edit": "Edit User",
|
||||
"delete": "Delete User"
|
||||
},
|
||||
"search": "Search...",
|
||||
"add-new": "New User"
|
||||
},
|
||||
"add-edit-user": {
|
||||
"title": {
|
||||
"new": "Add New User",
|
||||
"edit": "Edit User"
|
||||
},
|
||||
"form": {
|
||||
"first-name": "First Name",
|
||||
"last-name": "Last Name",
|
||||
"email": "Email",
|
||||
"role": "Role"
|
||||
},
|
||||
"actions": {
|
||||
"save": "Save User",
|
||||
"save-changes": "Save Changes",
|
||||
"delete": "Delete User",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
},
|
||||
"rules-screen": {
|
||||
"error": {
|
||||
@ -1015,5 +1049,12 @@
|
||||
"stream-download": {
|
||||
"abort": "You have an active download, closing this window will cause it to stop!",
|
||||
"error": "Failed to download {{filename}}. Please try again ..."
|
||||
},
|
||||
"roles": {
|
||||
"RED_USER": "User",
|
||||
"RED_MANAGER": "Manager",
|
||||
"RED_USER_ADMIN": "Users Admin",
|
||||
"RED_ADMIN": "Application Admin",
|
||||
"NO_ROLE": "No role defined"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
.mat-button,
|
||||
.mat-flat-button {
|
||||
font-family: Inter, sans-serif !important;
|
||||
border-radius: 17px !important;
|
||||
font-size: 13px !important;
|
||||
height: 34px;
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
}
|
||||
|
||||
.mat-checkbox-label {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
color: $accent;
|
||||
|
||||
|
||||
@ -58,6 +58,11 @@
|
||||
&.white-dark {
|
||||
border: 1px solid $grey-4;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
background-color: $grey-6;
|
||||
color: $grey-7;
|
||||
}
|
||||
}
|
||||
|
||||
.oval {
|
||||
|
||||
@ -125,6 +125,11 @@ form {
|
||||
border-color: $red-1;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: $grey-2;
|
||||
color: rgba($grey-1, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.hex-color-input {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
@import 'red-variables';
|
||||
|
||||
.mat-list-item {
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1 !important;
|
||||
font-size: 13px !important;
|
||||
line-height: 16px !important;
|
||||
|
||||
@ -55,6 +55,12 @@ $gn-next-mat-theme: mat-light-theme(
|
||||
|
||||
@include angular-material-theme($gn-next-mat-theme);
|
||||
|
||||
$custom-typography: mat-typography-config(
|
||||
$font-family: 'Inter, sans-serif'
|
||||
);
|
||||
|
||||
@include angular-material-typography($custom-typography);
|
||||
|
||||
.mat-flat-button {
|
||||
min-width: unset !important;
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
}
|
||||
|
||||
.mat-menu-item {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
color: $accent;
|
||||
padding: 0 8px;
|
||||
|
||||
@ -121,7 +121,7 @@ body {
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1600px) {
|
||||
redaction-initials-avatar .username {
|
||||
redaction-initials-avatar .username:not(.always-visible) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
@import 'red-variables';
|
||||
|
||||
.mat-select-panel .mat-option {
|
||||
font-family: Inter, sans-serif;
|
||||
|
||||
&:hover:not(.mat-option-disabled),
|
||||
&:focus:not(.mat-option-disabled) {
|
||||
background-color: $grey-6;
|
||||
|
||||
@ -26,10 +26,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mat-slide-toggle-content {
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
|
||||
&.mat-primary.mat-checked {
|
||||
.mat-slide-toggle-bar {
|
||||
background-color: $primary;
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
border-radius: 3px !important;
|
||||
padding: 10px;
|
||||
margin: 12px !important;
|
||||
font-family: Inter, sans-serif;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
color: $white !important;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user