Pull request #148: Empty states

Merge in RED/ui from empty-states to master

* commit 'e200d339eb155259f8fff5983be22d84c7479f27':
  Bulk actions on csv import file attributes dialog
  Empty state component & other fixes
  Fixed circle button
  Small UI fixes
This commit is contained in:
Timo Bejan 2021-04-11 09:16:56 +02:00
commit b360d3c5ba
42 changed files with 650 additions and 578 deletions

View File

@ -1,18 +1,17 @@
<div class='red-top-bar'>
<div class='top-bar-row'>
<div class='menu-placeholder' *ngIf='!permissionsService.isUser()'></div>
<div class='menu visible-lt-lg' *ngIf='permissionsService.isUser()'>
<button [matMenuTriggerFor]='menuNav' mat-flat-button>
<mat-icon svgIcon='red:menu'></mat-icon>
<div class="red-top-bar">
<div class="top-bar-row">
<div class="menu-placeholder" *ngIf="!permissionsService.isUser()"></div>
<div class="menu visible-lt-lg" *ngIf="permissionsService.isUser()">
<button [matMenuTriggerFor]="menuNav" mat-flat-button>
<mat-icon svgIcon="red:menu"></mat-icon>
</button>
<mat-menu #menuNav='matMenu'>
<button mat-menu-item routerLink='/ui/projects' translate='top-bar.navigation-items.projects'></button>
<button *ngIf='appStateService.activeProject'
[routerLink]="'/ui/projects/' + appStateService.activeProjectId" mat-menu-item>
<mat-menu #menuNav="matMenu">
<button mat-menu-item routerLink="/ui/projects" translate="top-bar.navigation-items.projects"></button>
<button *ngIf="appStateService.activeProject" [routerLink]="'/ui/projects/' + appStateService.activeProjectId" mat-menu-item>
{{ appStateService.activeProject.project.projectName }}
</button>
<button
*ngIf='appStateService.activeFile'
*ngIf="appStateService.activeFile"
[routerLink]="'/ui/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
mat-menu-item
>
@ -20,97 +19,91 @@
</button>
</mat-menu>
</div>
<div class='menu flex-2 visible-lg breadcrumbs-container' *ngIf='permissionsService.isUser()'>
<div class="menu flex-2 visible-lg breadcrumbs-container" *ngIf="permissionsService.isUser()">
<a
class='breadcrumb'
routerLink='/ui/projects'
translate='top-bar.navigation-items.projects'
routerLinkActive='active'
*ngIf='projectsView'
[routerLinkActiveOptions]='{ exact: true }'
class="breadcrumb"
routerLink="/ui/projects"
translate="top-bar.navigation-items.projects"
routerLinkActive="active"
*ngIf="projectsView"
[routerLinkActiveOptions]="{ exact: true }"
></a>
<a
class='breadcrumb back-to-projects'
routerLink='/ui/projects'
routerLinkActive='active'
*ngIf='settingsView'
[routerLinkActiveOptions]='{ exact: true }'
class="breadcrumb back-to-projects"
routerLink="/ui/projects"
routerLinkActive="active"
*ngIf="settingsView"
[routerLinkActiveOptions]="{ exact: true }"
>
<mat-icon svgIcon='red:expand'></mat-icon>
<mat-icon svgIcon="red:expand"></mat-icon>
{{ 'top-bar.navigation-items.back-to-projects' | translate }}
</a>
<ng-container *ngIf='projectsView'>
<mat-icon class='primary' *ngIf='!appStateService.activeProject' svgIcon='red:arrow-down'></mat-icon>
<mat-icon *ngIf='appStateService.activeProject' svgIcon='red:arrow-right'></mat-icon>
<ng-container *ngIf="projectsView">
<mat-icon class="primary" *ngIf="!appStateService.activeProject" svgIcon="red:arrow-down"></mat-icon>
<mat-icon *ngIf="appStateService.activeProject" svgIcon="red:arrow-right"></mat-icon>
<a
*ngIf='appStateService.activeProject'
class='breadcrumb'
*ngIf="appStateService.activeProject"
class="breadcrumb"
[routerLink]="'/ui/projects/' + appStateService.activeProjectId"
routerLinkActive='active'
[routerLinkActiveOptions]='{ exact: true }'
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }"
>
{{ appStateService.activeProject.project.projectName }}
</a>
<mat-icon svgIcon='red:arrow-right' *ngIf='appStateService.activeFile'></mat-icon>
<mat-icon svgIcon="red:arrow-right" *ngIf="appStateService.activeFile"></mat-icon>
<a
*ngIf='appStateService.activeFile'
class='breadcrumb'
*ngIf="appStateService.activeFile"
class="breadcrumb"
[routerLink]="'/ui/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
routerLinkActive='active'
routerLinkActive="active"
>
{{ appStateService.activeFile.filename }}
</a>
</ng-container>
</div>
<div class='center flex-1'>
<redaction-hidden-action (action)='userPreferenceService.toggleDevFeatures()'>
<div class="center flex-1">
<redaction-hidden-action (action)="userPreferenceService.toggleDevFeatures()">
<redaction-logo></redaction-logo>
</redaction-hidden-action>
<div class='app-name'>{{ titleService.getTitle() }}</div>
<span class='dev-mode' *ngIf='userPreferenceService.areDevFeaturesEnabled' translate='dev-mode'></span>
<div class="app-name">{{ titleService.getTitle() }}</div>
<span class="dev-mode" *ngIf="userPreferenceService.areDevFeaturesEnabled" translate="dev-mode"></span>
</div>
<div class='menu right flex-2'>
<redaction-notifications class='mr-8'
*ngIf='userPreferenceService.areDevFeaturesEnabled'></redaction-notifications>
<redaction-user-button [user]='user' [matMenuTriggerFor]='userMenu'
[showDot]='showPendingDownloadsDot'></redaction-user-button>
<mat-menu #userMenu='matMenu'>
<div class="menu right flex-2">
<redaction-notifications class="mr-8" *ngIf="userPreferenceService.areDevFeaturesEnabled"></redaction-notifications>
<redaction-user-button [user]="user" [matMenuTriggerFor]="userMenu" [showDot]="showPendingDownloadsDot"></redaction-user-button>
<mat-menu #userMenu="matMenu" class="user-menu" xPosition="before">
<button
*ngIf='permissionsService.isUser()'
*ngIf="permissionsService.isUser()"
[routerLink]="'/ui/my-profile'"
mat-menu-item
translate='top-bar.navigation-items.my-account.children.my-profile'
translate="top-bar.navigation-items.my-account.children.my-profile"
></button>
<button
*ngIf='permissionsService.isManager()'
(click)='appStateService.reset()'
*ngIf="permissionsService.isManager()"
(click)="appStateService.reset()"
[routerLink]="'/ui/admin'"
mat-menu-item
translate='top-bar.navigation-items.my-account.children.admin'
translate="top-bar.navigation-items.my-account.children.admin"
></button>
<button
*ngIf='permissionsService.isUser()'
*ngIf="permissionsService.isUser()"
[routerLink]="'/ui/downloads'"
mat-menu-item
translate='top-bar.navigation-items.my-account.children.downloads'
translate="top-bar.navigation-items.my-account.children.downloads"
></button>
<button [matMenuTriggerFor]='language' mat-menu-item
translate='top-bar.navigation-items.my-account.children.language.label'></button>
<mat-menu #language='matMenu'>
<button
*ngFor='let lang of languages'
(click)='changeLanguage(lang)'
mat-menu-item
translate
>top-bar.navigation-items.my-account.children.language.{{lang}}</button>
<button [matMenuTriggerFor]="language" mat-menu-item translate="top-bar.navigation-items.my-account.children.language.label"></button>
<mat-menu #language="matMenu">
<button *ngFor="let lang of languages" (click)="changeLanguage(lang)" mat-menu-item translate>
top-bar.navigation-items.my-account.children.language.{{ lang }}
</button>
</mat-menu>
<button (click)='logout()' mat-menu-item>
<mat-icon svgIcon='red:logout'></mat-icon>
<span translate='top-bar.navigation-items.my-account.children.logout'> </span>
<button (click)="logout()" mat-menu-item>
<mat-icon svgIcon="red:logout"></mat-icon>
<span translate="top-bar.navigation-items.my-account.children.logout"> </span>
</button>
</mat-menu>
</div>
</div>
<div class='divider'></div>
<div class="divider"></div>
</div>
<router-outlet></router-outlet>

View File

@ -1,5 +1,5 @@
<redaction-circle-button [matMenuTriggerFor]="overlay" icon="red:notification" [showDot]="hasUnread"></redaction-circle-button>
<mat-menu #overlay="matMenu" class="notifications-menu" backdropClass="notifications-backdrop">
<mat-menu #overlay="matMenu" class="notifications-menu" backdropClass="notifications-backdrop" xPosition="before">
<div *ngFor="let group of groupedNotifications | sortBy: 'desc':'dateString'">
<div class="all-caps-label">{{ day(group) }}</div>
<div

View File

@ -10,6 +10,7 @@
.notifications-menu {
min-width: 400px !important;
padding: 0 8px 16px;
margin-right: 8px;
max-height: calc(100vh - 200px);
.all-caps-label {

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

@ -123,6 +123,20 @@
</span>
<ng-container *ngIf="areSomeFieldsSelected">
<redaction-circle-button
[matMenuTriggerFor]="readOnlyMenu"
tooltip="file-attributes-csv-import.table-header.actions.read-only"
type="dark-bg"
icon="red:read-only"
>
</redaction-circle-button>
<redaction-circle-button
[matMenuTriggerFor]="displayMenu"
tooltip="file-attributes-csv-import.table-header.actions.display"
type="dark-bg"
icon="red:visibility"
>
</redaction-circle-button>
<redaction-circle-button
(action)="deactivateSelection()"
tooltip="file-attributes-csv-import.table-header.actions.remove-selected"
@ -130,6 +144,45 @@
icon="red:trash"
>
</redaction-circle-button>
<div class="separator"></div>
<redaction-chevron-button
text="file-attributes-csv-import.table-header.actions.type"
[matMenuTriggerFor]="typeMenu"
></redaction-chevron-button>
<mat-menu #readOnlyMenu="matMenu" class="no-padding-bottom">
<button
mat-menu-item
(click)="setAttributeForSelection('readonly', true)"
translate="file-attributes-csv-import.table-header.actions.enable-read-only"
></button>
<button
mat-menu-item
(click)="setAttributeForSelection('readonly', false)"
translate="file-attributes-csv-import.table-header.actions.disable-read-only"
></button>
</mat-menu>
<mat-menu #displayMenu="matMenu" class="no-padding-bottom">
<button
mat-menu-item
(click)="setAttributeForSelection('display', true)"
translate="file-attributes-csv-import.table-header.actions.enable-display"
></button>
<button
mat-menu-item
(click)="setAttributeForSelection('display', false)"
translate="file-attributes-csv-import.table-header.actions.disable-display"
></button>
</mat-menu>
<mat-menu #typeMenu="matMenu" class="no-padding-bottom">
<button *ngFor="let type of ['Text', 'Number', 'Date']" mat-menu-item (click)="setAttributeForSelection('type', type)">
{{ 'file-attributes-csv-import.types.' + type | translate }}
</button>
</mat-menu>
</ng-container>
</div>
@ -156,6 +209,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
@ -222,6 +277,7 @@
<div class="action-buttons">
<redaction-circle-button
(action)="toggleFieldActive(field)"
[removeTooltip]="true"
tooltip="file-attributes-csv-import.action.remove"
type="dark-bg"
icon="red:trash"

View File

@ -153,7 +153,7 @@
}
> .content-container {
width: 100%;
width: calc(100% - 527px);
redaction-table-col-name::ng-deep {
> div {
@ -173,6 +173,18 @@
.all-caps-label {
margin-right: 10px;
}
redaction-circle-button {
margin-right: 2px;
}
.separator {
margin-left: 14px;
background-color: $separator;
width: 1px;
height: 30px;
margin-right: 16px;
}
}
cdk-virtual-scroll-viewport {

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);
}
@ -174,6 +177,12 @@ export class FileAttributesCsvImportDialogComponent implements OnInit {
this.selectedFields = [];
}
public setAttributeForSelection(attribute: string, value: any) {
for (const csvColumn of this.selectedFields) {
this.activeFields.find((f) => f.csvColumn === csvColumn)[attribute] = value;
}
}
public async save() {
await this._fileAttributesControllerService
.addOrUpdateFileAttributesConfig(

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

@ -123,11 +123,7 @@
<ng-template #annotationFilterActionTemplate let-filter="filter">
<ng-container *ngIf="filter.key === 'skipped'">
<redaction-circle-button
[icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'"
(action)="toggleSkipped.emit($event)"
class="skipped-toggle-button"
>
<redaction-circle-button [icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'" (action)="toggleSkipped.emit($event)" type="dark-bg">
</redaction-circle-button>
</ng-container>
</ng-template>

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;
}
@ -203,13 +203,6 @@ redaction-dictionary-annotation-icon {
margin-left: 2px;
}
.skipped-toggle-button {
position: absolute;
right: 0;
justify-content: center;
align-items: center;
}
.analysis-progress {
padding: 12px 20px;
max-width: 400px;

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

@ -1,6 +1,6 @@
<div #matTooltip="matTooltip" [matTooltipClass]="tooltipClass" [matTooltipPosition]="tooltipPosition" [matTooltip]="tooltip | translate">
<div [matTooltipClass]="tooltipClass" [matTooltipPosition]="tooltipPosition" [matTooltip]="tooltip | translate">
<button
(click)="matTooltip.hide(0); performAction($event)"
(click)="performAction($event)"
[class.dark-bg]="type === 'dark-bg'"
[class.primary]="type === 'primary'"
[class.warn]="type === 'warn'"

View File

@ -3,6 +3,7 @@
:host {
height: 34px;
width: 34px;
align-items: center;
}
button {

View File

@ -1,4 +1,5 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatTooltip } from '@angular/material/tooltip';
@Component({
selector: 'redaction-circle-button',
@ -15,18 +16,26 @@ export class CircleButtonComponent implements OnInit {
@Input() small = false;
@Input() type: 'default' | 'primary' | 'warn' | 'dark-bg' = 'default';
@Input() dummy = false;
@Input() removeTooltip = false;
@Output() action = new EventEmitter<any>();
@ViewChild(MatTooltip) matTooltip: MatTooltip;
constructor() {}
ngOnInit(): void {}
performAction($event: any) {
if (!this.disabled) {
// Timeout to allow tooltip to disappear first
setTimeout(() => {
if (this.removeTooltip) {
this.matTooltip.hide();
// 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);
} else {
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

@ -36,7 +36,7 @@
</mat-checkbox>
<ng-template *ngTemplateOutlet="actionsTemplate ? actionsTemplate : null; context: { filter: filter }"> </ng-template>
</div>
<div *ngIf="filter.filters && filter.expanded">
<div *ngIf="filter.filters?.length && filter.expanded">
<div *ngFor="let subFilter of filter.filters" class="padding-left mat-menu-item" (click)="$event.stopPropagation()">
<mat-checkbox
[checked]="subFilter.checked"

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",
@ -1124,7 +1135,14 @@
"table-header": {
"title": "{{length}} file attributes",
"actions": {
"remove-selected": "Remove Selected"
"remove-selected": "Remove Selected",
"read-only": "Make Read-only",
"enable-read-only": "Enable Read-only for all attributes",
"disable-read-only": "Disable Read-only for all attributes",
"display": "Toggle Display",
"enable-display": "Enable Display for all attributes",
"disable-display": "Disable Display for all attributes",
"type": "Type"
}
},
"file": "File:",
@ -1148,6 +1166,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

@ -12,6 +12,10 @@
padding-bottom: 24px;
}
&.no-padding-bottom .mat-menu-content:not(:empty) {
padding-bottom: 8px;
}
.mat-menu-item {
font-size: 13px;
color: $accent;

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 {