Pull request #19: Ui updates
Merge in RED/ui from ui-updates to master * commit 'e8993da266e9d0fe4eb98b3546b00887aab2f602': fixed lint errors Some arrow navigation fixes Avatar initials color depending on role Project status bar using real data Redo tables layout with grid Some input styling Fixes Working arrow navigation Removed duplicate flash/lightning icon
This commit is contained in:
commit
32334c3299
@ -1,4 +1,4 @@
|
||||
<div class="flex-row">
|
||||
<div [className]="color + ' oval ' + size">{{initials}}</div>
|
||||
<div [className]="colorClass + ' oval ' + size">{{initials}}</div>
|
||||
<div *ngIf="withName" class="clamp-2">{{username || ('initials-avatar.unassigned.label' | translate)}}</div>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { User } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-initials-avatar',
|
||||
templateUrl: './initials-avatar.component.html',
|
||||
styleUrls: ['./initials-avatar.component.scss']
|
||||
})
|
||||
export class InitialsAvatarComponent implements OnInit {
|
||||
export class InitialsAvatarComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
public username: string;
|
||||
public userId: string;
|
||||
|
||||
@Input()
|
||||
public color = 'lightgray-dark';
|
||||
public color = 'lightgray';
|
||||
|
||||
@Input()
|
||||
public size: 'small' | 'large' = 'small';
|
||||
@ -18,21 +20,40 @@ export class InitialsAvatarComponent implements OnInit {
|
||||
@Input()
|
||||
public withName = false;
|
||||
|
||||
constructor() {
|
||||
public _user: User;
|
||||
|
||||
constructor(private readonly _userService: UserService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this._user = this._userService.getUserById(this.userId);
|
||||
}
|
||||
|
||||
public get username(): string {
|
||||
return this._userService.getName(this._user);
|
||||
}
|
||||
|
||||
public get initials(): string {
|
||||
if (!this.username) {
|
||||
if (!this._user) {
|
||||
return '?'
|
||||
}
|
||||
|
||||
return this.username
|
||||
return this._userService.getName(this._user)
|
||||
.split(' ')
|
||||
.filter(value => value !== ' ')
|
||||
.filter((value, idx) => idx < 2)
|
||||
.map((str) => str[0])
|
||||
.join('');
|
||||
}
|
||||
|
||||
public get colorClass() {
|
||||
if (this.color.includes('-')) {
|
||||
return this.color;
|
||||
}
|
||||
const textColor = !this._user || !this._userService.isManager(this._user) ? 'dark' : 'red';
|
||||
return `${this.color}-${textColor}`
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class="rectangle-container" [ngClass]="{ small: small }">
|
||||
<div *ngFor="let rect of config" [className]="'section-wrapper flex-' + rect.length">
|
||||
<div *ngFor="let rect of config" class="section-wrapper" [style]="'flex: ' + rect.length + ';'">
|
||||
<div [className]="'rectangle ' + rect.color "></div>
|
||||
<div *ngIf="rect.label" [ngClass]="labelClass">{{ rect.label }}</div>
|
||||
</div>
|
||||
|
||||
@ -39,39 +39,39 @@
|
||||
.rectangle {
|
||||
height: 4px;
|
||||
|
||||
&.unassigned {
|
||||
&.UNASSIGNED {
|
||||
background-color: $grey-5;
|
||||
}
|
||||
|
||||
&.under-review {
|
||||
&.UNDER_REVIEW {
|
||||
background-color: $yellow-1;
|
||||
}
|
||||
|
||||
&.under-approval {
|
||||
&.UNDER_APPROVAL {
|
||||
background-color: $red-1;
|
||||
}
|
||||
|
||||
&.approved {
|
||||
&.APPROVED {
|
||||
background-color: $blue-2;
|
||||
}
|
||||
|
||||
&.submitted {
|
||||
&.SUBMITTED {
|
||||
background-color: $blue-3;
|
||||
}
|
||||
|
||||
&.efsa {
|
||||
&.EFSA {
|
||||
background-color: $blue-4;
|
||||
}
|
||||
|
||||
&.finished {
|
||||
&.FINISHED {
|
||||
background-color: $green-2;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&.ACTIVE {
|
||||
background-color: $primary;
|
||||
}
|
||||
|
||||
&.archived {
|
||||
&.ARCHIVED {
|
||||
background-color: rgba($red-1, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<textarea formControlName="description" name="description" type="text" rows="5"></textarea>
|
||||
</div>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-form-field class="mt-20">
|
||||
<mat-label>{{'project-listing.add-edit-dialog.form.due-date.label' | translate}}</mat-label>
|
||||
<input matInput [matDatepicker]="picker" formControlName="dueDate">
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {FileDetailsDialogComponent} from './file-details-dialog/file-details-dialog.component';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import {
|
||||
FileStatus,
|
||||
FileUploadControllerService,
|
||||
@ -8,15 +8,15 @@ import {
|
||||
ManualRedactionEntry,
|
||||
Project
|
||||
} from '@redaction/red-ui-http';
|
||||
import {ConfirmationDialogComponent} from '../common/confirmation-dialog/confirmation-dialog.component';
|
||||
import {NotificationService, NotificationType} from '../notification/notification.service';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import {AppStateService, ProjectWrapper} from '../state/app-state.service';
|
||||
import {AddEditProjectDialogComponent} from './add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import {AssignOwnerDialogComponent} from './assign-owner-dialog/assign-owner-dialog.component';
|
||||
import {ProjectDetailsDialogComponent} from './project-details-dialog/project-details-dialog.component';
|
||||
import {ManualRedactionDialogComponent} from './manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import {Annotations} from '@pdftron/webviewer';
|
||||
import { ConfirmationDialogComponent } from '../common/confirmation-dialog/confirmation-dialog.component';
|
||||
import { NotificationService, NotificationType } from '../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AppStateService, ProjectWrapper } from '../state/app-state.service';
|
||||
import { AddEditProjectDialogComponent } from './add-edit-project-dialog/add-edit-project-dialog.component';
|
||||
import { AssignOwnerDialogComponent } from './assign-owner-dialog/assign-owner-dialog.component';
|
||||
import { ProjectDetailsDialogComponent } from './project-details-dialog/project-details-dialog.component';
|
||||
import { ManualRedactionDialogComponent } from './manual-redaction-dialog/manual-redaction-dialog.component';
|
||||
import { Annotations } from '@pdftron/webviewer';
|
||||
|
||||
const dialogConfig = {
|
||||
width: '600px',
|
||||
@ -38,17 +38,19 @@ export class DialogService {
|
||||
|
||||
}
|
||||
|
||||
public openFileDetailsDialog($event: MouseEvent, file: FileStatus) {
|
||||
public openFileDetailsDialog($event: MouseEvent, file: FileStatus): MatDialogRef<FileDetailsDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(FileDetailsDialogComponent, {
|
||||
return this._dialog.open(FileDetailsDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: file
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent, projectId: string, fileId: string, cb?: Function) {
|
||||
public openDeleteFileDialog($event: MouseEvent, projectId: string, fileId: string, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ConfirmationDialogComponent, dialogConfig).afterClosed().subscribe(result => {
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
const file = this._appStateService.getFileById(projectId, fileId);
|
||||
this._fileUploadControllerService.deleteFile(file.projectId, file.fileId).subscribe(async () => {
|
||||
@ -62,28 +64,34 @@ export class DialogService {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openManualRedactionDialog($event: ManualRedactionEntry, cb?: Function) {
|
||||
this._dialog.open(ManualRedactionDialogComponent, {
|
||||
public openManualRedactionDialog($event: ManualRedactionEntry, cb?: Function): MatDialogRef<ManualRedactionDialogComponent> {
|
||||
const ref = this._dialog.open(ManualRedactionDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true,
|
||||
data: $event
|
||||
}).afterClosed().subscribe(result => {
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (cb) {
|
||||
cb(result);
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public acceptSuggestionAnnotation($event: MouseEvent, annotation: Annotations.Annotation, projectId: string, fileId: string) {
|
||||
public acceptSuggestionAnnotation($event: MouseEvent, annotation: Annotations.Annotation, projectId: string, fileId: string): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
|
||||
const parts = annotation.Id.split(':');
|
||||
const annotationId = parts[parts.length - 1];
|
||||
|
||||
this._dialog.open(ConfirmationDialogComponent, dialogConfig)
|
||||
.afterClosed().subscribe(result => {
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService.approveRequest(projectId, fileId, annotationId).subscribe(() => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.confirm-annotation.success.label'), null, NotificationType.SUCCESS);
|
||||
@ -92,18 +100,22 @@ export class DialogService {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotation($event: MouseEvent, annotation: Annotations.Annotation, projectId: string, fileId: string) {
|
||||
public suggestRemoveAnnotation($event: MouseEvent, annotation: Annotations.Annotation, projectId: string, fileId: string): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
|
||||
const parts = annotation.Id.split(':');
|
||||
const annotationId = parts[parts.length - 1];
|
||||
|
||||
this._dialog.open(ConfirmationDialogComponent, {
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, {
|
||||
width: '400px',
|
||||
maxWidth: '90vw'
|
||||
}).afterClosed().subscribe(result => {
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this._manualRedactionControllerService.undo(projectId, fileId, annotationId).subscribe(ok => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.remove-annotation.success.label'), null, NotificationType.SUCCESS);
|
||||
@ -113,63 +125,74 @@ export class DialogService {
|
||||
}
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openEditProjectDialog($event: MouseEvent, project: Project) {
|
||||
public openEditProjectDialog($event: MouseEvent, project: Project): MatDialogRef<AddEditProjectDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AddEditProjectDialogComponent, {
|
||||
return this._dialog.open(AddEditProjectDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true,
|
||||
data: project
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteProjectDialog($event: MouseEvent, project: Project, cb?: Function) {
|
||||
public openDeleteProjectDialog($event: MouseEvent, project: Project, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ConfirmationDialogComponent, dialogConfig)
|
||||
.afterClosed().subscribe(async result => {
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe(async result => {
|
||||
if (result) {
|
||||
await this._appStateService.deleteProject(project);
|
||||
if (cb) cb();
|
||||
}
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAssignProjectMembersAndOwnerDialog($event: MouseEvent, project: Project, cb?: Function) {
|
||||
public openAssignProjectMembersAndOwnerDialog($event: MouseEvent, project: Project, cb?: Function): MatDialogRef<AssignOwnerDialogComponent> {
|
||||
$event?.stopPropagation();
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
const ref = this._dialog.open(AssignOwnerDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: {type: 'project', project: project}
|
||||
}).afterClosed().subscribe(result => {
|
||||
data: { type: 'project', project: project }
|
||||
});
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (result && cb) cb();
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent, file: FileStatus, cb?: Function) {
|
||||
public openAssignFileOwnerDialog($event: MouseEvent, file: FileStatus, cb?: Function): MatDialogRef<AssignOwnerDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(AssignOwnerDialogComponent, {
|
||||
const ref = this._dialog.open(AssignOwnerDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: {type: 'file', file: file}
|
||||
}).afterClosed().subscribe(() => {
|
||||
data: { type: 'file', file: file }
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe(() => {
|
||||
if (cb) cb();
|
||||
});
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openProjectDetailsDialog($event: MouseEvent, project: ProjectWrapper) {
|
||||
public openProjectDetailsDialog($event: MouseEvent, project: ProjectWrapper): MatDialogRef<ProjectDetailsDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
this._dialog.open(ProjectDetailsDialogComponent, {
|
||||
return this._dialog.open(ProjectDetailsDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: project
|
||||
});
|
||||
}
|
||||
|
||||
public openAddProjectDialog(cb?: Function): void {
|
||||
this._dialog.open(AddEditProjectDialogComponent, {
|
||||
public openAddProjectDialog(cb?: Function): MatDialogRef<AddEditProjectDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditProjectDialogComponent, {
|
||||
...dialogConfig,
|
||||
autoFocus: true
|
||||
}).afterClosed().subscribe(result => {
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe(result => {
|
||||
if (result && cb) cb();
|
||||
});
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,11 +7,11 @@
|
||||
|
||||
<div class="dialog-content">
|
||||
|
||||
<div class="file-details">
|
||||
<div class="detail-row"
|
||||
[innerHTML]="'manual-redaction.dialog.content.text.label' | translate:addRedactionRequest">
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<label translate="manual-redaction.dialog.content.text.label"></label>
|
||||
</div>
|
||||
{{ addRedactionRequest.value }}
|
||||
|
||||
<div class="red-input-group">
|
||||
<label translate="manual-redaction.dialog.content.reason.label"></label>
|
||||
@ -23,17 +23,17 @@
|
||||
<textarea formControlName="comment" name="comment" type="text" rows="2"></textarea>
|
||||
</div>
|
||||
|
||||
<mat-form-field >
|
||||
<mat-label>{{'manual-redaction.dialog.content.dictionary.label' | translate}}</mat-label>
|
||||
<div class="red-input-group">
|
||||
<label translate="manual-redaction.dialog.content.dictionary.label"></label>
|
||||
<mat-select formControlName="dictionary">
|
||||
<mat-option *ngFor="let dictionary of dictionaries | async" [value]="dictionary.type">
|
||||
{{dictionary.type}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<mat-checkbox
|
||||
<mat-checkbox color="primary"
|
||||
formControlName="addToDictionary">{{'manual-redaction.dialog.content.dictionary.add.label' | translate}}</mat-checkbox>
|
||||
</div>
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ export class IconsModule {
|
||||
'check', 'close', 'document', 'double-chevron-right', 'download',
|
||||
'edit', 'error', 'folder', 'info', 'lightning', 'logout', 'menu', 'pages',
|
||||
'plus', 'preview', 'refresh', 'report', 'secret', 'sort-asc', 'sort-desc',
|
||||
'status', 'trash', 'user', 'check-alt',"flash"
|
||||
'status', 'trash', 'user', 'check-alt',
|
||||
];
|
||||
|
||||
for (const icon of icons) {
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
</div>
|
||||
<div class="menu right flex-2">
|
||||
<button [matMenuTriggerFor]="menu" mat-button class="arrow-button">
|
||||
<redaction-initials-avatar color="red-white" size="small" [username]="user?.name" [withName]="true"></redaction-initials-avatar>
|
||||
<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">
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
@import "../../../assets/styles/red-mixins";
|
||||
|
||||
.breadcrumbs-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.breadcrumb {
|
||||
text-decoration: none;
|
||||
color: $accent;
|
||||
font-weight: 600;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
|
||||
&:last-child {
|
||||
color: $primary;
|
||||
@include line-clamp(1);
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
vertical-align: middle;
|
||||
width: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,7 +126,11 @@
|
||||
</div>
|
||||
|
||||
<div class="right-content">
|
||||
<div class="pages" #quickNavigation>
|
||||
<div class="pages" [class.activePanel]="pagesPanelActive"
|
||||
tabindex="0"
|
||||
(keyup)="$event.preventDefault()"
|
||||
(keydown)="$event.preventDefault();"
|
||||
#quickNavigation>
|
||||
<div class="page-number pointer"
|
||||
[ngClass]="{ active: pageNumber === activeViewerPage }"
|
||||
*ngFor="let pageNumber of displayedPages"
|
||||
@ -135,7 +139,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annotations" #annotations>
|
||||
<div class="annotations" [class.activePanel]="!pagesPanelActive" #annotations
|
||||
tabindex="1"
|
||||
(keyup)="$event.preventDefault()"
|
||||
(keydown)="$event.preventDefault();"
|
||||
>
|
||||
<div *ngFor="let page of displayedPages">
|
||||
<div class="page-separator" attr.anotation-page-header="{{page}}">
|
||||
<span class="all-caps-label"><span translate="page"></span> {{page}}</span>
|
||||
|
||||
@ -62,12 +62,11 @@ redaction-pdf-viewer {
|
||||
|
||||
.pages, .annotations {
|
||||
overflow-y: scroll;
|
||||
@include no-scroll-bar();
|
||||
outline: none;
|
||||
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
background: transparent; /* Chrome/Safari/Webkit */
|
||||
&.activePanel {
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
import {ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {ManualRedactionEntry, ReanalysisControllerService} from '@redaction/red-ui-http';
|
||||
import {AppStateService} from '../../../state/app-state.service';
|
||||
import {Annotations, WebViewerInstance} from '@pdftron/webviewer';
|
||||
import {PdfViewerComponent} from '../pdf-viewer/pdf-viewer.component';
|
||||
import {AnnotationUtils} from '../../../utils/annotation-utils';
|
||||
import {UserService} from '../../../user/user.service';
|
||||
import {debounce} from '../../../utils/debounce';
|
||||
import { ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ManualRedactionEntry, ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
|
||||
import { AnnotationUtils } from '../../../utils/annotation-utils';
|
||||
import { UserService } from '../../../user/user.service';
|
||||
import { debounce } from '../../../utils/debounce';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import {AnnotationFilters} from '../../../utils/types';
|
||||
import {FiltersService} from '../service/filters.service';
|
||||
import {FileDownloadService} from '../service/file-download.service';
|
||||
import {saveAs} from 'file-saver';
|
||||
import {FileType} from '../model/file-type';
|
||||
import {DialogService} from '../../../dialogs/dialog.service';
|
||||
import { AnnotationFilters } from '../../../utils/types';
|
||||
import { FiltersService } from '../service/filters.service';
|
||||
import { FileDownloadService } from '../service/file-download.service';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { FileType } from '../model/file-type';
|
||||
import { DialogService } from '../../../dialogs/dialog.service';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-preview-screen',
|
||||
@ -24,6 +25,8 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
|
||||
private projectId: string;
|
||||
private _activeViewer: 'ANNOTATED' | 'REDACTED' = 'ANNOTATED';
|
||||
private instance: WebViewerInstance;
|
||||
private _dialogRef: MatDialogRef<any>;
|
||||
|
||||
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
||||
@ViewChild('annotations') private _annotationsElement: ElementRef;
|
||||
@ -36,8 +39,8 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
public displayedAnnotations: { [key: number]: { annotations: Annotations.Annotation[] } } = {};
|
||||
public selectedAnnotation: Annotations.Annotation;
|
||||
public filters: AnnotationFilters;
|
||||
public expandedFilters: AnnotationFilters = {hint: false};
|
||||
private instance: WebViewerInstance;
|
||||
public expandedFilters: AnnotationFilters = { hint: false };
|
||||
public pagesPanelActive = true;
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
@ -83,11 +86,11 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
localStorage.clear();
|
||||
this._reloadFiles();
|
||||
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
|
||||
if(fileStatus.fileId === this.fileId) {
|
||||
if (fileStatus.fileId === this.fileId) {
|
||||
console.log(fileStatus);
|
||||
this._reloadFiles();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private _reloadFiles() {
|
||||
@ -102,7 +105,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
public openFileDetailsDialog($event: MouseEvent) {
|
||||
this._dialogService.openFileDetailsDialog($event, this.appStateService.activeFile);
|
||||
this._dialogRef = this._dialogService.openFileDetailsDialog($event, this.appStateService.activeFile);
|
||||
}
|
||||
|
||||
public reanalyseFile($event: MouseEvent) {
|
||||
@ -113,17 +116,16 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
public openDeleteFileDialog($event: MouseEvent) {
|
||||
this._dialogService.openDeleteFileDialog($event, this.projectId, this.fileId, () => {
|
||||
this._dialogRef = this._dialogService.openDeleteFileDialog($event, this.projectId, this.fileId, () => {
|
||||
this._router.navigate([`/ui/projects/${this.projectId}`]);
|
||||
});
|
||||
}
|
||||
|
||||
public openAssignFileOwnerDialog($event: MouseEvent) {
|
||||
const file = this.appStateService.getFileById(this.projectId, this.fileId);
|
||||
this._dialogService.openAssignFileOwnerDialog($event, file);
|
||||
this._dialogRef = this._dialogService.openAssignFileOwnerDialog($event, file);
|
||||
}
|
||||
|
||||
|
||||
public get activeViewer() {
|
||||
return this.instance;
|
||||
}
|
||||
@ -161,13 +163,12 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
|
||||
public openManualRedactionDialog($event: ManualRedactionEntry) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogService.openManualRedactionDialog($event, () => {
|
||||
this._dialogRef = this._dialogService.openManualRedactionDialog($event, () => {
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
get activeViewerPage() {
|
||||
return this.instance.docViewer.getCurrentPage();
|
||||
}
|
||||
@ -212,13 +213,13 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
|
||||
public acceptSuggestionAnnotation($event: MouseEvent, annotation: Annotations.Annotation) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogService.acceptSuggestionAnnotation($event, annotation, this.projectId, this.fileId);
|
||||
this._dialogRef = this._dialogService.acceptSuggestionAnnotation($event, annotation, this.projectId, this.fileId);
|
||||
});
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotation($event: MouseEvent, annotation: Annotations.Annotation) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogService.suggestRemoveAnnotation($event, annotation, this.projectId, this.fileId);
|
||||
this._dialogRef = this._dialogService.suggestRemoveAnnotation($event, annotation, this.projectId, this.fileId);
|
||||
});
|
||||
}
|
||||
|
||||
@ -272,10 +273,41 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent) {
|
||||
$event.preventDefault();
|
||||
const keyArray = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
|
||||
if (!keyArray.includes($event.key) || this._dialogRef?.getState() === MatDialogState.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event.key === 'ArrowLeft' || $event.key === 'ArrowRight') {
|
||||
this.pagesPanelActive = !this.pagesPanelActive;
|
||||
this._changeDetectorRef.detectChanges();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pagesPanelActive) {
|
||||
this._navigateAnnotations($event);
|
||||
} else {
|
||||
this._navigatePages($event);
|
||||
}
|
||||
}
|
||||
|
||||
private _navigateAnnotations($event: KeyboardEvent) {
|
||||
if (!this.selectedAnnotation || this.activeViewerPage !== this.selectedAnnotation.getPageNumber()) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
if (pageIdx !== -1) { // Displayed page has annotations
|
||||
this.selectAnnotation(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
} else { // Displayed page doesn't have annotations
|
||||
if ($event.key === 'ArrowDown') {
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
this.selectAnnotation(this.displayedAnnotations[nextPage].annotations[0]);
|
||||
} else {
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
||||
this.selectAnnotation(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.selectedAnnotation) {
|
||||
this.selectAnnotation(this.displayedAnnotations[this.displayedPages[0]].annotations[0]);
|
||||
} else {
|
||||
const page = this.selectedAnnotation.getPageNumber();
|
||||
const pageIdx = this.displayedPages.indexOf(page);
|
||||
@ -289,9 +321,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
const nextPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx + 1]].annotations;
|
||||
this.selectAnnotation(nextPageAnnotations[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($event.key === 'ArrowUp') {
|
||||
} else {
|
||||
if (idx !== 0) { // If not first item in page
|
||||
this.selectAnnotation(annotationsOnPage[idx - 1]);
|
||||
} else if (pageIdx) { // If not first page
|
||||
@ -302,6 +332,58 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private _navigatePages($event: KeyboardEvent) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
|
||||
if ($event.key === 'ArrowDown') {
|
||||
if (pageIdx !== -1) { // If active page has annotations
|
||||
if (pageIdx !== this.displayedPages.length - 1) {
|
||||
this.selectPage(this.displayedPages[pageIdx + 1]);
|
||||
}
|
||||
} else { // If active page doesn't have annotations
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
if (nextPage) {
|
||||
this.selectPage(nextPage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pageIdx !== -1) { // If active page has annotations
|
||||
if (pageIdx !== 0) {
|
||||
this.selectPage(this.displayedPages[pageIdx - 1]);
|
||||
}
|
||||
} else { // If active page doesn't have annotations
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
if (prevPage) {
|
||||
this.selectPage(prevPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _nextPageWithAnnotations() {
|
||||
let idx = 0;
|
||||
for (const page of this.displayedPages) {
|
||||
if (page > this.activeViewerPage) {
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return idx < this.displayedPages.length ? this.displayedPages[idx] : null;
|
||||
}
|
||||
|
||||
private _prevPageWithAnnotations() {
|
||||
let idx = this.displayedPages.length - 1;
|
||||
for (const page of this.displayedPages.reverse()) {
|
||||
if (page < this.activeViewerPage) {
|
||||
this.selectPage(this.displayedPages[idx]);
|
||||
this._scrollAnnotations();
|
||||
break;
|
||||
}
|
||||
--idx;
|
||||
}
|
||||
return idx >= 0 ? this.displayedPages[idx] : null;
|
||||
}
|
||||
|
||||
viewerPageChanged($event: number) {
|
||||
this._scrollViews();
|
||||
this._changeDetectorRef.detectChanges();
|
||||
@ -316,9 +398,9 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
// handle comments
|
||||
annotations.forEach(a => {
|
||||
a['comments'] = a['Mi'] ? a['Mi'].map(m => {
|
||||
return {value: m.eC}
|
||||
return { value: m.eC };
|
||||
}) : [];
|
||||
})
|
||||
});
|
||||
|
||||
AnnotationUtils.addAnnotations(this.annotations, annotations);
|
||||
this.applyFilters();
|
||||
|
||||
@ -184,14 +184,14 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
public selectAnnotation(annotation: Annotations.Annotation) {
|
||||
this.instance.annotManager.deselectAllAnnotations();
|
||||
this.instance.annotManager.selectAnnotation(annotation);
|
||||
this.instance.docViewer.displayPageLocation(
|
||||
annotation.getPageNumber(),
|
||||
0,
|
||||
annotation.getY() - 100);
|
||||
this.navigateToPage(annotation.getPageNumber());
|
||||
}
|
||||
|
||||
public navigateToPage(pageNumber: number) {
|
||||
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0);
|
||||
const activePage = this.instance.docViewer.getCurrentPage();
|
||||
if (activePage !== pageNumber) {
|
||||
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<div class="left-container">
|
||||
<div class="table-header">
|
||||
<span class="all-caps-label">
|
||||
{{'project-listing.table-header.title.label'| translate:{length: appStateService.allProjects?.length || 0} }}
|
||||
{{'project-listing.table-header.title.label'| translate:{ length: appStateService.allProjects?.length || 0 } }}
|
||||
</span>
|
||||
<div class="actions">
|
||||
<div translate="project-listing.table-header.bulk-select.label"></div>
|
||||
@ -45,70 +45,74 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-col-names">
|
||||
<div class="flex-2 small-label min-width" translate="project-listing.table-col-names.name.label"></div>
|
||||
<div class="flex-1 small-label min-width" translate="project-listing.table-col-names.owner.label"></div>
|
||||
<div class="status small-label flex-end min-width"
|
||||
translate="project-listing.table-col-names.status.label"></div>
|
||||
</div>
|
||||
<div class="grid-container">
|
||||
<div class="table-col-name">
|
||||
<span class="small-label" translate="project-listing.table-col-names.name.label"></span>
|
||||
</div>
|
||||
<div class="table-col-name">
|
||||
<span class="small-label" translate="project-listing.table-col-names.owner.label"></span>
|
||||
</div>
|
||||
<div class="table-col-name flex-end">
|
||||
<span class="small-label" translate="project-listing.table-col-names.status.label"></span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="appStateService.allProjects?.length === 0 " translate="project-listing.no-projects.label"></div>
|
||||
<div class="no-data" *ngIf="appStateService.allProjects?.length === 0 "
|
||||
translate="project-listing.no-projects.label"></div>
|
||||
|
||||
<div *ngFor="let pw of appStateService.allProjects | sortBy:sortingOption.order:sortingOption.column"
|
||||
[routerLink]="'/ui/projects/'+pw.project.projectId"
|
||||
class="table-item pointer"
|
||||
>
|
||||
<div class="flex-2">
|
||||
<div class="table-item-title table-item-title--large">
|
||||
{{pw.project.projectName}}
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:document"></mat-icon>
|
||||
{{documentCount(pw)}}</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:user"></mat-icon>
|
||||
{{userCount(pw)}}</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
{{pw.project.date | date:'mediumDate'}}
|
||||
<div class="table-item pointer"
|
||||
[routerLink]="'/ui/projects/'+pw.project.projectId"
|
||||
*ngFor="let pw of appStateService.allProjects | sortBy:sortingOption.order:sortingOption.column"
|
||||
>
|
||||
<div>
|
||||
<div class="table-item-title table-item-title--large">
|
||||
{{pw.project.projectName}}
|
||||
</div>
|
||||
<div *ngIf="pw.project.dueDate">
|
||||
<mat-icon svgIcon="red:flash"></mat-icon>
|
||||
{{pw.project.dueDate | date:'mediumDate'}}
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:document"></mat-icon>
|
||||
{{documentCount(pw)}}</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:user"></mat-icon>
|
||||
{{userCount(pw)}}</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
{{pw.project.date | date:'mediumDate'}}
|
||||
</div>
|
||||
<div *ngIf="pw.project.dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
{{pw.project.dueDate | date:'mediumDate'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<redaction-initials-avatar [userId]="pw.project.ownerId"
|
||||
withName="true"
|
||||
></redaction-initials-avatar>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<redaction-status-bar
|
||||
[config]="getProjectStatusConfig(pw)"
|
||||
></redaction-status-bar>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button mat-icon-button color="accent" (click)="openDeleteProjectDialog($event,pw.project)"
|
||||
[matTooltip]="'project-listing.delete.action.label'|translate">
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" (click)="openProjectDetailsDialog($event,pw)"
|
||||
[matTooltip]="'project-listing.report.action.label'|translate">
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button color="accent" (click)="openAssignProjectOwnerDialog($event,pw.project)" mat-icon-button
|
||||
[matTooltip]="'project-listing.assign.action.label'|translate">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<redaction-initials-avatar [username]="getOwnerName(pw)"
|
||||
color="lightgray-red"
|
||||
withName="true"
|
||||
></redaction-initials-avatar>
|
||||
</div>
|
||||
<div class="status">
|
||||
<redaction-status-bar
|
||||
[config]="[{ color: 'under-review', length: 2}, { length: 1, color: 'finished'}]"
|
||||
></redaction-status-bar>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button mat-icon-button color="accent" (click)="openDeleteProjectDialog($event,pw.project)"
|
||||
[matTooltip]="'project-listing.delete.action.label'|translate">
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="accent" (click)="openProjectDetailsDialog($event,pw)"
|
||||
[matTooltip]="'project-listing.report.action.label'|translate">
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<!-- <button mat-icon-button (click)="editProject($event,pw.project)">-->
|
||||
<!-- <mat-icon svgIcon="red:edit"></mat-icon>-->
|
||||
<!-- </button>-->
|
||||
<button color="accent" (click)="openAssignProjectOwnerDialog($event,pw.project)" mat-icon-button
|
||||
[matTooltip]="'project-listing.assign.action.label'|translate">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="right-fixed-container">
|
||||
|
||||
@ -11,19 +11,15 @@
|
||||
.left-container {
|
||||
width: calc(100vw - #{$right-container-width} - 130px);
|
||||
|
||||
.table-item {
|
||||
&:hover {
|
||||
.status {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-subtitle {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.grid-container {
|
||||
grid-template-columns: 2fr 1fr auto;
|
||||
}
|
||||
|
||||
.status, .action-buttons {
|
||||
.stats-subtitle {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.status-container {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,10 +68,6 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
return this.appStateService.allProjects.length - this.activeProjects;
|
||||
}
|
||||
|
||||
public getOwnerName(pw: ProjectWrapper) {
|
||||
return this._userService.getNameForId(pw.project.ownerId);
|
||||
}
|
||||
|
||||
public documentCount(project: ProjectWrapper) {
|
||||
return project.files.length;
|
||||
}
|
||||
@ -99,4 +95,18 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
public openAssignProjectOwnerDialog($event: MouseEvent, project: Project) {
|
||||
this._dialogService.openAssignProjectMembersAndOwnerDialog($event, project);
|
||||
}
|
||||
|
||||
public getProjectStatusConfig(pw: ProjectWrapper) {
|
||||
const obj = pw.files.reduce((acc, file) => {
|
||||
const status = file.status;
|
||||
if (!acc[status]) {
|
||||
acc[status] = 1;
|
||||
} else {
|
||||
acc[status]++;
|
||||
}
|
||||
return acc;
|
||||
}, {})
|
||||
|
||||
return Object.keys(obj).sort().map(status => ({length: obj[status], color: status}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,75 +51,92 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-container">
|
||||
<!-- Table column names-->
|
||||
<div class="table-col-name"></div>
|
||||
|
||||
<div class="table-col-names">
|
||||
<div class="select-oval placeholder"></div>
|
||||
<div class="flex-6 small-label min-width" translate="project-overview.table-col-names.name.label"></div>
|
||||
<div class="flex-4 min-width pointer" (click)="toggleSortByAddedOn()">
|
||||
<div class="table-col-name">
|
||||
<span class="small-label" translate="project-overview.table-col-names.name.label"></span>
|
||||
</div>
|
||||
|
||||
<div class="table-col-name pointer" (click)="toggleSortByAddedOn()">
|
||||
<span class="small-label" translate="project-overview.table-col-names.added-on.label"></span>
|
||||
<div class="sort-arrows-container">
|
||||
<mat-icon svgIcon="red:arrow-up" [color]="sortingOption === sortingOptions[0] ? 'primary' : 'currentColor'"></mat-icon>
|
||||
<mat-icon svgIcon="red:arrow-down" [color]="sortingOption === sortingOptions[1] ? 'primary' : 'currentColor'"></mat-icon>
|
||||
<mat-icon svgIcon="red:arrow-up"
|
||||
[color]="sortingOption === sortingOptions[0] ? 'primary' : 'currentColor'"></mat-icon>
|
||||
<mat-icon svgIcon="red:arrow-down"
|
||||
[color]="sortingOption === sortingOptions[1] ? 'primary' : 'currentColor'"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-2 small-label min-width" translate="project-overview.table-col-names.needs-work.label"></div>
|
||||
<div class="flex-2 small-label min-width" translate="project-overview.table-col-names.assigned-to.label"></div>
|
||||
<div class="flex-1 small-label flex-end min-width"
|
||||
translate="project-overview.table-col-names.status.label"></div>
|
||||
</div>
|
||||
|
||||
<div class="table-item"
|
||||
[class.pointer]="canOpenFile(fileStatus.status)"
|
||||
*ngFor="let fileStatus of appStateService.activeProject.files | sortBy: sortingOption.order:sortingOption.column; trackBy:fileId"
|
||||
[routerLink]="canOpenFile(fileStatus.status) ? ['/ui/projects/'+activeProject.projectId+'/file/'+fileStatus.fileId] : []">
|
||||
|
||||
<div class="select-oval"
|
||||
[class.active]="isFileSelected(fileStatus)"
|
||||
(click)="toggleFileSelected($event, fileStatus)"></div>
|
||||
|
||||
<div class="flex-6 table-item-title min-width" [matTooltip]="'['+fileStatus.status+'] '+fileStatus.filename ">
|
||||
{{ fileStatus.filename }}
|
||||
<div class="table-col-name">
|
||||
<span class="small-label" translate="project-overview.table-col-names.needs-work.label"></span>
|
||||
</div>
|
||||
|
||||
<div class="small-label flex-4 min-width">
|
||||
{{ fileStatus.added | date:'d MMM. yyyy, hh:mm a' }}
|
||||
<div class="table-col-name">
|
||||
<span class="small-label" translate="project-overview.table-col-names.assigned-to.label"></span>
|
||||
</div>
|
||||
|
||||
<div class="flex-2 min-width needs-work">
|
||||
<redaction-annotation-icon type="redaction"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon type="hint"></redaction-annotation-icon>
|
||||
<div class="table-col-name flex-end">
|
||||
<span class="small-label" translate="project-overview.table-col-names.status.label"></span>
|
||||
</div>
|
||||
|
||||
<div class="small-label flex-2 assigned-to min-width">
|
||||
<redaction-initials-avatar
|
||||
withName="true"
|
||||
[username]="getFileOwnerUsername(fileStatus)"
|
||||
></redaction-initials-avatar>
|
||||
</div>
|
||||
<div class="table-item"
|
||||
[class.pointer]="canOpenFile(fileStatus.status)"
|
||||
*ngFor="let fileStatus of appStateService.activeProject.files | sortBy: sortingOption.order:sortingOption.column; trackBy:fileId"
|
||||
[routerLink]="canOpenFile(fileStatus.status) ? ['/ui/projects/'+activeProject.projectId+'/file/'+fileStatus.fileId] : []">
|
||||
|
||||
<div class="status-container flex-1 min-width">
|
||||
<redaction-status-bar
|
||||
[config]="[{ color: (fileStatus.status === 'PROCESSED' ? 'finished': fileStatus.status ==='ERROR'?'under-approval' : 'under-review'), length: 1 }]"
|
||||
></redaction-status-bar>
|
||||
</div>
|
||||
<div class="pr-0">
|
||||
<div class="select-oval"
|
||||
[class.active]="isFileSelected(fileStatus)"
|
||||
(click)="toggleFileSelected($event, fileStatus)"></div>
|
||||
</div>
|
||||
|
||||
<div class="table-item-title" [matTooltip]="'['+fileStatus.status+'] '+fileStatus.filename ">
|
||||
{{ fileStatus.filename }}
|
||||
</div>
|
||||
|
||||
<div class="small-label">
|
||||
{{ fileStatus.added | date:'d MMM. yyyy, hh:mm a' }}
|
||||
</div>
|
||||
|
||||
<div class="needs-work">
|
||||
<redaction-annotation-icon type="redaction"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon type="hint"></redaction-annotation-icon>
|
||||
</div>
|
||||
|
||||
<div class="assigned-to">
|
||||
<redaction-initials-avatar
|
||||
withName="true"
|
||||
[userId]="fileStatus.currentReviewer"
|
||||
></redaction-initials-avatar>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<redaction-status-bar
|
||||
[config]="[{ color: (fileStatus.status === 'PROCESSED' ? 'finished': fileStatus.status ==='ERROR'?'under-approval' : 'under-review'), length: 1 }]"
|
||||
></redaction-status-bar>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button (click)="openDeleteFileDialog($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.delete.action.label'|translate">
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button (click)="$event.stopPropagation()" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.report.action.label'|translate">
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button (click)="openAssignFileOwnerDialog($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.assign.action.label'|translate">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
<button (click)="reanalyseFile($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.bar-charts.action.label'|translate">
|
||||
<mat-icon svgIcon="red:analyse"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons flex-3 min-width">
|
||||
<button (click)="openDeleteFileDialog($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.delete.action.label'|translate">
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<button (click)="$event.stopPropagation()" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.report.action.label'|translate">
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
<button (click)="openAssignFileOwnerDialog($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.assign.action.label'|translate">
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
<button (click)="reanalyseFile($event,fileStatus)" color="accent" mat-icon-button
|
||||
[matTooltip]="'project-overview.bar-charts.action.label'|translate">
|
||||
<mat-icon svgIcon="red:analyse"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -151,7 +168,7 @@
|
||||
{{ appStateService.activeProject.project.date | date:'d MMM. yyyy' }}
|
||||
</div>
|
||||
<div *ngIf="appStateService.activeProject.project.dueDate">
|
||||
<mat-icon svgIcon="red:flash"></mat-icon>
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
{{appStateService.activeProject.project.dueDate | date:'mediumDate'}}
|
||||
</div>
|
||||
</div>
|
||||
@ -161,9 +178,8 @@
|
||||
</div>
|
||||
|
||||
<div class="owner flex-row mt-20">
|
||||
<redaction-initials-avatar [username]="ownerName"
|
||||
<redaction-initials-avatar [userId]="activeProject.ownerId"
|
||||
size="large"
|
||||
color="lightgray-red"
|
||||
withName="true"
|
||||
></redaction-initials-avatar>
|
||||
</div>
|
||||
@ -175,8 +191,8 @@
|
||||
<div class="project-team mt-20">
|
||||
<div class="all-caps-label" translate="project-overview.project-details.project-team.label"></div>
|
||||
<div class="flex mt-20 members-container">
|
||||
<div *ngFor="let username of displayMembers" class="member">
|
||||
<redaction-initials-avatar [username]="username" size="large"></redaction-initials-avatar>
|
||||
<div *ngFor="let userId of displayMembers" class="member">
|
||||
<redaction-initials-avatar [userId]="userId" size="large"></redaction-initials-avatar>
|
||||
</div>
|
||||
<div class="member" *ngIf="overflowCount">
|
||||
<div class="oval large white-dark">+{{overflowCount}}</div>
|
||||
|
||||
@ -4,10 +4,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.min-width {
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.select-all-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
@ -18,14 +14,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.pr-0 {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.select-oval {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $grey-5;
|
||||
background-color: $white;
|
||||
padding: 0;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
@ -37,25 +35,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
.table-item {
|
||||
.needs-work {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
}
|
||||
.grid-container {
|
||||
grid-template-columns: auto 3fr 2fr 1fr 2fr auto;
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.assigned-to, .status-container {
|
||||
display: none;
|
||||
.table-item {
|
||||
.table-item-title {
|
||||
line-height: 80px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
margin-left: 32px;
|
||||
.needs-work {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {FileStatus, ReanalysisControllerService, StatusControllerService} from '@redaction/red-ui-http';
|
||||
import {NotificationService} from '../../notification/notification.service';
|
||||
import {AppStateService} from '../../state/app-state.service';
|
||||
import {FileDropOverlayService} from '../../upload/file-drop/service/file-drop-overlay.service';
|
||||
import {FileUploadModel} from '../../upload/model/file-upload.model';
|
||||
import {FileUploadService} from '../../upload/file-upload.service';
|
||||
import {UploadStatusOverlayService} from '../../upload/upload-status-dialog/service/upload-status-overlay.service';
|
||||
import {UserService} from '../../user/user.service';
|
||||
import {SortingOption} from '../../utils/types';
|
||||
import {DoughnutChartConfig} from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import {groupBy} from '../../utils/functions';
|
||||
import {DialogService} from '../../dialogs/dialog.service';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FileStatus, ReanalysisControllerService, StatusControllerService } from '@redaction/red-ui-http';
|
||||
import { NotificationService } from '../../notification/notification.service';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { FileDropOverlayService } from '../../upload/file-drop/service/file-drop-overlay.service';
|
||||
import { FileUploadModel } from '../../upload/model/file-upload.model';
|
||||
import { FileUploadService } from '../../upload/file-upload.service';
|
||||
import { UploadStatusOverlayService } from '../../upload/upload-status-dialog/service/upload-status-overlay.service';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { SortingOption } from '../../utils/types';
|
||||
import { DoughnutChartConfig } from '../../components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { groupBy } from '../../utils/functions';
|
||||
import { DialogService } from '../../dialogs/dialog.service';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -23,11 +23,11 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
private _selectedFileIds: string[] = [];
|
||||
|
||||
public sortingOptions: SortingOption[] = [
|
||||
{label: 'project-overview.sorting.recent.label', order: 'desc', column: 'lastUpdated'},
|
||||
{label: 'project-overview.sorting.oldest.label', order: 'asc', column: 'lastUpdated'},
|
||||
{label: 'project-overview.sorting.alphabetically.label', order: 'asc', column: 'filename'},
|
||||
{label: 'project-overview.sorting.number-of-pages.label', order: 'asc', column: 'numberOfPages'},
|
||||
{label: 'project-overview.sorting.number-of-analyses.label', order: 'desc', column: 'numberOfAnalyses'}
|
||||
{ label: 'project-overview.sorting.recent.label', order: 'desc', column: 'lastUpdated' },
|
||||
{ label: 'project-overview.sorting.oldest.label', order: 'asc', column: 'lastUpdated' },
|
||||
{ label: 'project-overview.sorting.alphabetically.label', order: 'asc', column: 'filename' },
|
||||
{ label: 'project-overview.sorting.number-of-pages.label', order: 'asc', column: 'numberOfPages' },
|
||||
{ label: 'project-overview.sorting.number-of-analyses.label', order: 'desc', column: 'numberOfAnalyses' }
|
||||
];
|
||||
public sortingOption: SortingOption = this.sortingOptions[0];
|
||||
public documentsChartData: DoughnutChartConfig[] = [];
|
||||
@ -69,20 +69,12 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
return this._userService.user;
|
||||
}
|
||||
|
||||
public get members() {
|
||||
return this.activeProject.memberIds.map(m => this._userService.getName(this._userService.getUserById(m)));
|
||||
}
|
||||
|
||||
public get displayMembers() {
|
||||
return this.members.slice(0, 6);
|
||||
return this.activeProject.memberIds.slice(0, 6);
|
||||
}
|
||||
|
||||
public get overflowCount() {
|
||||
return this.members.length > 6 ? this.members.length - 6 : 0;
|
||||
}
|
||||
|
||||
public get ownerName() {
|
||||
return this._userService.getNameForId(this.activeProject.ownerId);
|
||||
return this.activeProject.memberIds.length > 6 ? this.activeProject.memberIds.length - 6 : 0;
|
||||
}
|
||||
|
||||
private _getFileStatus() {
|
||||
@ -95,7 +87,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
const groups = groupBy(this.appStateService.activeProject.files, 'status');
|
||||
this.documentsChartData = [];
|
||||
for (const key of Object.keys(groups)) {
|
||||
this.documentsChartData.push({value: groups[key].length, color: key, label: key});
|
||||
this.documentsChartData.push({ value: groups[key].length, color: key, label: key });
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +110,8 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public areAllFilesSelected() {
|
||||
return this._selectedFileIds.length === this.appStateService.activeProject.files.length;
|
||||
return this.appStateService.activeProject.files.length !== 0 &&
|
||||
this._selectedFileIds.length === this.appStateService.activeProject.files.length;
|
||||
}
|
||||
|
||||
public isFileSelected(file: FileStatus) {
|
||||
@ -189,10 +182,6 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
return fileStatus === 'PROCESSING' || fileStatus === 'REVIEWED' || true;
|
||||
}
|
||||
|
||||
public getFileOwnerUsername(fileStatus: FileStatus) {
|
||||
return this._userService.getNameForId(fileStatus.currentReviewer);
|
||||
}
|
||||
|
||||
public toggleSortByAddedOn() {
|
||||
const sortedByRecent: boolean = (this.sortingOption === this.sortingOptions[0]);
|
||||
this.sortingOption = sortedByRecent ? this.sortingOptions[1] : this.sortingOptions[0];
|
||||
|
||||
@ -128,7 +128,7 @@ export class AppStateService {
|
||||
const projects = await this._projectControllerService.getProjects().toPromise();
|
||||
if (projects) {
|
||||
|
||||
let mappedProjects = projects.map(p => {
|
||||
const mappedProjects = projects.map(p => {
|
||||
return new ProjectWrapper(p, this._getExistingFiles(p));
|
||||
});
|
||||
|
||||
@ -150,9 +150,9 @@ export class AppStateService {
|
||||
const files = await this._statusControllerService.getProjectStatus(project.project.projectId).toPromise();
|
||||
const oldFiles = [...project.files];
|
||||
|
||||
for (let file of files) {
|
||||
for (const file of files) {
|
||||
let found = false;
|
||||
for (let oldFile of oldFiles) {
|
||||
for (const oldFile of oldFiles) {
|
||||
if (oldFile.fileId === file.fileId) {
|
||||
// emit when analysis count changed
|
||||
if (oldFile.numberOfAnalyses !== file.numberOfAnalyses) {
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
},
|
||||
"content": {
|
||||
"text": {
|
||||
"label": "<strong>Selected Text: </strong> {{value}}"
|
||||
"label": "Selected text:"
|
||||
},
|
||||
"dictionary": {
|
||||
"add": {
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
<svg height="511pt" viewBox="-91 0 511 511.99948" width="511pt" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="m315.5 211h-124.214844l107.253906-188.585938c2.640626-4.640624 2.609376-10.339843-.074218-14.957031-2.683594-4.617187-7.625-7.457031-12.964844-7.457031h-180c-6.460938 0-12.199219 4.140625-14.234375 10.273438l-90 270.996093c-1.519531 4.574219-.75 9.597657 2.070313 13.507813 2.820312 3.90625 7.34375 6.222656 12.164062 6.222656h127.292969l-81.089844 190.113281c-2.886719 6.769531-.441406 14.628907 5.777344 18.5625 6.21875 3.933594 14.371093 2.773438 19.25-2.730469l240-271c8.546875-9.65625 1.679687-24.945312-11.230469-24.945312zm-189.921875 206.832031 53.71875-125.945312c4.210937-9.875-3.039063-20.886719-13.796875-20.886719h-129.214844l80.039063-241h143.386719l-107.25 188.582031c-5.671876 9.972657 1.535156 22.417969 13.039062 22.417969h116.679688zm0 0"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 902 B |
@ -10,3 +10,19 @@
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.mat-checkbox-layout {
|
||||
.mat-checkbox-inner-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mat-checkbox-label {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
color: $accent;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
.mat-dialog-container {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
position: relative;
|
||||
min-height: 80px;
|
||||
|
||||
@ -1,53 +1,63 @@
|
||||
@import "red-variables";
|
||||
@import "red-mixins";
|
||||
|
||||
.red-input-group {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 13px;
|
||||
|
||||
label {
|
||||
height: 14px;
|
||||
opacity: 0.6;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
input, textarea, mat-select {
|
||||
box-sizing: border-box;
|
||||
width: 322px;
|
||||
padding-left: 11px;
|
||||
padding-right: 11px;
|
||||
border: 1px solid $grey-5;
|
||||
font-family: Inter, sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
line-height: 14px;
|
||||
margin-bottom: 5px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
margin-top: 3px;
|
||||
min-height: 34px;
|
||||
|
||||
&:focus {
|
||||
border-color: $grey-1;
|
||||
}
|
||||
}
|
||||
|
||||
mat-select {
|
||||
width: 220px;
|
||||
|
||||
.mat-select-trigger {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.mat-select-value {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none;
|
||||
box-sizing: border-box;
|
||||
padding-left: 11px;
|
||||
padding-right: 11px;
|
||||
width: 300px;
|
||||
border: 1px solid $grey-3;
|
||||
opacity: 0.4;
|
||||
border-radius: 2px;
|
||||
background-color: #FFFFFF;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
border-color: $grey-1;
|
||||
}
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
@include no-scroll-bar();
|
||||
}
|
||||
|
||||
input {
|
||||
box-sizing: border-box;
|
||||
padding-left: 11px;
|
||||
padding-right: 11px;
|
||||
height: 34px;
|
||||
width: 300px;
|
||||
border: 1px solid $grey-3;
|
||||
opacity: 0.4;
|
||||
border-radius: 2px;
|
||||
background-color: #FFFFFF;
|
||||
outline: none;
|
||||
label {
|
||||
opacity: 0.7;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0;
|
||||
line-height: 14px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&:focus {
|
||||
border-color: $grey-1;
|
||||
&.mat-checkbox-layout {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,8 @@
|
||||
color: $accent;
|
||||
|
||||
.arrow-wrapper {
|
||||
width: 24px;
|
||||
width: 16px;
|
||||
margin-right: 8px;
|
||||
text-align: center;
|
||||
|
||||
mat-icon {
|
||||
@ -24,23 +25,5 @@
|
||||
&.padding-left {
|
||||
padding-left: 64px;
|
||||
}
|
||||
|
||||
.mat-checkbox-layout {
|
||||
margin-left: 4px;
|
||||
|
||||
.mat-checkbox-inner-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.mat-checkbox-label {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
color: $accent;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,3 +8,12 @@
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin no-scroll-bar {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
background: transparent; /* Chrome/Safari/Webkit */
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
@import "red-variables";
|
||||
@import "red-mixins";
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
@ -89,22 +88,6 @@ html, body {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.flex-3 {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.flex-4 {
|
||||
flex: 4;
|
||||
}
|
||||
|
||||
.flex-5 {
|
||||
flex: 5;
|
||||
}
|
||||
|
||||
.flex-6 {
|
||||
flex: 6;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
@ -172,27 +155,6 @@ html, body {
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumbs-container {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.breadcrumb {
|
||||
text-decoration: none;
|
||||
color: $accent;
|
||||
font-weight: 600;
|
||||
|
||||
&:last-child {
|
||||
color: $primary;
|
||||
@include line-clamp(1);
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
vertical-align: middle;
|
||||
width: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
opacity: 0.15;
|
||||
|
||||
@ -16,59 +16,83 @@
|
||||
}
|
||||
}
|
||||
|
||||
.table-col-names {
|
||||
display: flex;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid rgba(226, 228, 233, 0.9);
|
||||
align-items: center;
|
||||
.grid-container {
|
||||
display: grid;
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
padding: 8px 16px;
|
||||
.no-data {
|
||||
grid-column: 1/-1;
|
||||
}
|
||||
|
||||
.table-col-name {
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
border-bottom: 1px solid rgba(226, 228, 233, 0.9);
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
text-transform: uppercase;
|
||||
|
||||
.sort-arrows-container {
|
||||
mat-icon {
|
||||
display: block;
|
||||
width: 6px;
|
||||
height: 11px;
|
||||
.sort-arrows-container {
|
||||
mat-icon {
|
||||
display: block;
|
||||
width: 6px;
|
||||
height: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 80px;
|
||||
border-bottom: 1px solid rgba(226, 228, 233, 0.9);
|
||||
.table-item {
|
||||
display: contents;
|
||||
|
||||
> div {
|
||||
padding: 0 16px;
|
||||
}
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.table-item-title {
|
||||
font-weight: 600;
|
||||
@include line-clamp(1);
|
||||
}
|
||||
.table-item-title {
|
||||
font-weight: 600;
|
||||
@include line-clamp(1);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: none;
|
||||
}
|
||||
> div {
|
||||
height: 80px;
|
||||
border-bottom: 1px solid rgba(226, 228, 233, 0.9);
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #F9FAFB;
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
display: none;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: fit-content;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-left: 100px;
|
||||
padding-right: 8px;
|
||||
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, #F4F5F7 35%);
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> div {
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user