Empty state component & other fixes

This commit is contained in:
Adina Țeudan 2021-04-09 17:54:38 +03:00
parent 60ce807e08
commit a160aae8d9
34 changed files with 489 additions and 493 deletions

View File

@ -89,6 +89,8 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state *ngIf="!logs?.totalHits" icon="red:document" screen="audit-screen"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div class="table-item pointer" *cdkVirtualFor="let log of logs?.data">
<div>

View File

@ -13,141 +13,141 @@
<div class="red-content-inner">
<div class="content-container">
<div *ngIf="dictionaries.length === 0" class="empty-state">
<mat-icon svgIcon="red:dictionary"></mat-icon>
<div class="heading-l" translate="dictionary-listing.no-data.title"></div>
<redaction-icon-button (action)="openAddEditDictionaryDialog()" icon="red:plus" text="dictionary-listing.no-data.action" type="primary">
</redaction-icon-button>
<div class="header-item">
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
[class.active]="areAllDictsSelected"
class="select-oval always-visible"
*ngIf="!areAllDictsSelected && !areSomeDictsSelected"
></div>
<mat-icon *ngIf="areAllDictsSelected" (click)="toggleSelectAll()" class="selection-icon active" svgIcon="red:radio-selected"></mat-icon>
<mat-icon
*ngIf="areSomeDictsSelected && !areAllDictsSelected"
(click)="toggleSelectAll()"
class="selection-icon"
svgIcon="red:radio-indeterminate"
></mat-icon>
</div>
<span class="all-caps-label">
{{ 'dictionary-listing.table-header.title' | translate: { length: displayedDictionaries.length } }}
</span>
<div class="attributes-actions-container">
<redaction-search-input [form]="searchForm" [placeholder]="'dictionary-listing.search'"></redaction-search-input>
<div class="actions">
<redaction-icon-button
*ngIf="permissionsService.isAdmin()"
icon="red:plus"
(action)="openAddEditDictionaryDialog()"
text="dictionary-listing.add-new"
type="primary"
></redaction-icon-button>
</div>
</div>
</div>
<ng-container *ngIf="dictionaries.length > 0">
<div class="header-item">
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
[class.active]="areAllDictsSelected"
class="select-oval always-visible"
*ngIf="!areAllDictsSelected && !areSomeDictsSelected"
></div>
<mat-icon *ngIf="areAllDictsSelected" (click)="toggleSelectAll()" class="selection-icon active" svgIcon="red:radio-selected"></mat-icon>
<mat-icon
*ngIf="areSomeDictsSelected && !areAllDictsSelected"
(click)="toggleSelectAll()"
class="selection-icon"
svgIcon="red:radio-indeterminate"
></mat-icon>
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="!dictionaries.length">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
label="dictionary-listing.table-col-names.type"
column="label"
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
></redaction-table-col-name>
<redaction-table-col-name
label="dictionary-listing.table-col-names.order-of-importance"
column="rank"
class="flex-center"
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
></redaction-table-col-name>
<redaction-table-col-name label="dictionary-listing.table-col-names.hint-redaction" class="flex-center"></redaction-table-col-name>
<div></div>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="!dictionaries.length"
icon="red:dictionary"
(action)="openAddEditDictionaryDialog()"
[showButton]="permissionsService.isAdmin()"
screen="dictionary-listing"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
class="table-item pointer"
*cdkVirtualFor="let dict of displayedDictionaries | sortBy: sortingOption.order:sortingOption.column"
[routerLink]="[dict.type]"
>
<div class="pr-0" (click)="toggleDictSelected($event, dict)">
<div *ngIf="!isDictSelected(dict)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isDictSelected(dict)" svgIcon="red:radio-selected"></mat-icon>
</div>
<span class="all-caps-label">
{{ 'dictionary-listing.table-header.title' | translate: { length: displayedDictionaries.length } }}
</span>
<div class="attributes-actions-container">
<redaction-search-input [form]="searchForm" [placeholder]="'dictionary-listing.search'"></redaction-search-input>
<div class="actions">
<redaction-icon-button
*ngIf="permissionsService.isAdmin()"
icon="red:plus"
(action)="openAddEditDictionaryDialog()"
text="dictionary-listing.add-new"
type="primary"
></redaction-icon-button>
<div>
<div class="color-square" [ngStyle]="{ 'background-color': dict.hexColor }"></div>
<div class="dict-name">
<div class="table-item-title heading">
{{ dict.label }}
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:entries"></mat-icon>
{{ dict.entries?.length }}
</div>
<div *ngIf="!dict.caseInsensitive">
<mat-icon svgIcon="red:case-sensitive"></mat-icon>
{{ 'dictionary-listing.case-sensitive' | translate }}
</div>
</div>
</div>
</div>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<div class="center small-label">
{{ dict.rank }}
</div>
<redaction-table-col-name
label="dictionary-listing.table-col-names.type"
column="label"
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
></redaction-table-col-name>
<div class="center">
<redaction-annotation-icon [dictType]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
</div>
<redaction-table-col-name
label="dictionary-listing.table-col-names.order-of-importance"
column="rank"
class="flex-center"
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
></redaction-table-col-name>
<div class="actions-container">
<div class="action-buttons">
<redaction-circle-button
(action)="openDeleteDictionaryDialog($event, dict)"
*ngIf="permissionsService.isAdmin()"
tooltip="dictionary-listing.action.delete"
type="dark-bg"
icon="red:trash"
>
</redaction-circle-button>
<redaction-table-col-name label="dictionary-listing.table-col-names.hint-redaction" class="flex-center"></redaction-table-col-name>
<div></div>
<redaction-circle-button
(action)="openEditDictionaryDialog($event, dict)"
*ngIf="permissionsService.isAdmin()"
tooltip="dictionary-listing.action.edit"
type="dark-bg"
icon="red:edit"
>
</redaction-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>
</div>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
class="table-item pointer"
*cdkVirtualFor="let dict of displayedDictionaries | sortBy: sortingOption.order:sortingOption.column"
[routerLink]="[dict.type]"
>
<div class="pr-0" (click)="toggleDictSelected($event, dict)">
<div *ngIf="!isDictSelected(dict)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isDictSelected(dict)" svgIcon="red:radio-selected"></mat-icon>
</div>
<div>
<div class="color-square" [ngStyle]="{ 'background-color': dict.hexColor }"></div>
<div class="dict-name">
<div class="table-item-title heading">
{{ dict.label }}
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:entries"></mat-icon>
{{ dict.entries?.length }}
</div>
<div *ngIf="!dict.caseInsensitive">
<mat-icon svgIcon="red:case-sensitive"></mat-icon>
{{ 'dictionary-listing.case-sensitive' | translate }}
</div>
</div>
</div>
</div>
<div class="center small-label">
{{ dict.rank }}
</div>
<div class="center">
<redaction-annotation-icon [dictType]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
</div>
<div class="actions-container">
<div class="action-buttons">
<redaction-circle-button
(action)="openDeleteDictionaryDialog($event, dict)"
*ngIf="permissionsService.isAdmin()"
tooltip="dictionary-listing.action.delete"
type="dark-bg"
icon="red:trash"
>
</redaction-circle-button>
<redaction-circle-button
(action)="openEditDictionaryDialog($event, dict)"
*ngIf="permissionsService.isAdmin()"
tooltip="dictionary-listing.action.edit"
type="dark-bg"
icon="red:edit"
>
</redaction-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>
</div>
</cdk-virtual-scroll-viewport>
</ng-container>
</cdk-virtual-scroll-viewport>
</div>
<div class="right-container" redactionHasScrollbar>
<redaction-simple-doughnut-chart
*ngIf="dictionaries.length"
[config]="chartData"
[strokeWidth]="15"
[radius]="82"

View File

@ -156,6 +156,8 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state *ngIf="!activeFields.length" icon="red:attribute" screen="file-attributes-csv-import"> </redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
<!-- Table lines -->
<div

View File

@ -153,7 +153,7 @@
}
> .content-container {
width: 100%;
width: calc(100% - 527px);
redaction-table-col-name::ng-deep {
> div {

View File

@ -63,7 +63,10 @@ export class FileAttributesCsvImportDialogComponent implements OnInit {
ngOnInit(): void {
setTimeout(() => {
this.cdkVirtualScrollViewport.checkViewportSize();
// Calculate viewport size after dialog is completely expanded
if (this.cdkVirtualScrollViewport) {
this.cdkVirtualScrollViewport.checkViewportSize();
}
}, 500);
}

View File

@ -58,7 +58,7 @@
</div>
</div>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="!attributes.length" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -78,7 +78,7 @@
<div class="scrollbar-placeholder"></div>
</div>
<div *ngIf="noData" class="no-data heading-l" translate="file-attributes-listing.no-data"></div>
<redaction-empty-state *ngIf="!attributes.length" screen="file-attributes-listing" icon="red:attribute"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->

View File

@ -56,10 +56,6 @@ export class FileAttributesListingScreenComponent implements OnInit {
}
}
public get noData(): boolean {
return this.displayedAttributes.length === 0;
}
public get sortingOption(): SortingOption {
return this._sortingService.getSortingOption('file-attributes-listing');
}

View File

@ -1,4 +1,4 @@
<section>
<section *ngIf="viewReady">
<div class="page-header">
<redaction-admin-breadcrumbs [root]="true" class="flex-1"></redaction-admin-breadcrumbs>
@ -96,7 +96,6 @@
</div>
<combo-chart-component
*ngIf="viewReady"
[view]="[1000, 300]"
[scheme]="comboBarScheme"
[legend]="true"

View File

@ -48,7 +48,7 @@
</span>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="!ruleSets.length">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -76,6 +76,8 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state *ngIf="!ruleSets.length" icon="red:template" screen="project-templates-listing"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="100" redactionHasScrollbar>
<div
class="table-item pointer"

View File

@ -44,7 +44,7 @@
{{ 'user-listing.table-header.title' | translate: { length: displayedUsers.length } }}
</span>
<ng-container *ngIf="true || (areSomeUsersSelected && !loading)">
<ng-container *ngIf="areSomeUsersSelected && !loading">
<redaction-circle-button
(action)="bulkDelete()"
[disabled]="!canDeleteSelected"
@ -73,6 +73,8 @@
<div class="scrollbar-placeholder"></div>
</div>
<!--No empty state necessary because there will always be at least the current user.-->
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div class="table-item" *cdkVirtualFor="let user of displayedUsers">

View File

@ -19,6 +19,7 @@ export class IconsModule {
'arrow-up',
'assign',
'assign-me',
'attribute',
'calendar',
'case-sensitive',
'check',

View File

@ -1,34 +0,0 @@
@import '../../../../../assets/styles/red-mixins';
:host {
display: block;
position: absolute;
height: 100%;
background: white;
z-index: 1;
width: 100%;
@include inset-shadow;
}
.section {
padding: 40px;
flex-direction: column;
&:not(:last-child) {
border-bottom: 1px solid $separator;
}
}
p {
height: 48px;
width: 234px;
text-align: center;
color: #9398a0;
}
mat-icon {
width: 60px;
height: 60px;
opacity: 10%;
}

View File

@ -1,12 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'redaction-disabled-for-redaction',
template:
"<div class='section flex-align-items-center'>\n" +
' <mat-icon [svgIcon]="\'red:needs-work\'"></mat-icon>\n' +
" <p class='heading-l'>{{'file-preview.tabs.is-excluded' | translate}}</p>\n" +
'</div>',
styleUrls: ['./disabled-for-redaction.component.scss']
})
export class DisabledForRedactionComponent {}

View File

@ -1,7 +0,0 @@
<div class="empty-state-container">
<div class="heading-xl" translate="project-listing.no-projects"></div>
<button (click)="addProjectRequest.emit()" *ngIf="userService.isManager()" class="add-project-btn" color="primary" mat-flat-button>
<mat-icon svgIcon="red:plus"></mat-icon>
<span translate="project-listing.add-new"></span>
</button>
</div>

View File

@ -1,13 +0,0 @@
import { Component, EventEmitter, Output } from '@angular/core';
import { UserService } from '../../../../../services/user.service';
@Component({
selector: 'redaction-project-listing-empty',
templateUrl: './project-listing-empty.component.html',
styleUrls: ['./project-listing-empty.component.scss']
})
export class ProjectListingEmptyComponent {
@Output() addProjectRequest = new EventEmitter();
constructor(public userService: UserService) {}
}

View File

@ -14,7 +14,6 @@ import { CommentsComponent } from './components/comments/comments.component';
import { ProjectDetailsComponent } from './components/project-details/project-details.component';
import { PageIndicatorComponent } from './components/page-indicator/page-indicator.component';
import { NeedsWorkBadgeComponent } from './components/needs-work-badge/needs-work-badge.component';
import { ProjectListingEmptyComponent } from './components/empty-states/project-listing-empty/project-listing-empty.component';
import { AnnotationActionsComponent } from './components/annotation-actions/annotation-actions.component';
import { ProjectListingDetailsComponent } from './components/project-listing-details/project-listing-details.component';
import { FileActionsComponent } from './components/file-actions/file-actions.component';
@ -35,7 +34,6 @@ import { PdfViewerDataService } from './services/pdf-viewer-data.service';
import { ManualAnnotationService } from './services/manual-annotation.service';
import { AnnotationDrawService } from './services/annotation-draw.service';
import { AnnotationProcessingService } from './services/annotation-processing.service';
import { DisabledForRedactionComponent } from './components/disabled-for-redaction/disabled-for-redaction.component';
const screens = [ProjectListingScreenComponent, ProjectOverviewScreenComponent, FilePreviewScreenComponent];
@ -55,7 +53,6 @@ const components = [
PageIndicatorComponent,
NeedsWorkBadgeComponent,
AnnotationActionsComponent,
ProjectListingEmptyComponent,
ProjectListingDetailsComponent,
TypeAnnotationIconComponent,
TypeFilterComponent,
@ -65,7 +62,6 @@ const components = [
ProjectListingActionsComponent,
DocumentInfoComponent,
FileWorkloadComponent,
DisabledForRedactionComponent,
...screens,
...dialogs

View File

@ -208,7 +208,12 @@
</div>
<div class="right-container">
<redaction-disabled-for-redaction *ngIf="viewReady && appStateService.activeFile.isExcluded"> </redaction-disabled-for-redaction>
<redaction-empty-state
*ngIf="viewReady && appStateService.activeFile.isExcluded && !viewDocumentInfo"
icon="red:needs-work"
text="file-preview.tabs.is-excluded"
[horizontalPadding]="40"
></redaction-empty-state>
<redaction-document-info
*ngIf="viewReady && viewDocumentInfo"
@ -218,6 +223,7 @@
</redaction-document-info>
<redaction-file-workload
*ngIf="!appStateService.activeFile.isExcluded"
#fileWorkloadComponent
[annotations]="annotations"
[selectedAnnotations]="selectedAnnotations"

View File

@ -187,7 +187,7 @@
}
}
redaction-dictionary-annotation-icon {
::ng-deep redaction-dictionary-annotation-icon {
margin-right: 8px;
}

View File

@ -1,4 +1,4 @@
<section *ngIf="appStateService.hasProjects">
<section>
<div class="page-header">
<div class="filters">
<div translate="filters.filter-by"></div>
@ -52,7 +52,7 @@
</span>
</div>
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div class="table-header" redactionSyncWidth="table-item">
<redaction-table-col-name
(toggleSort)="sortingService.toggleSort('project-listing', $event)"
[activeSortingOption]="sortingOption"
@ -69,7 +69,15 @@
<div class="scrollbar-placeholder"></div>
</div>
<div *ngIf="noData" class="no-data heading-l" translate="project-listing.no-projects-match"></div>
<redaction-empty-state
*ngIf="!appStateService.hasProjects"
icon="red:folder"
screen="project-listing"
(action)="openAddProjectDialog()"
[showButton]="permissionsService.isManager()"
></redaction-empty-state>
<div *ngIf="appStateService.hasProjects && !displayedProjects.length" class="no-data heading-l" translate="project-listing.no-projects-match"></div>
<cdk-virtual-scroll-viewport [itemSize]="85" redactionHasScrollbar>
<div
@ -127,6 +135,7 @@
<div class="right-container" redactionHasScrollbar>
<redaction-project-listing-details
*ngIf="appStateService.hasProjects"
(filtersChanged)="filtersChanged($event)"
[documentsChartData]="documentsChartData"
[filters]="detailsContainerFilters"
@ -136,7 +145,7 @@
</div>
</section>
<redaction-project-listing-empty (addProjectRequest)="openAddProjectDialog()" *ngIf="!appStateService.hasProjects"></redaction-project-listing-empty>
<!--<redaction-project-listing-empty (addProjectRequest)="openAddProjectDialog()" *ngIf="!appStateService.hasProjects"></redaction-project-listing-empty>-->
<ng-template #needsWorkTemplate let-filter="filter">
<redaction-type-filter [filter]="filter"></redaction-type-filter>

View File

@ -109,7 +109,7 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy {
}
public get noData() {
return this.displayedProjects?.length === 0;
return this.appStateService.allProjects?.length === 0;
}
public resetFilters() {

View File

@ -83,172 +83,171 @@
</div>
<div class="red-content-inner">
<div class="content-container" [class.extended]="collapsedDetails">
<div *ngIf="!appStateService.activeProject?.hasFiles" class="empty-state">
<mat-icon svgIcon="red:document"></mat-icon>
<div class="heading-l" translate="project-overview.no-data.title"></div>
<redaction-icon-button (action)="fileInput.click()" icon="red:upload" text="project-overview.no-data.action" type="primary">
</redaction-icon-button>
<div class="header-item">
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
[class.active]="areAllFilesSelected"
class="select-oval always-visible"
*ngIf="!areAllFilesSelected && !areSomeFilesSelected"
></div>
<mat-icon *ngIf="areAllFilesSelected" (click)="toggleSelectAll()" class="selection-icon active" svgIcon="red:radio-selected"></mat-icon>
<mat-icon
*ngIf="areSomeFilesSelected && !areAllFilesSelected"
(click)="toggleSelectAll()"
class="selection-icon"
svgIcon="red:radio-indeterminate"
></mat-icon>
</div>
<span class="all-caps-label">
{{ 'project-overview.table-header.title' | translate: { length: displayedFiles.length || 0 } }}
</span>
<redaction-project-overview-bulk-actions
[selectedFileIds]="selectedFileIds"
(reload)="bulkActionPerformed()"
></redaction-project-overview-bulk-actions>
</div>
<ng-container *ngIf="appStateService.activeProject?.hasFiles">
<div class="header-item">
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
[class.active]="areAllFilesSelected"
class="select-oval always-visible"
*ngIf="!areAllFilesSelected && !areSomeFilesSelected"
></div>
<mat-icon *ngIf="areAllFilesSelected" (click)="toggleSelectAll()" class="selection-icon active" svgIcon="red:radio-selected"></mat-icon>
<mat-icon
*ngIf="areSomeFilesSelected && !areAllFilesSelected"
(click)="toggleSelectAll()"
class="selection-icon"
svgIcon="red:radio-indeterminate"
></mat-icon>
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="!appStateService.activeProject?.hasFiles">
<!-- Table column names-->
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="filename"
label="project-overview.table-col-names.name"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="added"
label="project-overview.table-col-names.added-on"
></redaction-table-col-name>
<redaction-table-col-name label="project-overview.table-col-names.needs-work"></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="reviewerName"
label="project-overview.table-col-names.assigned-to"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="pages"
label="project-overview.table-col-names.pages"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
class="flex-end"
column="statusSort"
label="project-overview.table-col-names.status"
></redaction-table-col-name>
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state
*ngIf="!appStateService.activeProject?.hasFiles"
icon="red:document"
screen="project-overview"
(action)="fileInput.click()"
buttonIcon="red:upload"
></redaction-empty-state>
<div
*ngIf="appStateService.activeProject?.hasFiles && !displayedFiles.length"
class="no-data heading-l"
translate="project-overview.no-files-match"
></div>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
*cdkVirtualFor="let fileStatus of displayedFiles | sortBy: sortingOption.order:sortingOption.column; trackBy: fileId"
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
[routerLink]="fileLink(fileStatus)"
class="table-item"
[class.disabled]="fileStatus.isExcluded"
>
<div class="pr-0" (click)="toggleFileSelected($event, fileStatus)">
<div *ngIf="!isFileSelected(fileStatus)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isFileSelected(fileStatus)" svgIcon="red:radio-selected"></mat-icon>
</div>
<span class="all-caps-label">
{{ 'project-overview.table-header.title' | translate: { length: displayedFiles.length || 0 } }}
</span>
<div [title]="'[' + fileStatus.status + '] ' + fileStatus.filename">
<div class="filename-wrapper">
<div [class.disabled]="fileStatus.isPending || fileStatus.isProcessing" [class.error]="fileStatus.isError" class="table-item-title">
{{ fileStatus.filename }}
</div>
<!-- <span *ngIf="permissionsService.fileRequiresReanalysis(fileStatus)" class="pill" translate="project-overview.new-rule.label"></span>-->
</div>
</div>
<redaction-project-overview-bulk-actions
[selectedFileIds]="selectedFileIds"
(reload)="bulkActionPerformed()"
></redaction-project-overview-bulk-actions>
</div>
<div>
<div [class.error]="fileStatus.isError" class="small-label">
{{ fileStatus.added | date: 'd MMM. yyyy, hh:mm a' }}
</div>
</div>
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="noData">
<!-- Table column names-->
<div class="select-oval-placeholder"></div>
<!-- always show A for error-->
<div *ngIf="fileStatus.isError">
<redaction-annotation-icon type="square" label="A" color="#dd4d50"></redaction-annotation-icon>
</div>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="filename"
label="project-overview.table-col-names.name"
></redaction-table-col-name>
<div *ngIf="!fileStatus.isError">
<redaction-needs-work-badge [needsWorkInput]="fileStatus"></redaction-needs-work-badge>
</div>
<div *ngIf="!fileStatus.isError" class="assigned-to">
<redaction-initials-avatar [userId]="fileStatus.currentReviewer" [withName]="true"></redaction-initials-avatar>
</div>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="added"
label="project-overview.table-col-names.added-on"
></redaction-table-col-name>
<div *ngIf="!fileStatus.isError">
<div class="quick-navigation">
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>
</div>
<redaction-table-col-name label="project-overview.table-col-names.needs-work"></redaction-table-col-name>
<div [class.extend-cols]="fileStatus.isError" class="status-container">
<div *ngIf="fileStatus.isError" class="small-label error" translate="project-overview.file-listing.file-entry.file-error"></div>
<div *ngIf="fileStatus.isPending" class="small-label" translate="project-overview.file-listing.file-entry.file-pending"></div>
<div
*ngIf="fileStatus.isProcessing"
class="small-label loading"
translate="project-overview.file-listing.file-entry.file-processing"
></div>
<redaction-status-bar
*ngIf="fileStatus.isWorkable"
[config]="[
{
color: fileStatus.status,
length: 1
}
]"
></redaction-status-bar>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="reviewerName"
label="project-overview.table-col-names.assigned-to"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
column="pages"
label="project-overview.table-col-names.pages"
></redaction-table-col-name>
<redaction-table-col-name
(toggleSort)="toggleSort($event)"
[activeSortingOption]="sortingOption"
[withSort]="true"
class="flex-end"
column="statusSort"
label="project-overview.table-col-names.status"
></redaction-table-col-name>
<redaction-file-actions
[fileStatus]="fileStatus"
(actionPerformed)="calculateData()"
*ngIf="!fileStatus.isProcessing"
class="mr-4"
></redaction-file-actions>
</div>
<div class="scrollbar-placeholder"></div>
</div>
<div *ngIf="noData" class="no-data heading-l" translate="project-overview.no-files-match"></div>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
*cdkVirtualFor="let fileStatus of displayedFiles | sortBy: sortingOption.order:sortingOption.column; trackBy: fileId"
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
[routerLink]="fileLink(fileStatus)"
class="table-item"
[class.disabled]="fileStatus.isExcluded"
>
<div class="pr-0" (click)="toggleFileSelected($event, fileStatus)">
<div *ngIf="!isFileSelected(fileStatus)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isFileSelected(fileStatus)" svgIcon="red:radio-selected"></mat-icon>
</div>
<div [title]="'[' + fileStatus.status + '] ' + fileStatus.filename">
<div class="filename-wrapper">
<div
[class.disabled]="fileStatus.isPending || fileStatus.isProcessing"
[class.error]="fileStatus.isError"
class="table-item-title"
>
{{ fileStatus.filename }}
</div>
<!-- <span *ngIf="permissionsService.fileRequiresReanalysis(fileStatus)" class="pill" translate="project-overview.new-rule.label"></span>-->
</div>
</div>
<div>
<div [class.error]="fileStatus.isError" class="small-label">
{{ fileStatus.added | date: 'd MMM. yyyy, hh:mm a' }}
</div>
</div>
<!-- always show A for error-->
<div *ngIf="fileStatus.isError">
<redaction-annotation-icon type="square" label="A" color="#dd4d50"></redaction-annotation-icon>
</div>
<div *ngIf="!fileStatus.isError">
<redaction-needs-work-badge [needsWorkInput]="fileStatus"></redaction-needs-work-badge>
</div>
<div *ngIf="!fileStatus.isError" class="assigned-to">
<redaction-initials-avatar [userId]="fileStatus.currentReviewer" [withName]="true"></redaction-initials-avatar>
</div>
<div *ngIf="!fileStatus.isError">
<div class="quick-navigation">
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>
</div>
<div [class.extend-cols]="fileStatus.isError" class="status-container">
<div *ngIf="fileStatus.isError" class="small-label error" translate="project-overview.file-listing.file-entry.file-error"></div>
<div *ngIf="fileStatus.isPending" class="small-label" translate="project-overview.file-listing.file-entry.file-pending"></div>
<div
*ngIf="fileStatus.isProcessing"
class="small-label loading"
translate="project-overview.file-listing.file-entry.file-processing"
></div>
<redaction-status-bar
*ngIf="fileStatus.isWorkable"
[config]="[
{
color: fileStatus.status,
length: 1
}
]"
></redaction-status-bar>
<redaction-file-actions
[fileStatus]="fileStatus"
(actionPerformed)="calculateData()"
*ngIf="!fileStatus.isProcessing"
class="mr-4"
></redaction-file-actions>
</div>
<div class="scrollbar-placeholder"></div>
</div>
</cdk-virtual-scroll-viewport>
</ng-container>
</cdk-virtual-scroll-viewport>
</div>
<div class="right-container" redactionHasScrollbar [class.collapsed]="collapsedDetails">

View File

@ -143,10 +143,6 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
return [FileStatus.StatusEnum.REPROCESS, FileStatus.StatusEnum.FULLREPROCESS, FileStatus.StatusEnum.PROCESSING].includes(fileStatusWrapper.status);
}
public get noData() {
return this.displayedFiles?.length === 0;
}
public get sortingOption(): SortingOption {
return this._sortingService.getSortingOption('project-overview');
}
@ -179,6 +175,9 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
}
calculateData(): void {
if (!this.appStateService.activeProjectId) {
return;
}
this._computeAllFilters();
this._filterFiles();
this._projectDetailsComponent?.calculateChartConfig();

View File

@ -29,7 +29,7 @@ export class CircleButtonComponent implements OnInit {
if (!this.disabled) {
if (this.removeTooltip) {
this.matTooltip.hide();
// Timeout to allow tooltip to disappear first
// Timeout to allow tooltip to disappear first, useful when removing an item from the list without a confirmation dialog
setTimeout(() => {
this.action.emit($event);
}, 0);

View File

@ -0,0 +1,6 @@
<div class="empty-state" [ngStyle]="{ 'padding-left': horizontalPadding + 'px', 'padding-right': horizontalPadding + 'px' }">
<mat-icon [svgIcon]="icon"></mat-icon>
<div class="heading-l" [translate]="text || screen + '.no-data.title'"></div>
<redaction-icon-button *ngIf="showButton" (action)="action.emit()" [icon]="buttonIcon" [text]="screen + '.no-data.action'" type="primary">
</redaction-icon-button>
</div>

View File

@ -0,0 +1,24 @@
@import '../../../../../assets/styles/red-mixins';
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 120px;
text-align: center;
> mat-icon {
height: 60px;
width: 60px;
opacity: 0.1;
}
.heading-l {
color: $grey-7;
}
> .heading-l,
redaction-icon-button {
margin-top: 24px;
}
}

View File

@ -0,0 +1,22 @@
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
@Component({
selector: 'redaction-empty-state',
templateUrl: './empty-state.component.html',
styleUrls: ['./empty-state.component.scss']
})
export class EmptyStateComponent implements OnInit {
@Input() screen: string;
@Input() text: string;
@Input() icon: string;
@Input() showButton = true;
@Input() buttonIcon = 'red:plus';
@Input() horizontalPadding = 100;
@Output() action = new EventEmitter();
constructor() {}
ngOnInit(): void {
this.showButton = this.showButton && this.action.observers.length > 0;
}
}

View File

@ -1,5 +1,9 @@
<div class="wrapper">
<div [className]="colorClass + ' oval ' + size + (hasBorder ? ' border' : '')" [matTooltipPosition]="'above'" [matTooltip]="displayName">
<div
[className]="colorClass + ' oval ' + size + (hasBorder ? ' border' : '')"
[matTooltipPosition]="'above'"
[matTooltip]="displayName || ('initials-avatar.unassigned' | translate)"
>
{{ initials }}
</div>
<div *ngIf="withName" class="clamp-2 username" [class.always-visible]="alwaysShowName" [class.disabled]="disabled">

View File

@ -25,12 +25,14 @@ export class SyncWidthDirective implements AfterViewInit, OnDestroy {
@debounce(10)
matchWidth() {
const headerItems = this.el.nativeElement.children;
const tableRows = document.getElementsByClassName(this.redactionSyncWidth);
const tableRows = this.el.nativeElement.parentElement.getElementsByClassName(this.redactionSyncWidth);
if (!tableRows || !tableRows.length) {
return;
}
this.el.nativeElement.setAttribute('synced', true);
const { tableRow, length } = this._sampleRow;
const hasExtraColumns = headerItems.length !== length ? 1 : 0;

View File

@ -26,6 +26,7 @@ import { DictionaryAnnotationIconComponent } from './components/dictionary-annot
import { HiddenActionComponent } from './components/hidden-action/hidden-action.component';
import { ConfirmationDialogComponent } from './dialogs/confirmation-dialog/confirmation-dialog.component';
import { FilterComponent } from './components/filter/filter.component';
import { EmptyStateComponent } from './components/empty-state/empty-state.component';
const buttons = [ChevronButtonComponent, CircleButtonComponent, FileDownloadBtnComponent, IconButtonComponent, UserButtonComponent];
@ -42,6 +43,7 @@ const components = [
HiddenActionComponent,
FilterComponent,
ConfirmationDialogComponent,
EmptyStateComponent,
...buttons
];

View File

@ -126,6 +126,11 @@
"total-documents": "Total Document(s)"
}
},
"no-data": {
"title": "You currently have no projects.",
"action": "New Project"
},
"add-new": "New Project",
"add-edit-dialog": {
"header-new": "Create Project",
"header-edit": "Edit Project",
@ -158,8 +163,6 @@
"action": "Delete Project",
"delete-failed": "Failed to delete project: {{projectName}}"
},
"add-new": "New Project",
"no-projects": "You currently have no projects.",
"no-projects-match": "No Projects match your current filters"
},
"project-details": {
@ -737,6 +740,9 @@
"created-by": "Created by",
"created-on": "Created on",
"modified-on": "Modified on"
},
"no-data": {
"title": "There are no project templates yet."
}
},
"file-attributes-listing": {
@ -750,7 +756,9 @@
"created-by": "Created by",
"read-only": "Read-Only"
},
"no-data": "No file attributes.",
"no-data": {
"title": "There are no file attributes yet."
},
"read-only": "Read-only",
"action": {
"edit": "Edit attribute",
@ -955,7 +963,10 @@
},
"all-categories": "All Categories",
"all-users": "All Users",
"to": "to"
"to": "to",
"no-data": {
"title": "No available logs."
}
},
"pagination": {
"previous": "Prev",
@ -1148,6 +1159,9 @@
"save-name": "Save",
"cancel-edit-name": "Cancel",
"remove": "Remove"
},
"no-data": {
"title": "No file attributes defined. Select a column from the left panel to start defining file attributes."
}
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<title>status</title>
<g id="Styleguide" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Styleguide-Actions" transform="translate(-608.000000, -651.000000)" fill="currentColor" fill-rule="nonzero">
<g id="Group-6" transform="translate(598.000000, 641.000000)">
<g id="status" transform="translate(10.000000, 10.000000)">
<path d="M14,8.8817842e-15 L14,6.7673716 L7.2326284,13.5347432 C6.612286,14.1550856 5.59718026,14.1550856 4.97683787,13.5347432 L4.97683787,13.5347432 L0.465256798,9.02316213 C-0.155085599,8.40281974 -0.155085599,7.387714 0.465256798,6.7673716 L0.465256798,6.7673716 L7.2326284,8.8817842e-15 L14,8.8817842e-15 Z M12.4209466,1.57905337 L7.90936556,1.57905337 L1.59315206,7.89526687 L6.10473313,12.4068479 L12.4209466,6.09063444 L12.4209466,1.57905337 Z M9.48841893,3.3836858 C9.79987861,3.07222612 10.3048545,3.07222612 10.6163142,3.3836858 C10.9277739,3.69514548 10.9277739,4.20012139 10.6163142,4.51158107 C10.3048545,4.82304075 9.79987861,4.82304075 9.48841893,4.51158107 C9.17695925,4.20012139 9.17695925,3.69514548 9.48841893,3.3836858 Z" id="Combined-Shape"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -79,126 +79,103 @@ body {
height: calc(100% - 110px);
z-index: 1;
transition: height ease-in-out 0.2s;
}
.content-container {
.overlay-shadow {
@include inset-shadow;
position: fixed;
width: 100%;
height: 4px;
z-index: 1;
.content-container {
.overlay-shadow {
@include inset-shadow;
position: fixed;
width: 100%;
height: 4px;
z-index: 1;
}
overflow: hidden;
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
position: relative;
&.extended {
width: calc(100vw - 60px) !important;
}
.dialog {
border-radius: 8px;
margin-top: 40px;
margin-bottom: 70px;
background-color: $white;
max-width: 650px;
height: fit-content;
box-shadow: 0 1px 5px 0 rgba(40, 50, 65, 0.19);
position: unset;
.heading-l {
margin-bottom: 16px;
}
overflow: hidden;
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
position: relative;
&.extended {
width: calc(100vw - 60px) !important;
}
.empty-state {
.dialog-content {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 120px;
@include inset-shadow;
> mat-icon {
height: 60px;
width: 60px;
opacity: 0.1;
.dialog-content-left {
min-width: 300px;
margin-right: 64px;
}
.heading-l {
color: $grey-7;
}
> mat-icon,
.heading-l {
margin-bottom: 24px;
}
}
.dialog {
border-radius: 8px;
margin-top: 40px;
margin-bottom: 70px;
background-color: $white;
max-width: 650px;
height: fit-content;
box-shadow: 0 1px 5px 0 rgba(40, 50, 65, 0.19);
position: unset;
.heading-l {
margin-bottom: 16px;
}
.dialog-content {
display: flex;
.dialog-content-left {
min-width: 300px;
margin-right: 64px;
}
.link-action {
margin-top: 8px;
}
}
}
@media only screen and (max-width: 1600px) {
redaction-initials-avatar .username:not(.always-visible) {
display: none;
.link-action {
margin-top: 8px;
}
}
}
.right-container {
border-left: 1px solid $grey-4;
box-sizing: border-box;
background: $white;
z-index: 1;
overflow: hidden;
@include inset-shadow;
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
&:hover {
overflow-y: auto;
@include scroll-bar;
@media only screen and (max-width: 1600px) {
redaction-initials-avatar .username:not(.always-visible) {
display: none;
}
}
}
.collapsed-wrapper {
.right-container {
border-left: 1px solid $grey-4;
box-sizing: border-box;
background: $white;
z-index: 1;
overflow: hidden;
@include inset-shadow;
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
&:hover {
overflow-y: auto;
@include scroll-bar;
}
.collapsed-wrapper {
display: none;
}
&.collapsed {
padding-left: 0 !important;
padding-right: 0 !important;
width: 60px !important;
min-width: 60px !important;
display: flex;
div:not(.collapsed-wrapper) {
display: none;
}
&.collapsed {
padding-left: 0 !important;
padding-right: 0 !important;
width: 60px !important;
min-width: 60px;
.collapsed-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 60px;
div:not(.collapsed-wrapper) {
display: none;
div {
display: initial;
}
.collapsed-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 60px;
div {
display: initial;
}
.all-caps-label {
transform: rotate(90deg) translateX(50%);
white-space: nowrap;
margin-top: 10px;
}
.all-caps-label {
transform: rotate(90deg) translateX(50%);
white-space: nowrap;
margin-top: 10px;
}
}
}
@ -337,19 +314,6 @@ body {
margin-right: 8px !important;
}
.empty-state-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: calc(100vh - 61px);
position: relative;
> *:not(:last-child) {
margin-bottom: 2px;
}
}
.fit-content {
width: fit-content;
}

View File

@ -12,17 +12,12 @@
}
}
.no-data {
padding: 24px;
}
.table-header {
display: flex;
border-bottom: 1px solid $separator;
&.no-data {
justify-content: space-between;
padding: initial;
&.no-data:not([synced='true']) {
padding-left: 30px;
}
redaction-table-col-name:last-of-type {