User management WIP

This commit is contained in:
Adina Țeudan 2021-04-06 01:17:56 +03:00
parent ac424134d0
commit a4d0d15f65
30 changed files with 525 additions and 54 deletions

View File

@ -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
];

View File

@ -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>

View File

@ -0,0 +1,7 @@
.roles-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
grid-row-gap: 8px;
margin-top: 8px;
width: 300px;
}

View File

@ -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');
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -7,6 +7,10 @@
.username {
margin-left: 6px;
&.disabled {
opacity: 0.7;
}
}
}

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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[];

View File

@ -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"
}
}

View File

@ -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;

View File

@ -18,7 +18,6 @@
}
.mat-checkbox-label {
font-family: 'Inter', sans-serif;
font-size: 13px;
color: $accent;

View File

@ -58,6 +58,11 @@
&.white-dark {
border: 1px solid $grey-4;
}
&.inactive {
background-color: $grey-6;
color: $grey-7;
}
}
.oval {

View File

@ -125,6 +125,11 @@ form {
border-color: $red-1;
}
}
&:disabled {
background-color: $grey-2;
color: rgba($grey-1, 0.3);
}
}
.hex-color-input {

View File

@ -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;

View File

@ -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;
}

View File

@ -13,7 +13,6 @@
}
.mat-menu-item {
font-family: 'Inter', sans-serif;
font-size: 13px;
color: $accent;
padding: 0 8px;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;