Refactor buttons

This commit is contained in:
Adina Țeudan 2020-11-23 22:29:26 +02:00
parent 732a961431
commit 794716c317
43 changed files with 621 additions and 498 deletions

View File

@ -79,6 +79,10 @@ import { DictionaryAnnotationIconComponent } from './components/dictionary-annot
import { BulkActionsComponent } from './screens/project-overview-screen/bulk-actions/bulk-actions.component'; import { BulkActionsComponent } from './screens/project-overview-screen/bulk-actions/bulk-actions.component';
import { HttpCacheInterceptor } from '@redaction/red-cache'; import { HttpCacheInterceptor } from '@redaction/red-cache';
import { HiddenActionComponent } from './common/hidden-action/hidden-action.component'; import { HiddenActionComponent } from './common/hidden-action/hidden-action.component';
import { IconButtonComponent } from './components/buttons/icon-button/icon-button.component';
import { UserButtonComponent } from './components/buttons/user-button/user-button.component';
import { CircleButtonComponent } from './components/buttons/circle-button/circle-button.component';
import { ChevronButtonComponent } from './components/buttons/chevron-button/chevron-button.component';
export function HttpLoaderFactory(httpClient: HttpClient) { export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -125,7 +129,11 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
DictionaryAnnotationIconComponent, DictionaryAnnotationIconComponent,
BulkActionsComponent, BulkActionsComponent,
FileActionsComponent, FileActionsComponent,
HiddenActionComponent HiddenActionComponent,
IconButtonComponent,
UserButtonComponent,
CircleButtonComponent,
ChevronButtonComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -1,107 +1,102 @@
<div class="file-actions"> <div class="file-actions">
<button <redaction-circle-button
(click)="reanalyseFile($event, fileStatus)" (action)="reanalyseFile($event, fileStatus)"
*ngIf="permissionsService.canReanalyseFile(fileStatus) && screen === 'file-preview'" *ngIf="permissionsService.canReanalyseFile(fileStatus) && screen === 'file-preview'"
[matTooltip]="'file-preview.reanalyse-notification' | translate" tooltip="file-preview.reanalyse-notification"
class="warn" type="warn"
mat-icon-button tooltipClass="warn small"
matTooltipClass="warn small" icon="red:refresh"
> >
<mat-icon svgIcon="red:refresh"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="reanalyseFile($event, fileStatus)" (action)="reanalyseFile($event, fileStatus)"
*ngIf="permissionsService.canReanalyseFile(fileStatus) && screen !== 'file-preview'" *ngIf="permissionsService.canReanalyseFile(fileStatus) && screen === 'project-overview'"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.reanalyse.action' | translate" tooltip="project-overview.reanalyse.action"
color="accent" icon="red:refresh"
mat-icon-button type="dark-bg"
> >
<mat-icon svgIcon="red:refresh"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="assignReviewer($event, fileStatus)" (action)="openDeleteFileDialog($event, fileStatus)"
*ngIf="permissionsService.canAssignReviewer(fileStatus) && screen !== 'file-preview'"
[matTooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.assign.action' | translate"
color="accent"
mat-icon-button
>
<mat-icon svgIcon="red:assign" *ngIf="permissionsService.isManagerAndOwner()"></mat-icon>
<mat-icon svgIcon="red:assign-me" *ngIf="!permissionsService.isManagerAndOwner()"></mat-icon>
</button>
<button
(click)="openDeleteFileDialog($event, fileStatus)"
*ngIf="permissionsService.canDeleteFile(fileStatus)" *ngIf="permissionsService.canDeleteFile(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.delete.action' | translate" tooltip="project-overview.delete.action"
color="accent" icon="red:trash"
mat-icon-button [type]="buttonType"
> >
<mat-icon svgIcon="red:trash"></mat-icon> </redaction-circle-button>
</button>
<div <redaction-circle-button
(action)="downloadFileRedactionReport($event, fileStatus)"
*ngIf="permissionsService.canShowRedactionReportDownloadBtn(fileStatus)" *ngIf="permissionsService.canShowRedactionReportDownloadBtn(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="(fileStatus.isApproved ? 'report.action' : 'report.unavailable-single') | translate" [tooltip]="fileStatus.isApproved ? 'report.action' : 'report.unavailable-single'"
[disabled]="!fileStatus.isApproved"
icon="red:report"
[type]="buttonType"
> >
<button (click)="downloadFileRedactionReport($event, fileStatus)" [disabled]="!fileStatus.isApproved" color="accent" mat-icon-button> </redaction-circle-button>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
</div>
<button <redaction-circle-button
(click)="downloadFile($event, fileStatus)" (action)="assignReviewer($event, fileStatus)"
*ngIf="permissionsService.canAssignReviewer(fileStatus)"
[tooltipPosition]="tooltipPosition"
tooltip="project-overview.assign.action"
[icon]="permissionsService.isManagerAndOwner() ? 'red:assign' : 'red-assign-me'"
[type]="buttonType"
>
</redaction-circle-button>
<redaction-circle-button
(action)="downloadFile($event, fileStatus)"
*ngIf="permissionsService.canDownloadRedactedFile(fileStatus)" *ngIf="permissionsService.canDownloadRedactedFile(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.download-redacted-file' | translate" tooltip="project-overview.download-redacted-file"
color="accent" icon="red:download"
mat-icon-button [type]="buttonType"
> >
<mat-icon svgIcon="red:download"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="setFileApproved($event, fileStatus)" (action)="setFileApproved($event, fileStatus)"
*ngIf="permissionsService.canApprove(fileStatus)" *ngIf="permissionsService.canApprove(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.approve' | translate" tooltip="project-overview.approve"
color="accent" icon="red:check-alt"
mat-icon-button [type]="buttonType"
> >
<mat-icon svgIcon="red:check-alt"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="setFileUnderApproval($event, fileStatus)" (action)="setFileUnderApproval($event, fileStatus)"
*ngIf="permissionsService.canSetUnderApproval(fileStatus)" *ngIf="permissionsService.canSetUnderApproval(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.under-approval' | translate" tooltip="project-overview.under-approval"
color="accent" icon="red:check-alt"
mat-icon-button [type]="buttonType"
> >
<mat-icon svgIcon="red:check-alt"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="setFileUnderApproval($event, fileStatus)" (action)="setFileUnderApproval($event, fileStatus)"
*ngIf="permissionsService.canUndoApproval(fileStatus)" *ngIf="permissionsService.canUndoApproval(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.under-approval' | translate" tooltip="project-overview.under-approval"
color="accent" icon="red:undo"
mat-icon-button [type]="buttonType"
> >
<mat-icon svgIcon="red:undo"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="setFileUnderReview($event, fileStatus)" (action)="setFileUnderReview($event, fileStatus)"
*ngIf="permissionsService.canUndoUnderApproval(fileStatus)" *ngIf="permissionsService.canUndoUnderApproval(fileStatus)"
[matTooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[matTooltip]="'project-overview.under-review' | translate" tooltip="project-overview.under-review"
color="accent" icon="red:undo"
mat-icon-button [type]="buttonType"
> >
<mat-icon svgIcon="red:undo"></mat-icon> </redaction-circle-button>
</button>
</div> </div>

View File

@ -1,5 +1,6 @@
.file-actions { .file-actions {
display: flex; display: flex;
gap: 2px;
} }
.reviewer { .reviewer {

View File

@ -44,6 +44,10 @@ export class FileActionsComponent implements OnInit {
return this.screen === 'file-preview' ? 'below' : 'above'; return this.screen === 'file-preview' ? 'below' : 'above';
} }
public get buttonType() {
return this.screen === 'file-preview' ? 'default' : 'dark-bg';
}
openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) { openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) {
this._dialogService.openDeleteFilesDialog($event, fileStatusWrapper.projectId, [fileStatusWrapper.fileId], () => { this._dialogService.openDeleteFilesDialog($event, fileStatusWrapper.projectId, [fileStatusWrapper.fileId], () => {
this.actionPerformed.emit('delete'); this.actionPerformed.emit('delete');

View File

@ -1,10 +1,12 @@
<div class="filter-root"> <div class="filter-root">
<button color="accent" mat-button [class.arrow-button]="hasArrow" [matMenuTriggerFor]="filterMenu" [ngClass]="{ overlay: hasActiveFilters }"> <redaction-icon-button
<mat-icon [svgIcon]="icon" *ngIf="icon"></mat-icon> *ngIf="!chevron"
<span [translate]="filterLabel"></span> [text]="filterLabel"
<mat-icon svgIcon="red:arrow-down" *ngIf="hasArrow"></mat-icon> [icon]="icon"
</button> [matMenuTriggerFor]="filterMenu"
<div class="dot" *ngIf="hasActiveFilters"></div> [showDot]="hasActiveFilters"
></redaction-icon-button>
<redaction-chevron-button *ngIf="chevron" [text]="filterLabel" [matMenuTriggerFor]="filterMenu" [showDot]="hasActiveFilters"></redaction-chevron-button>
<mat-menu #filterMenu="matMenu" xPosition="before" (closed)="applyFilters()"> <mat-menu #filterMenu="matMenu" xPosition="before" (closed)="applyFilters()">
<div (mouseleave)="filterMouseLeave()" (mouseenter)="filterMouseEnter()"> <div (mouseleave)="filterMouseLeave()" (mouseenter)="filterMouseEnter()">
<div class="filter-menu-header"> <div class="filter-menu-header">

View File

@ -1,19 +1,5 @@
@import '../../../assets/styles/red-variables'; @import '../../../assets/styles/red-variables';
.filter-root {
position: relative;
.dot {
background: $primary;
height: 10px;
width: 10px;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
}
}
.filter-menu-header { .filter-menu-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -1,8 +1,8 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, TemplateRef, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core';
import { AppStateService } from '../../state/app-state.service'; import { AppStateService } from '../../state/app-state.service';
import { FilterModel } from './model/filter.model'; import { FilterModel } from './model/filter.model';
import { handleCheckedValue } from './utils/filter-utils'; import { handleCheckedValue } from './utils/filter-utils';
import { MatMenu, MatMenuTrigger } from '@angular/material/menu'; import { MatMenuTrigger } from '@angular/material/menu';
@Component({ @Component({
selector: 'redaction-filter', selector: 'redaction-filter',
@ -10,12 +10,12 @@ import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
styleUrls: ['./filter.component.scss'] styleUrls: ['./filter.component.scss']
}) })
export class FilterComponent { export class FilterComponent {
@Input() filterTemplate: TemplateRef<any>;
@Output() filtersChanged = new EventEmitter<FilterModel[]>(); @Output() filtersChanged = new EventEmitter<FilterModel[]>();
@Input() filterTemplate: TemplateRef<any>;
@Input() filters: FilterModel[] = []; @Input() filters: FilterModel[] = [];
@Input() filterLabel = 'filter-menu.label'; @Input() filterLabel = 'filter-menu.label';
@Input() hasArrow = true;
@Input() icon: string; @Input() icon: string;
@Input() chevron = false;
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;

View File

@ -1,9 +1,5 @@
<div class="flex-row"> <div class="wrapper">
<div <div [className]="colorClass + ' oval ' + size" [matTooltipPosition]="'above'" [matTooltip]="username">
[className]="colorClass + ' oval ' + size"
[matTooltipPosition]="'above'"
[matTooltip]="username"
>
{{ initials }} {{ initials }}
</div> </div>
<div *ngIf="withName" class="clamp-2"> <div *ngIf="withName" class="clamp-2">

View File

@ -1,6 +1,6 @@
@import '../../../assets/styles/red-variables'; .wrapper {
display: flex;
.flex-row { align-items: center;
width: fit-content; width: fit-content;
gap: 12px; gap: 6px;
} }

View File

@ -0,0 +1,5 @@
<button mat-button [class.overlay]="showDot" [class.primary]="primary">
<span [translate]="text"></span>
<mat-icon svgIcon="red:arrow-down" class="chevron-icon"></mat-icon>
</button>
<div class="dot" *ngIf="showDot"></div>

View File

@ -0,0 +1,8 @@
button {
padding: 0 10px 0 14px;
mat-icon {
width: 7px;
margin: 0 3.5px;
}
}

View File

@ -0,0 +1,16 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'redaction-chevron-button',
templateUrl: './chevron-button.component.html',
styleUrls: ['./chevron-button.component.scss']
})
export class ChevronButtonComponent implements OnInit {
@Input() text: string;
@Input() showDot = false;
@Input() primary = false;
constructor() {}
ngOnInit(): void {}
}

View File

@ -0,0 +1,12 @@
<div [matTooltipClass]="tooltipClass" [matTooltipPosition]="tooltipPosition" [matTooltip]="tooltip | translate">
<button
(click)="action.emit($event)"
[class.dark-bg]="type === 'dark-bg'"
[class.primary]="type === 'primary'"
[class.warn]="type === 'warn'"
[disabled]="disabled"
mat-icon-button
>
<mat-icon [svgIcon]="icon"></mat-icon>
</button>
</div>

View File

@ -0,0 +1,25 @@
@import '../../../../assets/styles/red-variables';
button {
height: 34px;
width: 34px;
line-height: 34px;
mat-icon {
width: 14px;
}
&.primary {
&.mat-button-disabled {
background-color: $grey-6;
color: $white !important;
}
}
&.warn {
background-color: $yellow-2;
&:hover {
background-color: $yellow-2;
}
}
}

View File

@ -0,0 +1,20 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'redaction-circle-button',
templateUrl: './circle-button.component.html',
styleUrls: ['./circle-button.component.scss']
})
export class CircleButtonComponent implements OnInit {
@Input() icon: string;
@Input() tooltip: string;
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
@Input() tooltipClass: string;
@Input() disabled = false;
@Input() type: 'default' | 'primary' | 'warn' | 'dark-bg' = 'default';
@Output() action = new EventEmitter<any>();
constructor() {}
ngOnInit(): void {}
}

View File

@ -0,0 +1,5 @@
<button mat-button [class.overlay]="showDot" [class.primary]="primary" (click)="action.emit($event)">
<mat-icon [svgIcon]="icon" *ngIf="icon"></mat-icon>
<span [translate]="text"></span>
</button>
<div class="dot" *ngIf="showDot"></div>

View File

@ -0,0 +1,7 @@
button {
padding: 0 14px 0 10px;
mat-icon {
width: 14px;
}
}

View File

@ -0,0 +1,18 @@
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'redaction-icon-button',
templateUrl: './icon-button.component.html',
styleUrls: ['./icon-button.component.scss']
})
export class IconButtonComponent implements OnInit {
@Input() icon: string;
@Input() text: string;
@Input() showDot = false;
@Input() primary = false;
@Output() action = new EventEmitter<any>();
constructor() {}
ngOnInit(): void {}
}

View File

@ -0,0 +1,4 @@
<button mat-button>
<redaction-initials-avatar color="red-white" size="small" [userId]="user?.id" [withName]="true"></redaction-initials-avatar>
<mat-icon svgIcon="red:arrow-down"></mat-icon>
</button>

View File

@ -0,0 +1,23 @@
@import '../../../../assets/styles/red-variables';
:host {
button {
padding: 0 10px 0 5px;
font-weight: 400 !important;
&:hover {
background-color: $grey-6;
}
mat-icon {
width: 7px;
margin: 0 3.5px;
}
}
&[aria-expanded='true'] {
button {
background: rgba($primary, 0.1);
}
}
}

View File

@ -0,0 +1,15 @@
import { Component, Input, OnInit } from '@angular/core';
import { UserWrapper } from '../../../user/user.service';
@Component({
selector: 'redaction-user-button',
templateUrl: './user-button.component.html',
styleUrls: ['./user-button.component.scss']
})
export class UserButtonComponent implements OnInit {
@Input() user: UserWrapper;
constructor() {}
ngOnInit(): void {}
}

View File

@ -1,10 +1,10 @@
<div class="wrapper"> <div class="wrapper">
<ng-container *ngIf="expanded"> <ng-container *ngIf="expanded">
<div *ngFor="let comment of annotation.comments; let idx = index" class="comment"> <div *ngFor="let comment of annotation.comments; let idx = index" class="comment">
<div class="comment-icon" [class.red]="isCommentOwner(comment)" [class.comment-owner]="isCommentOwner(comment)"> <div [class.comment-owner]="isCommentOwner(comment)" [class.red]="isCommentOwner(comment)" class="comment-icon">
<mat-icon [svgIcon]="isCommentOwner(comment) ? 'red:comment-fill' : 'red:comment'"></mat-icon> <mat-icon [svgIcon]="isCommentOwner(comment) ? 'red:comment-fill' : 'red:comment'"></mat-icon>
</div> </div>
<div class="trash-icon red" (click)="deleteComment(comment)" [class.comment-owner]="isCommentOwner(comment)"> <div (click)="deleteComment(comment)" [class.comment-owner]="isCommentOwner(comment)" class="trash-icon red">
<mat-icon svgIcon="red:trash"></mat-icon> <mat-icon svgIcon="red:trash"></mat-icon>
</div> </div>
@ -25,19 +25,17 @@
}) })
}} }}
</div> </div>
<div *ngIf="!addingComment" (click)="toggleAddingComment($event)" translate="comments.add-comment"></div> <div (click)="toggleAddingComment($event)" *ngIf="!addingComment" translate="comments.add-comment"></div>
</div> </div>
<form *ngIf="addingComment" [formGroup]="commentForm" (submit)="addComment()"> <form (submit)="addComment()" *ngIf="addingComment" [formGroup]="commentForm">
<div class="red-input-group"> <div class="red-input-group">
<input formControlName="comment" name="comment" type="text" [placeholder]="translateService.instant('comments.add-comment')" /> <input [placeholder]="translateService.instant('comments.add-comment')" formControlName="comment" name="comment" type="text" />
</div> </div>
</form> </form>
<div *ngIf="addingComment" class="comment-actions-container"> <div *ngIf="addingComment" class="comment-actions-container">
<button (click)="addComment()" mat-icon-button class="primary" [disabled]="!commentForm.value.comment"> <redaction-circle-button (action)="addComment()" [disabled]="!commentForm.value.comment" icon="red:check-alt" type="primary"></redaction-circle-button>
<mat-icon svgIcon="red:check-alt"></mat-icon>
</button>
<div (click)="toggleAddingComment($event)" class="all-caps-label" translate="comments.cancel"></div> <div (click)="toggleAddingComment($event)" class="all-caps-label" translate="comments.cancel"></div>
</div> </div>
</div> </div>

View File

@ -42,6 +42,9 @@ export class CommentsComponent {
public toggleAddingComment($event?: MouseEvent): void { public toggleAddingComment($event?: MouseEvent): void {
$event?.stopPropagation(); $event?.stopPropagation();
this.addingComment = !this.addingComment; this.addingComment = !this.addingComment;
if (this.addingComment) {
this.expanded = true;
}
this._changeDetectorRef.detectChanges(); this._changeDetectorRef.detectChanges();
} }
@ -54,7 +57,6 @@ export class CommentsComponent {
id: commentResponse.commentId, id: commentResponse.commentId,
user: this._userService.userId user: this._userService.userId
}); });
this.expanded = true;
}); });
this.commentForm.reset(); this.commentForm.reset();
this.toggleAddingComment(); this.toggleAddingComment();

View File

@ -5,26 +5,13 @@
<mat-icon svgIcon="red:menu"></mat-icon> <mat-icon svgIcon="red:menu"></mat-icon>
</button> </button>
<mat-menu #menuNav="matMenu"> <mat-menu #menuNav="matMenu">
<button <button mat-menu-item routerLink="/ui/projects" translate="top-bar.navigation-items.projects"></button>
mat-menu-item <button *ngIf="appStateService.activeProject" [routerLink]="'/ui/projects/' + appStateService.activeProjectId" mat-menu-item>
routerLink="/ui/projects"
translate="top-bar.navigation-items.projects"
></button>
<button
*ngIf="appStateService.activeProject"
[routerLink]="'/ui/projects/' + appStateService.activeProjectId"
mat-menu-item
>
{{ appStateService.activeProject.project.projectName }} {{ appStateService.activeProject.project.projectName }}
</button> </button>
<button <button
*ngIf="appStateService.activeFile" *ngIf="appStateService.activeFile"
[routerLink]=" [routerLink]="'/ui/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
'/ui/projects/' +
appStateService.activeProjectId +
'/file/' +
appStateService.activeFile.fileId
"
mat-menu-item mat-menu-item
> >
{{ appStateService.activeFile.filename }} {{ appStateService.activeFile.filename }}
@ -32,19 +19,11 @@
</mat-menu> </mat-menu>
</div> </div>
<div class="menu flex-2 visible-lg breadcrumbs-container"> <div class="menu flex-2 visible-lg breadcrumbs-container">
<a <a class="breadcrumb" routerLink="/ui/projects" translate="top-bar.navigation-items.projects"></a>
class="breadcrumb"
routerLink="/ui/projects"
translate="top-bar.navigation-items.projects"
></a>
<div *ngIf="appStateService.activeProject" class="breadcrumb"> <div *ngIf="appStateService.activeProject" class="breadcrumb">
<mat-icon svgIcon="red:arrow-right"></mat-icon> <mat-icon svgIcon="red:arrow-right"></mat-icon>
</div> </div>
<a <a *ngIf="appStateService.activeProject" class="breadcrumb" [routerLink]="'/ui/projects/' + appStateService.activeProjectId">
*ngIf="appStateService.activeProject"
class="breadcrumb"
[routerLink]="'/ui/projects/' + appStateService.activeProjectId"
>
{{ appStateService.activeProject.project.projectName }} {{ appStateService.activeProject.project.projectName }}
</a> </a>
<div *ngIf="appStateService.activeFile" class="breadcrumb"> <div *ngIf="appStateService.activeFile" class="breadcrumb">
@ -53,12 +32,7 @@
<a <a
*ngIf="appStateService.activeFile" *ngIf="appStateService.activeFile"
class="breadcrumb" class="breadcrumb"
[routerLink]=" [routerLink]="'/ui/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
'/ui/projects/' +
appStateService.activeProjectId +
'/file/' +
appStateService.activeFile.fileId
"
> >
{{ appStateService.activeFile.filename }} {{ appStateService.activeFile.filename }}
</a> </a>
@ -68,32 +42,12 @@
<div class="app-name" translate="app-name"></div> <div class="app-name" translate="app-name"></div>
</div> </div>
<div class="menu right flex-2"> <div class="menu right flex-2">
<button [matMenuTriggerFor]="menu" mat-button class="arrow-button"> <redaction-user-button [user]="user" [matMenuTriggerFor]="menu"></redaction-user-button>
<redaction-initials-avatar
color="red-white"
size="small"
[userId]="user?.id"
[withName]="true"
></redaction-initials-avatar>
<mat-icon svgIcon="red:arrow-down"></mat-icon>
</button>
<mat-menu #menu="matMenu"> <mat-menu #menu="matMenu">
<button <button [matMenuTriggerFor]="language" mat-menu-item translate="top-bar.navigation-items.my-account.children.language.label"></button>
[matMenuTriggerFor]="language"
mat-menu-item
translate="top-bar.navigation-items.my-account.children.language.label"
></button>
<mat-menu #language="matMenu"> <mat-menu #language="matMenu">
<button <button (click)="changeLanguage('en')" mat-menu-item translate="top-bar.navigation-items.my-account.children.language.english"></button>
(click)="changeLanguage('en')" <button (click)="changeLanguage('de')" mat-menu-item translate="top-bar.navigation-items.my-account.children.language.german"></button>
mat-menu-item
translate="top-bar.navigation-items.my-account.children.language.english"
></button>
<button
(click)="changeLanguage('de')"
mat-menu-item
translate="top-bar.navigation-items.my-account.children.language.german"
></button>
</mat-menu> </mat-menu>
<button (click)="logout()" mat-menu-item> <button (click)="logout()" mat-menu-item>
<mat-icon svgIcon="red:logout"> </mat-icon> <mat-icon svgIcon="red:logout"> </mat-icon>

View File

@ -1,46 +1,51 @@
<div [class.visible]="menuOpen" *ngIf="canPerformAnnotationActions" class="annotation-actions"> <div [class.visible]="menuOpen" *ngIf="canPerformAnnotationActions" class="annotation-actions">
<button <redaction-circle-button
(click)="acceptSuggestion($event, annotation)" (action)="acceptSuggestion($event, annotation)"
mat-icon-button type="dark-bg"
*ngIf="canAcceptSuggestion" *ngIf="canAcceptSuggestion"
[matTooltip]="'annotation-actions.accept-suggestion.label' | translate" tooltip="annotation-actions.accept-suggestion.label"
icon="red:check-alt"
> >
<mat-icon svgIcon="red:check-alt"></mat-icon> </redaction-circle-button>
</button>
<button (click)="undoDirectAction($event, annotation)" *ngIf="canUndoAnnotation" mat-icon-button> <redaction-circle-button
<mat-icon svgIcon="red:undo" [matTooltip]="'annotation-actions.undo' | translate"></mat-icon> (action)="undoDirectAction($event, annotation)"
</button> *ngIf="canUndoAnnotation"
type="dark-bg"
icon="red:undo"
tooltip="annotation-actions.undo"
>
</redaction-circle-button>
<button <redaction-circle-button
(click)="rejectSuggestion($event, annotation)" (action)="rejectSuggestion($event, annotation)"
mat-icon-button type="dark-bg"
icon="red:close"
*ngIf="canRejectSuggestion" *ngIf="canRejectSuggestion"
[matTooltip]="'annotation-actions.reject-suggestion' | translate" tooltip="annotation-actions.reject-suggestion"
> >
<mat-icon svgIcon="red:close"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="suggestRemoveAnnotation($event, annotation, true)" (action)="suggestRemoveAnnotation($event, annotation, true)"
mat-icon-button type="dark-bg"
icon="red:trash"
*ngIf="canDirectlySuggestToRemoveAnnotation" *ngIf="canDirectlySuggestToRemoveAnnotation"
[matTooltip]="'annotation-actions.suggest-remove-annotation' | translate" tooltip="annotation-actions.suggest-remove-annotation"
> >
<mat-icon svgIcon="red:trash"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="openMenu($event)" *ngIf="requiresSuggestionRemoveMenu"
(action)="openMenu($event)"
[class.active]="menuOpen" [class.active]="menuOpen"
[matMenuTriggerFor]="menu" [matMenuTriggerFor]="menu"
[matTooltip]="'annotation-actions.suggest-remove-annotation' | translate" tooltip="annotation-actions.suggest-remove-annotation"
class="confirm" type="dark-bg"
mat-icon-button icon="red:trash"
*ngIf="requiresSuggestionRemoveMenu"
> >
<mat-icon svgIcon="red:trash"></mat-icon> </redaction-circle-button>
</button>
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before"> <mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
<div (click)="suggestRemoveAnnotation($event, annotation, true)" mat-menu-item> <div (click)="suggestRemoveAnnotation($event, annotation, true)" mat-menu-item>
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon> <redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>

View File

@ -106,8 +106,4 @@ export class AnnotationActionsComponent implements OnInit {
get dictionaryColor() { get dictionaryColor() {
return this.appStateService.getDictionaryColor('suggestion-add-dictionary'); return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
} }
get suggestRemoveLabel() {
return 'suggestRemoveLabel';
}
} }

View File

@ -26,10 +26,10 @@
]" ]"
> >
</redaction-status-bar> </redaction-status-bar>
<div class="all-caps-label mr-8">{{ appStateService.activeFile.status.toLowerCase().replace('_', '-') | translate }}</div> <div class="all-caps-label mr-16 ml-8">{{ appStateService.activeFile.status.toLowerCase().replace('_', '-') | translate }}</div>
<redaction-initials-avatar <redaction-initials-avatar
[userId]="appStateService.activeFile.currentReviewer" [userId]="appStateService.activeFile.currentReviewer"
[withName]="appStateService.activeFile.currentReviewer" [withName]="!!appStateService.activeFile.currentReviewer"
></redaction-initials-avatar> ></redaction-initials-avatar>
<div <div
*ngIf="!appStateService.activeFile.currentReviewer && permissionsService.canAssignReviewer(appStateService.activeFile)" *ngIf="!appStateService.activeFile.currentReviewer && permissionsService.canAssignReviewer(appStateService.activeFile)"
@ -37,29 +37,37 @@
translate="file-preview.assign-reviewer" translate="file-preview.assign-reviewer"
(click)="assignReviewer()" (click)="assignReviewer()"
></div> ></div>
<button
mat-icon-button <div class="assign-actions-wrapper" *ngIf="permissionsService.canAssignReviewer(appStateService.activeFile)">
matTooltipPosition="below" <redaction-circle-button
[matTooltip]="'project-overview.assign.action' | translate" tooltipPosition="below"
*ngIf="permissionsService.canAssignReviewer(appStateService.activeFile) && appStateService.activeFile.currentReviewer" tooltip="project-overview.assign.action"
(click)="assignReviewer()" *ngIf="permissionsService.canAssignReviewer(appStateService.activeFile) && appStateService.activeFile.currentReviewer"
> (action)="assignReviewer()"
<mat-icon svgIcon="red:edit"></mat-icon> icon="red:edit"
</button> >
<button </redaction-circle-button>
mat-icon-button
matTooltipPosition="below" <redaction-circle-button
[matTooltip]="'file-preview.assign-me' | translate" tooltipPosition="below"
*ngIf="permissionsService.canAssignReviewer(appStateService.activeFile) && !permissionsService.isFileReviewer()" tooltip="file-preview.assign-me"
(click)="assignToMe()" *ngIf="permissionsService.canAssignReviewer(appStateService.activeFile) && !permissionsService.isFileReviewer()"
> (action)="assignToMe()"
<mat-icon svgIcon="red:assign-me"></mat-icon> icon="red:assign-me"
</button> >
</redaction-circle-button>
</div>
<div class="vertical-line"></div> <div class="vertical-line"></div>
<redaction-file-actions (actionPerformed)="fileActionPerformed($event)" *ngIf="viewReady"></redaction-file-actions> <redaction-file-actions (actionPerformed)="fileActionPerformed($event)" *ngIf="viewReady"></redaction-file-actions>
<button [routerLink]="['/ui/projects/' + appStateService.activeProjectId]" mat-icon-button [matTooltip]="'common.close' | translate"> <redaction-circle-button
<mat-icon svgIcon="red:close"></mat-icon> class="ml-8"
</button> [routerLink]="['/ui/projects/' + appStateService.activeProjectId]"
tooltip="common.close"
tooltipPosition="below"
icon="red:close"
></redaction-circle-button>
</div> </div>
</div> </div>
@ -87,6 +95,7 @@
(filtersChanged)="filtersChanged($event)" (filtersChanged)="filtersChanged($event)"
[filterTemplate]="annotationFilterTemplate" [filterTemplate]="annotationFilterTemplate"
[filters]="annotationFilters" [filters]="annotationFilters"
[chevron]="true"
></redaction-filter> ></redaction-filter>
</div> </div>
</redaction-hidden-action> </redaction-hidden-action>

View File

@ -13,14 +13,11 @@ redaction-pdf-viewer {
width: 1px; width: 1px;
height: 30px; height: 30px;
background-color: $grey-4; background-color: $grey-4;
margin: 0 8px; margin: 0 16px;
}
.mr-8 {
margin-right: 8px;
} }
.assign-reviewer { .assign-reviewer {
margin-left: 6px;
text-decoration: underline; text-decoration: underline;
} }
@ -28,7 +25,6 @@ redaction-pdf-viewer {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
gap: 10px;
} }
.right-fixed-container { .right-fixed-container {
@ -128,3 +124,21 @@ redaction-pdf-viewer {
padding: 24px; padding: 24px;
text-align: center; text-align: center;
} }
.assign-actions-wrapper {
display: flex;
gap: 2px;
margin-left: 8px;
}
.mr-8 {
margin-right: 8px;
}
.mr-16 {
margin-right: 16px;
}
.ml-8 {
margin-left: 8px;
}

View File

@ -1,18 +1,16 @@
<section *ngIf="appStateService.hasProjects"> <section *ngIf="appStateService.hasProjects">
<div class="page-header"> <div class="page-header">
<div class="filters flex-row"> <div class="filters">
<div translate="filters.filter-by"></div> <div translate="filters.filter-by"></div>
<redaction-filter <redaction-filter
[filters]="statusFilters" [filters]="statusFilters"
[filterLabel]="'filters.status'" [filterLabel]="'filters.status'"
[hasArrow]="false"
[icon]="'red:status'" [icon]="'red:status'"
(filtersChanged)="filtersChanged()" (filtersChanged)="filtersChanged()"
></redaction-filter> ></redaction-filter>
<redaction-filter <redaction-filter
[filters]="peopleFilters" [filters]="peopleFilters"
[filterLabel]="'filters.people'" [filterLabel]="'filters.people'"
[hasArrow]="false"
[icon]="'red:user'" [icon]="'red:user'"
(filtersChanged)="filtersChanged()" (filtersChanged)="filtersChanged()"
></redaction-filter> ></redaction-filter>
@ -21,14 +19,16 @@
[filterLabel]="'filters.needs-work'" [filterLabel]="'filters.needs-work'"
[filterTemplate]="needsWorkTemplate" [filterTemplate]="needsWorkTemplate"
[filters]="needsWorkFilters" [filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'" [icon]="'red:needs-work'"
></redaction-filter> ></redaction-filter>
</div> </div>
<button (click)="openAddProjectDialog()" *ngIf="userService.isManager(user)" class="add-project-btn" color="primary" mat-flat-button> <redaction-icon-button
<mat-icon svgIcon="red:plus"></mat-icon> *ngIf="userService.isManager(user)"
<span translate="project-listing.add-new"></span> icon="red:plus"
</button> (action)="openAddProjectDialog()"
text="project-listing.add-new"
[primary]="true"
></redaction-icon-button>
</div> </div>
<div class="flex red-content-inner"> <div class="flex red-content-inner">
@ -99,54 +99,48 @@
<redaction-status-bar [config]="getProjectStatusConfig(pw)"></redaction-status-bar> <redaction-status-bar [config]="getProjectStatusConfig(pw)"></redaction-status-bar>
<div class="action-buttons" *ngIf="permissionsService.isManager()"> <div class="action-buttons" *ngIf="permissionsService.isManager()">
<button <redaction-circle-button
(click)="openDeleteProjectDialog($event, pw.project)" (action)="openDeleteProjectDialog($event, pw.project)"
[matTooltip]="'project-listing.delete.action' | translate" tooltip="project-listing.delete.action"
matTooltipPosition="above" type="dark-bg"
color="accent" icon="red:trash"
mat-icon-button
> >
<mat-icon svgIcon="red:trash"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
(click)="openEditProjectDialog($event, pw.project)" (action)="openEditProjectDialog($event, pw.project)"
[matTooltip]="'project-listing.edit.action' | translate" tooltip="project-listing.edit.action"
matTooltipPosition="above" type="dark-bg"
color="accent" icon="red:edit"
mat-icon-button
> >
<mat-icon svgIcon="red:edit"></mat-icon> </redaction-circle-button>
</button>
<div <redaction-circle-button
[matTooltip]="(pw.allFilesApproved ? 'report.action' : 'report.unavailable') | translate"
matTooltipPosition="above"
*ngIf="permissionsService.isManagerAndOwner(pw.project) && pw.hasFiles" *ngIf="permissionsService.isManagerAndOwner(pw.project) && pw.hasFiles"
(action)="downloadRedactionReport($event, pw.project)"
[tooltip]="pw.allFilesApproved ? 'report.action' : 'report.unavailable'"
[disabled]="!pw.allFilesApproved"
type="dark-bg"
icon="red:report"
> >
<button mat-icon-button (click)="downloadRedactionReport($event, pw.project)" [disabled]="!pw.allFilesApproved" color="accent"> </redaction-circle-button>
<mat-icon svgIcon="red:report"></mat-icon>
</button> <redaction-circle-button
</div> (action)="openAssignProjectOwnerDialog($event, pw.project)"
<button tooltip="project-listing.assign.action"
(click)="openAssignProjectOwnerDialog($event, pw.project)" type="dark-bg"
[matTooltip]="'project-listing.assign.action' | translate" icon="red:assign"
matTooltipPosition="above"
color="accent"
mat-icon-button
> >
<mat-icon svgIcon="red:assign"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button
color="accent"
*ngIf="permissionsService.isManagerAndOwner(pw.project) && pw.hasFiles" *ngIf="permissionsService.isManagerAndOwner(pw.project) && pw.hasFiles"
(click)="reanalyseProject($event, pw.project)" (action)="reanalyseProject($event, pw.project)"
mat-icon-button tooltip="project-listing.reanalyse.action"
[matTooltip]="'project-listing.reanalyse.action' | translate" type="dark-bg"
matTooltipPosition="above" icon="red:refresh"
> >
<mat-icon svgIcon="red:refresh"></mat-icon> </redaction-circle-button>
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,12 +1,5 @@
@import '../../../assets/styles/red-mixins'; @import '../../../assets/styles/red-mixins';
.add-project-btn {
.mat-icon {
width: 14px;
color: $white;
}
}
.left-container { .left-container {
width: calc(100vw - #{$right-container-width} - 90px); width: calc(100vw - #{$right-container-width} - 90px);

View File

@ -1,27 +1,19 @@
<ng-container *ngIf="areSomeFilesSelected"> <ng-container *ngIf="areSomeFilesSelected">
<button <redaction-circle-button
(click)="delete()" (action)="delete()"
*ngIf="canDelete" *ngIf="canDelete"
[matTooltip]="'project-overview.bulk.delete' | translate" tooltip="project-overview.bulk.delete"
class="dark" type="dark-bg"
color="accent" icon="red:trash"
mat-icon-button ></redaction-circle-button>
matTooltipPosition="above"
>
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button <redaction-circle-button
(click)="assign()" (action)="assign()"
*ngIf="canAssign" *ngIf="canAssign"
[matTooltip]="'project-overview.bulk.assign' | translate" tooltip="project-overview.bulk.assign"
class="dark" type="dark-bg"
color="accent" icon="red:assign"
mat-icon-button ></redaction-circle-button>
matTooltipPosition="above"
>
<mat-icon svgIcon="red:assign"></mat-icon>
</button>
<!-- <div [matTooltip]="reanalyseTooltip | translate" matTooltipPosition="above">--> <!-- <div [matTooltip]="reanalyseTooltip | translate" matTooltipPosition="above">-->
<!-- <button (click)="reanalyse()" [disabled]="reanalyseDisabled" class="dark" color="accent" mat-icon-button>--> <!-- <button (click)="reanalyse()" [disabled]="reanalyseDisabled" class="dark" color="accent" mat-icon-button>-->
@ -29,39 +21,23 @@
<!-- </button>--> <!-- </button>-->
<!-- </div>--> <!-- </div>-->
<button <redaction-circle-button
(click)="approveDocuments()" (action)="approveDocuments()"
*ngIf="canApprove" *ngIf="canApprove"
[matTooltip]="'project-overview.approve' | translate" tooltip="project-overview.approve"
class="dark" type="dark-bg"
color="accent" icon="red:check-alt"
mat-icon-button ></redaction-circle-button>
matTooltipPosition="above"
>
<mat-icon svgIcon="red:check-alt"></mat-icon>
</button>
<button <redaction-circle-button
(click)="setToUnderApproval()" (action)="setToUnderApproval()"
*ngIf="canSetToUnderApproval" *ngIf="canSetToUnderApproval"
[matTooltip]="'project-overview.under-approval' | translate" tooltip="project-overview.under-approval"
class="dark" type="dark-bg"
color="accent" icon="red:check-alt"
mat-icon-button
matTooltipPosition="above"
> >
<mat-icon svgIcon="red:check-alt"></mat-icon> </redaction-circle-button>
</button>
<button <redaction-circle-button (action)="setToUnderReview()" *ngIf="canSetToUnderReview" tooltip="project-overview.under-review" type="dark-bg" icon="red:refres">
(click)="setToUnderReview()" </redaction-circle-button>
*ngIf="canSetToUnderReview"
[matTooltip]="'project-overview.under-review' | translate"
class="dark"
color="accent"
mat-icon-button
matTooltipPosition="above"
>
<mat-icon svgIcon="red:refresh"></mat-icon>
</button>
</ng-container> </ng-container>

View File

@ -1,3 +1,5 @@
:host { :host {
display: flex; display: flex;
align-items: center;
gap: 2px;
} }

View File

@ -1,22 +1,4 @@
<div *ngIf="permissionsService.isManager()" class="actions-row"> <div class="heading-xl mt-8">
<button (click)="openDeleteProjectDialog($event)" [matTooltip]="'project-details.delete' | translate" mat-icon-button matTooltipPosition="above">
<mat-icon svgIcon="red:trash"></mat-icon>
</button>
<button (click)="openEditProjectDialog($event)" [matTooltip]="'project-details.edit' | translate" mat-icon-button matTooltipPosition="above">
<mat-icon svgIcon="red:edit"></mat-icon>
</button>
<div
*ngIf="permissionsService.isManagerAndOwner()"
[matTooltip]="(appStateService.activeProject.allFilesApproved ? 'report.action' : 'report.unavailable') | translate"
matTooltipPosition="above"
>
<button (click)="downloadRedactionReport($event)" [disabled]="!appStateService.activeProject.allFilesApproved" mat-icon-button>
<mat-icon svgIcon="red:report"></mat-icon>
</button>
</div>
</div>
<div class="heading-xl mt-16">
{{ appStateService.activeProject.project.projectName }} {{ appStateService.activeProject.project.projectName }}
</div> </div>
@ -36,7 +18,7 @@
<div *ngIf="overflowCount" class="member"> <div *ngIf="overflowCount" class="member">
<div class="oval large white-dark">+{{ overflowCount }}</div> <div class="oval large white-dark">+{{ overflowCount }}</div>
</div> </div>
<div (click)="openAssignProjectMembersDialog()" *ngIf="permissionsService.isManager()" class="member pointer"> <div (click)="openAssignProjectMembersDialog.emit()" *ngIf="permissionsService.isManager()" class="member pointer">
<div class="oval red-white large">+</div> <div class="oval red-white large">+</div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Output, EventEmitter, Input, ChangeDetectorRef } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service'; import { AppStateService } from '../../../state/app-state.service';
import { groupBy } from '../../../utils/functions'; import { groupBy } from '../../../utils/functions';
import { DoughnutChartConfig } from '../../../components/simple-doughnut-chart/simple-doughnut-chart.component'; import { DoughnutChartConfig } from '../../../components/simple-doughnut-chart/simple-doughnut-chart.component';
@ -17,8 +17,8 @@ import { StatusSorter } from '../../../common/sorters/status-sorter';
export class ProjectDetailsComponent implements OnInit { export class ProjectDetailsComponent implements OnInit {
public documentsChartData: DoughnutChartConfig[] = []; public documentsChartData: DoughnutChartConfig[] = [];
@Input() public filters: { needsWorkFilters: FilterModel[]; statusFilters: FilterModel[] }; @Input() public filters: { needsWorkFilters: FilterModel[]; statusFilters: FilterModel[] };
@Output() public reloadProjects = new EventEmitter();
@Output() public filtersChanged = new EventEmitter(); @Output() public filtersChanged = new EventEmitter();
@Output() public openAssignProjectMembersDialog = new EventEmitter();
constructor( constructor(
public readonly appStateService: AppStateService, public readonly appStateService: AppStateService,
@ -44,28 +44,6 @@ export class ProjectDetailsComponent implements OnInit {
return this.appStateService.activeProject.project.memberIds.length > 6 ? this.appStateService.activeProject.project.memberIds.length - 6 : 0; return this.appStateService.activeProject.project.memberIds.length > 6 ? this.appStateService.activeProject.project.memberIds.length - 6 : 0;
} }
public openEditProjectDialog($event: MouseEvent) {
this._dialogService.openEditProjectDialog($event, this.appStateService.activeProject.project);
}
public openDeleteProjectDialog($event: MouseEvent) {
this._dialogService.openDeleteProjectDialog($event, this.appStateService.activeProject.project, () => {
this._router.navigate(['/ui/projects']);
});
}
public openAssignProjectMembersDialog(): void {
this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.appStateService.activeProject.project, () => {
this.reloadProjects.emit();
this._changeDetectorRef.detectChanges();
});
}
public downloadRedactionReport($event: MouseEvent): void {
$event.stopPropagation();
this.appStateService.downloadRedactionReport();
}
public calculateChartConfig(): void { public calculateChartConfig(): void {
if (this.appStateService.activeProject) { if (this.appStateService.activeProject) {
const groups = groupBy(this.appStateService.activeProject?.files, 'status'); const groups = groupBy(this.appStateService.activeProject?.files, 'status');

View File

@ -1,20 +1,18 @@
<redaction-project-overview-empty (uploadFiles)="uploadFiles($event)" *ngIf="!appStateService.activeProject?.hasFiles"></redaction-project-overview-empty> <redaction-project-overview-empty (uploadFiles)="uploadFiles($event)" *ngIf="!appStateService.activeProject?.hasFiles"></redaction-project-overview-empty>
<section *ngIf="appStateService.activeProject?.hasFiles"> <section *ngIf="appStateService.activeProject?.hasFiles">
<div *ngIf="appStateService.activeProject" class="page-header"> <div *ngIf="appStateService.activeProject" class="page-header">
<div class="filters flex-row"> <div class="filters">
<div translate="filters.filter-by"></div> <div translate="filters.filter-by"></div>
<redaction-filter <redaction-filter
(filtersChanged)="filtersChanged()" (filtersChanged)="filtersChanged()"
[filterLabel]="'filters.status'" [filterLabel]="'filters.status'"
[filters]="statusFilters" [filters]="statusFilters"
[hasArrow]="false"
[icon]="'red:status'" [icon]="'red:status'"
></redaction-filter> ></redaction-filter>
<redaction-filter <redaction-filter
(filtersChanged)="filtersChanged()" (filtersChanged)="filtersChanged()"
[filterLabel]="'filters.assigned-people'" [filterLabel]="'filters.assigned-people'"
[filters]="peopleFilters" [filters]="peopleFilters"
[hasArrow]="false"
[icon]="'red:user'" [icon]="'red:user'"
></redaction-filter> ></redaction-filter>
<redaction-filter <redaction-filter
@ -22,24 +20,52 @@
[filterLabel]="'filters.needs-work'" [filterLabel]="'filters.needs-work'"
[filterTemplate]="needsWorkTemplate" [filterTemplate]="needsWorkTemplate"
[filters]="needsWorkFilters" [filters]="needsWorkFilters"
[hasArrow]="false"
[icon]="'red:needs-work'" [icon]="'red:needs-work'"
></redaction-filter> ></redaction-filter>
</div> </div>
<div> <div class="actions">
<button <redaction-circle-button
(click)="fileInput.click()" (action)="openEditProjectDialog($event)"
class="primary" tooltip="project-overview.header-actions.edit"
mat-icon-button tooltipPosition="below"
[matTooltip]="'project-overview.upload-document' | translate" icon="red:edit"
matTooltipPosition="before" ></redaction-circle-button>
> <redaction-circle-button
<mat-icon svgIcon="red:upload"></mat-icon> (action)="openDeleteProjectDialog($event)"
</button> tooltip="project-overview.header-actions.delete"
<button [routerLink]="['/ui/projects/']" mat-icon-button [matTooltip]="'common.close' | translate"> tooltipPosition="below"
<mat-icon svgIcon="red:close"></mat-icon> icon="red:trash"
</button> ></redaction-circle-button>
<redaction-circle-button
*ngIf="permissionsService.isManagerAndOwner()"
(action)="downloadRedactionReport($event)"
[tooltip]="appStateService.activeProject.allFilesApproved ? 'report.action' : 'report.unavailable'"
[disabled]="!appStateService.activeProject.allFilesApproved"
tooltipPosition="below"
icon="red:report"
></redaction-circle-button>
<redaction-circle-button
(action)="openAssignProjectMembersDialog()"
tooltip="project-overview.header-actions.assign"
tooltipPosition="below"
icon="red:assign"
></redaction-circle-button>
<redaction-circle-button
class="ml-14"
(action)="fileInput.click()"
tooltip="project-overview.header-actions.upload-document"
tooltipPosition="below"
icon="red:upload"
type="primary"
></redaction-circle-button>
<redaction-circle-button
class="ml-6"
[routerLink]="['/ui/projects/']"
tooltip="common.close"
tooltipPosition="below"
icon="red:close"
></redaction-circle-button>
<input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" /> <input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" />
</div> </div>
@ -184,6 +210,7 @@
[fileStatus]="fileStatus" [fileStatus]="fileStatus"
(actionPerformed)="calculateData()" (actionPerformed)="calculateData()"
*ngIf="!fileStatus.isProcessing" *ngIf="!fileStatus.isProcessing"
class="mr-4"
></redaction-file-actions> ></redaction-file-actions>
<redaction-status-bar <redaction-status-bar
class="mr-8" class="mr-8"
@ -204,7 +231,7 @@
<div class="right-fixed-container"> <div class="right-fixed-container">
<redaction-project-details <redaction-project-details
#projectDetailsComponent #projectDetailsComponent
(reloadProjects)="calculateData()" (openAssignProjectMembersDialog)="openAssignProjectMembersDialog()"
[filters]="detailsContainerFilters" [filters]="detailsContainerFilters"
(filtersChanged)="filtersChanged($event)" (filtersChanged)="filtersChanged($event)"
></redaction-project-details> ></redaction-project-details>

View File

@ -65,3 +65,15 @@
color: lighten($accent, 10%); color: lighten($accent, 10%);
} }
} }
.ml-6 {
margin-left: 6px;
}
.ml-14 {
margin-left: 14px;
}
.mr-4 {
margin-right: 4px;
}

View File

@ -286,4 +286,25 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
this.selectedFileIds = []; this.selectedFileIds = [];
this.reloadProjects(); this.reloadProjects();
} }
public openEditProjectDialog($event: MouseEvent) {
this._dialogService.openEditProjectDialog($event, this.appStateService.activeProject.project);
}
public openDeleteProjectDialog($event: MouseEvent) {
this._dialogService.openDeleteProjectDialog($event, this.appStateService.activeProject.project, () => {
this._router.navigate(['/ui/projects']);
});
}
public downloadRedactionReport($event: MouseEvent): void {
$event.stopPropagation();
this.appStateService.downloadRedactionReport();
}
public openAssignProjectMembersDialog(): void {
this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.appStateService.activeProject.project, () => {
this.reloadProjects();
});
}
} }

View File

@ -62,13 +62,13 @@
"needs-work": "Analyzed" "needs-work": "Analyzed"
}, },
"report": { "report": {
"unavailable": "Redaction Report is only available once all files have been approved.", "unavailable": "Redaction report is only available once all files have been approved.",
"unavailable-single": "Redaction Report is only available once this file has been approved.", "unavailable-single": "Redaction report is only available once this file has been approved.",
"action": "Download Redaction Report" "action": "Download Redaction Report"
}, },
"project-listing": { "project-listing": {
"reanalyse": { "reanalyse": {
"action": "Reanalyse entire Project" "action": "Reanalyse entire project"
}, },
"assign": { "assign": {
"action": "Assign Owner & Members" "action": "Assign Owner & Members"
@ -126,8 +126,6 @@
} }
}, },
"project-details": { "project-details": {
"edit": "Edit",
"delete": "Delete",
"dialog": { "dialog": {
"title": "Project Details", "title": "Project Details",
"info": { "info": {
@ -142,6 +140,12 @@
"members": "Members" "members": "Members"
}, },
"project-overview": { "project-overview": {
"header-actions": {
"edit": "Edit",
"delete": "Delete",
"assign": "Assign Owner & Members",
"upload-document": "Upload Document"
},
"download-redacted-file": "Download Redacted File", "download-redacted-file": "Download Redacted File",
"under-approval": "For Approval", "under-approval": "For Approval",
"approve": "Approve", "approve": "Approve",
@ -162,7 +166,7 @@
} }
}, },
"report": { "report": {
"action": "Download Redaction Report" "action": "Download redaction report"
}, },
"assign": { "assign": {
"action": "Assign Reviewer" "action": "Assign Reviewer"
@ -214,13 +218,11 @@
"description": "Description" "description": "Description"
}, },
"header": "Project Overview", "header": "Project Overview",
"upload-document": "Upload Document",
"no-project": "Requested project: {{projectId}} does not exist! <a href='/ui/projects'>Back to Project Listing. <a/>", "no-project": "Requested project: {{projectId}} does not exist! <a href='/ui/projects'>Back to Project Listing. <a/>",
"bulk": { "bulk": {
"delete": "Delete documents", "delete": "Delete Documents",
"assign": "Assign reviewer", "assign": "Assign Reviewer",
"change-state": "Change state", "reanalyse": "Reanalyse Documents",
"reanalyse": "Reanalyse documents",
"reanalyse-error-outdated": "Not all selected documents can be reanalysed, some may not be assigned to you or may not be outdated." "reanalyse-error-outdated": "Not all selected documents can be reanalysed, some may not be assigned to you or may not be outdated."
} }
}, },

View File

@ -1,3 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 426.67" width="640" height="426.67"><defs><path d="M640 0L0 0L320 426.67L640 0Z" id="c419bDOtl"></path></defs><g><g><g><use xlink:href="#c419bDOtl" opacity="1" fill-opacity="1"></use><g><use xlink:href="#c419bDOtl" opacity="1" fill-opacity="0" stroke="#000000" stroke-width="1" stroke-opacity="0"></use></g></g></g></g></svg> <title>9A0E7FCD-AD02-40D9-BEF9-48F61351C288</title>
<g id="Styleguide" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Styleguide-Buttons" transform="translate(-469.000000, -757.000000)">
<rect x="0" y="0" width="904" height="906"></rect>
<rect id="Rectangle" x="359" y="747" width="134" height="34" rx="17"></rect>
<g id="right" transform="translate(469.000000, 757.000000)" fill="currentColor">
<polygon id="Fill-1" points="7 9 10 5 4 5"></polygon>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 772 B

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="100px" height="90px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>upload</title> <title>upload</title>
<g id="upload" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd"> <g id="upload" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
<path d="M35,35 L35,45 L15,45 L15,90 L85,90 L85,45 L65,45 L65,35 L95,35 L95,100 L5,100 L5,35 L35,35 Z M50,0 L70,20 L63,27 L55,19 L55,70 L45,70 L45,19 L37,27 L30,20 L50,0 Z" id="Combined-Shape" fill-rule="nonzero"></path> <path d="M35,35 L35,45 L15,45 L15,90 L85,90 L85,45 L65,45 L65,35 L95,35 L95,100 L5,100 L5,35 L35,35 Z M50,0 L70,20 L63,27 L55,19 L55,70 L45,70 L45,19 L37,27 L30,20 L50,0 Z" id="Combined-Shape" fill-rule="nonzero"></path>

Before

Width:  |  Height:  |  Size: 551 B

After

Width:  |  Height:  |  Size: 550 B

View File

@ -9,14 +9,6 @@
display: flex !important; display: flex !important;
align-items: center; align-items: center;
&.mat-primary {
font-weight: 500 !important;
}
&:not(.mat-primary) {
font-weight: 400 !important;
}
.mat-button-wrapper { .mat-button-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
@ -24,50 +16,62 @@
} }
} }
.mat-button[aria-expanded='true'], redaction-icon-button,
.mat-button.overlay { redaction-chevron-button,
background: rgba($primary, 0.1); redaction-circle-button {
} position: relative;
transition: background-color 0.2s;
.mat-button, button {
.mat-flat-button, font-weight: 400 !important;
.mat-icon-button {
gap: 6px;
mat-icon { &.overlay {
width: 14px; background: rgba($primary, 0.1);
} }
}
.arrow-button mat-icon { &:not(.overlay):hover {
width: 9px; background-color: $grey-6;
margin-left: 3px; }
}
.mat-icon-button { &.primary {
transition: background-color 0.25s ease-in-out; font-weight: 500 !important;
width: 34px !important; background-color: $primary;
height: 34px !important; color: $white;
line-height: 34px !important;
&.warn { &:hover {
background-color: $yellow-2; background-color: $red-2;
} }
}
&.primary { &.dark-bg:hover {
background-color: $primary; background-color: $grey-4;
color: $white !important;
&.mat-button-disabled {
background-color: rgba($primary, 0.7);
} }
} }
&:hover:not(.warn):not(.primary) { .dot {
background-color: $grey-2; background: $primary;
} height: 10px;
width: 10px;
&.dark:hover { border-radius: 50%;
background-color: $grey-4; position: absolute;
top: 0;
left: 0;
}
}
redaction-chevron-button,
redaction-icon-button {
&[aria-expanded='true'] {
button {
background: rgba($primary, 0.1);
}
}
}
redaction-circle-button {
&[aria-expanded='true'] {
button {
background: $grey-4;
}
} }
} }

View File

@ -24,6 +24,21 @@ body {
box-sizing: border-box; box-sizing: border-box;
background-color: $white; background-color: $white;
z-index: 3; z-index: 3;
.filters {
gap: 2px;
display: flex;
align-items: center;
> div:first-child {
margin-right: 6px;
}
}
.actions {
display: flex;
align-items: center;
gap: 2px;
}
} }
.red-content-inner { .red-content-inner {
@ -33,33 +48,12 @@ body {
.right-fixed-container { .right-fixed-container {
border-left: 1px solid $grey-4; border-left: 1px solid $grey-4;
background: $white; background: $white;
height: calc(100vh - 110px - 2 * #{$right-container-padding}); height: calc(100vh - 110px - 2 * #{$right-container-vertical-padding});
width: $right-container-inside-width; width: $right-container-inside-width;
padding: $right-container-padding; padding: $right-container-vertical-padding $right-container-horizontal-padding;
position: fixed; position: fixed;
right: 0; right: 0;
z-index: 2; z-index: 2;
.actions-row {
display: flex;
height: 40px;
margin-left: -10px;
}
}
.filters {
div {
margin-right: 6px;
}
button {
flex-direction: row-reverse;
}
}
.flex-row {
display: flex;
align-items: center;
} }
.flex { .flex {

View File

@ -14,6 +14,7 @@ $blue-2: #48c9f7;
$blue-3: #5b97db; $blue-3: #5b97db;
$blue-4: #374c81; $blue-4: #374c81;
$red-1: #dd4d50; $red-1: #dd4d50;
$red-2: #f16164;
$yellow-1: #ffb83b; $yellow-1: #ffb83b;
$yellow-2: #fdbd00; $yellow-2: #fdbd00;
$green-1: #00ff00; $green-1: #00ff00;
@ -28,7 +29,6 @@ $dark: $black;
$separator: rgba(226, 228, 233, 0.9); $separator: rgba(226, 228, 233, 0.9);
$right-container-inside-width: 340px; $right-container-inside-width: 340px;
$right-container-padding: 24px; $right-container-horizontal-padding: 24px;
$right-container-width: calc( $right-container-vertical-padding: 16px;
#{$right-container-inside-width} + 2 *#{$right-container-padding} + 1px $right-container-width: calc(#{$right-container-inside-width} + 2 *#{$right-container-horizontal-padding} + 1px);
);