Pull request #292: Workflow

Merge in RED/ui from workflow to master

* commit '1e9071a643f9ed3439540378c5f19e419d8c2f96':
  Empty state, reload fixes
  Tooltip position fix
  Updates
  Disabled items
  Permissions, updated common-ui
  Workflow initial
This commit is contained in:
Adina Teudan 2021-09-28 15:05:51 +02:00
commit 0e8b081fdd
21 changed files with 611 additions and 344 deletions

View File

@ -14,7 +14,7 @@
border-radius: 4px; border-radius: 4px;
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
padding: 0 8px; padding: 4px 8px;
margin-left: -8px; margin-left: -8px;
&.link-property { &.link-property {

View File

@ -1,17 +1,26 @@
<div *ngIf="screen === 'dossier-overview'" class="action-buttons"> <div *ngIf="isDossierOverviewList" class="action-buttons">
<ng-container *ngTemplateOutlet="actions"></ng-container> <ng-container *ngTemplateOutlet="actions"></ng-container>
<iqser-status-bar *ngIf="isWorkable" [configs]="statusBarConfig"></iqser-status-bar> <iqser-status-bar *ngIf="showStatusBar" [configs]="statusBarConfig"></iqser-status-bar>
</div> </div>
<ng-container *ngIf="screen === 'file-preview'"> <ng-container *ngIf="isFilePreview || isDossierOverviewWorkflow">
<ng-container *ngTemplateOutlet="actions"></ng-container> <ng-container *ngTemplateOutlet="actions"></ng-container>
</ng-container> </ng-container>
<ng-template #actions redactionLongPress (longPress)="forceReanalysisAction($event)"> <ng-template #actions redactionLongPress (longPress)="forceReanalysisAction($event)">
<div class="file-actions" *ngIf="fileStatus"> <div *ngIf="fileStatus" class="file-actions">
<iqser-circle-button
(action)="openDocument()"
*ngIf="showOpenDocument"
[tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.open-document' | translate"
[type]="buttonType"
icon="red:collapse"
></iqser-circle-button>
<iqser-circle-button <iqser-circle-button
(action)="openDeleteFileDialog($event)" (action)="openDeleteFileDialog($event)"
*ngIf="canDelete" *ngIf="showDelete"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.delete.action' | translate" [tooltip]="'dossier-overview.delete.action' | translate"
[type]="buttonType" [type]="buttonType"
@ -20,7 +29,7 @@
<iqser-circle-button <iqser-circle-button
(action)="assign($event)" (action)="assign($event)"
*ngIf="canAssign && screen === 'dossier-overview'" *ngIf="showAssign"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="assignTooltip | translate" [tooltip]="assignTooltip | translate"
[type]="buttonType" [type]="buttonType"
@ -29,7 +38,7 @@
<iqser-circle-button <iqser-circle-button
(action)="assignToMe($event)" (action)="assignToMe($event)"
*ngIf="canAssignToSelf && screen === 'dossier-overview'" *ngIf="showAssignToSelf"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.assign-me' | translate" [tooltip]="'dossier-overview.assign-me' | translate"
[type]="buttonType" [type]="buttonType"
@ -47,7 +56,7 @@
<iqser-circle-button <iqser-circle-button
(action)="toggleViewDocumentInfo()" (action)="toggleViewDocumentInfo()"
*ngIf="screen === 'file-preview'" *ngIf="showDocumentInfo"
[attr.aria-expanded]="activeDocumentInfo" [attr.aria-expanded]="activeDocumentInfo"
[tooltip]="'file-preview.document-info' | translate" [tooltip]="'file-preview.document-info' | translate"
icon="red:status-info" icon="red:status-info"
@ -56,7 +65,7 @@
<iqser-circle-button <iqser-circle-button
(action)="toggleExcludePages()" (action)="toggleExcludePages()"
*ngIf="screen === 'file-preview'" *ngIf="showExcludePages"
[attr.aria-expanded]="activeExcludePages" [attr.aria-expanded]="activeExcludePages"
[showDot]="!!fileStatus.excludedPages?.length" [showDot]="!!fileStatus.excludedPages?.length"
[tooltip]="'file-preview.exclude-pages' | translate" [tooltip]="'file-preview.exclude-pages' | translate"
@ -67,7 +76,7 @@
<!-- Ready for approval--> <!-- Ready for approval-->
<iqser-circle-button <iqser-circle-button
(action)="setFileUnderApproval($event)" (action)="setFileUnderApproval($event)"
*ngIf="canSetToUnderApproval" *ngIf="showUnderApproval"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.under-approval' | translate" [tooltip]="'dossier-overview.under-approval' | translate"
[type]="buttonType" [type]="buttonType"
@ -77,7 +86,7 @@
<!-- Back to review --> <!-- Back to review -->
<iqser-circle-button <iqser-circle-button
(action)="setFileUnderReview($event, true)" (action)="setFileUnderReview($event, true)"
*ngIf="canSetToUnderReview" *ngIf="showUnderReview"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.under-review' | translate" [tooltip]="'dossier-overview.under-review' | translate"
[type]="buttonType" [type]="buttonType"
@ -87,7 +96,7 @@
<!-- Approved--> <!-- Approved-->
<iqser-circle-button <iqser-circle-button
(action)="setFileApproved($event)" (action)="setFileApproved($event)"
*ngIf="readyForApproval" *ngIf="showApprove"
[disabled]="!fileStatus.canBeApproved" [disabled]="!fileStatus.canBeApproved"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]=" [tooltip]="
@ -100,7 +109,7 @@
<!-- Back to approval --> <!-- Back to approval -->
<iqser-circle-button <iqser-circle-button
(action)="setFileUnderApproval($event)" (action)="setFileUnderApproval($event)"
*ngIf="canUndoApproval" *ngIf="showUndoApproval"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.under-approval' | translate" [tooltip]="'dossier-overview.under-approval' | translate"
[type]="buttonType" [type]="buttonType"
@ -109,7 +118,7 @@
<iqser-circle-button <iqser-circle-button
(action)="ocrFile($event)" (action)="ocrFile($event)"
*ngIf="fileStatus.canBeOCRed" *ngIf="showOCR"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.ocr-file' | translate" [tooltip]="'dossier-overview.ocr-file' | translate"
[type]="buttonType" [type]="buttonType"
@ -119,22 +128,22 @@
<!-- reanalyse file preview --> <!-- reanalyse file preview -->
<iqser-circle-button <iqser-circle-button
(action)="reanalyseFile($event)" (action)="reanalyseFile($event)"
*ngIf="canReanalyse && screen === 'file-preview'" *ngIf="canReanalyse && isFilePreview"
[tooltip]="'file-preview.reanalyse-notification' | translate" [tooltip]="'file-preview.reanalyse-notification' | translate"
[type]="circleButtonTypes.warn"
icon="iqser:refresh" icon="iqser:refresh"
tooltipClass="warn small" tooltipClass="warn small"
tooltipPosition="below" tooltipPosition="below"
[type]="circleButtonTypes.warn"
></iqser-circle-button> ></iqser-circle-button>
<!-- reanalyse file listing --> <!-- reanalyse file listing -->
<iqser-circle-button <iqser-circle-button
(action)="reanalyseFile($event)" (action)="reanalyseFile($event)"
*ngIf="canReanalyse && screen === 'dossier-overview'" *ngIf="canReanalyse && isDossierOverview"
[tooltipPosition]="tooltipPosition" [tooltipPosition]="tooltipPosition"
[tooltip]="'dossier-overview.reanalyse.action' | translate" [tooltip]="'dossier-overview.reanalyse.action' | translate"
icon="iqser:refresh"
[type]="circleButtonTypes.dark" [type]="circleButtonTypes.dark"
icon="iqser:refresh"
></iqser-circle-button> ></iqser-circle-button>
<!-- exclude from redaction --> <!-- exclude from redaction -->
<div class="iqser-input-group"> <div class="iqser-input-group">
@ -142,7 +151,7 @@
(change)="toggleAnalysis()" (change)="toggleAnalysis()"
(click)="$event.stopPropagation()" (click)="$event.stopPropagation()"
[checked]="!fileStatus?.excluded" [checked]="!fileStatus?.excluded"
[class.mr-24]="screen === 'dossier-overview'" [class.mr-24]="isDossierOverviewList"
[disabled]="!canToggleAnalysis" [disabled]="!canToggleAnalysis"
[matTooltipPosition]="tooltipPosition" [matTooltipPosition]="tooltipPosition"
[matTooltip]="toggleTooltip | translate" [matTooltip]="toggleTooltip | translate"

View File

@ -1,5 +1,11 @@
@use 'common-mixins';
@use 'variables';
.file-actions { .file-actions {
display: flex; display: flex;
overflow-y: auto;
color: variables.$grey-1;
@include common-mixins.no-scroll-bar;
> *:not(:last-child) { > *:not(:last-child) {
margin-right: 2px; margin-right: 2px;

View File

@ -5,7 +5,7 @@ import { AppStateService } from '@state/app-state.service';
import { FileActionService } from '../../services/file-action.service'; import { FileActionService } from '../../services/file-action.service';
import { DossiersDialogService } from '../../services/dossiers-dialog.service'; import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes, LoadingService, StatusBarConfig, Toaster } from '@iqser/common-ui'; import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes, LoadingService, Required, StatusBarConfig, Toaster } from '@iqser/common-ui';
import { FileManagementControllerService, FileStatus } from '@redaction/red-ui-http'; import { FileManagementControllerService, FileStatus } from '@redaction/red-ui-http';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
@ -25,25 +25,29 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
@Input() fileStatus: FileStatusWrapper; @Input() fileStatus: FileStatusWrapper;
@Input() activeDocumentInfo: boolean; @Input() activeDocumentInfo: boolean;
@Input() activeExcludePages: boolean; @Input() activeExcludePages: boolean;
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
@Output() actionPerformed = new EventEmitter<string>(); @Output() actionPerformed = new EventEmitter<string>();
screen: 'file-preview' | 'dossier-overview';
statusBarConfig?: readonly StatusBarConfig<FileStatus.StatusEnum>[]; statusBarConfig?: readonly StatusBarConfig<FileStatus.StatusEnum>[];
tooltipPosition?: 'below' | 'above'; tooltipPosition?: 'below' | 'above';
toggleTooltip?: string; toggleTooltip?: string;
assignTooltip?: string; assignTooltip?: string;
buttonType?: CircleButtonType; buttonType?: CircleButtonType;
isWorkable: boolean;
canUndoApproval: boolean; showUndoApproval: boolean;
canAssignToSelf: boolean; showAssignToSelf: boolean;
canAssign: boolean; showAssign: boolean;
canDelete: boolean; showDelete: boolean;
showOCR: boolean;
canReanalyse: boolean; canReanalyse: boolean;
canSetToUnderReview: boolean; showUnderReview: boolean;
canSetToUnderApproval: boolean; showUnderApproval: boolean;
readyForApproval: boolean; showApprove: boolean;
canToggleAnalysis: boolean; canToggleAnalysis: boolean;
showExcludePages: boolean;
showDocumentInfo: boolean;
showStatusBar: boolean;
showOpenDocument: boolean;
constructor( constructor(
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
@ -59,6 +63,22 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
super(); super();
} }
get isDossierOverviewList(): boolean {
return this.type === 'dossier-overview-list';
}
get isDossierOverviewWorkflow(): boolean {
return this.type === 'dossier-overview-workflow';
}
get isFilePreview(): boolean {
return this.type === 'file-preview';
}
get isDossierOverview(): boolean {
return this.type.startsWith('dossier-overview-list');
}
private get _toggleTooltip(): string { private get _toggleTooltip(): string {
if (!this.currentUser.isManager) { if (!this.currentUser.isManager) {
return _('file-preview.toggle-analysis.only-managers'); return _('file-preview.toggle-analysis.only-managers');
@ -68,13 +88,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
} }
ngOnInit(): void { ngOnInit(): void {
if (this.fileStatus) { if (!this.fileStatus) {
this.screen = 'dossier-overview';
} else {
this.fileStatus = this.appStateService.activeFile; this.fileStatus = this.appStateService.activeFile;
this.screen = 'file-preview';
} }
this._setup(); this._setup();
this.addSubscription = this.appStateService.fileChanged$ this.addSubscription = this.appStateService.fileChanged$
.pipe(filter(file => file.fileId === this.fileStatus?.fileId)) .pipe(filter(file => file.fileId === this.fileStatus?.fileId))
@ -96,6 +112,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
this.actionPerformed.emit('view-exclude-pages'); this.actionPerformed.emit('view-exclude-pages');
} }
openDocument() {
this.actionPerformed.emit('navigate');
}
openDeleteFileDialog($event: MouseEvent) { openDeleteFileDialog($event: MouseEvent) {
this._dialogService.openDialog( this._dialogService.openDialog(
'confirm', 'confirm',
@ -223,22 +243,31 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
private _setup() { private _setup() {
this.statusBarConfig = [{ color: this.fileStatus.status, length: 1 }]; this.statusBarConfig = [{ color: this.fileStatus.status, length: 1 }];
this.tooltipPosition = this.screen === 'file-preview' ? 'below' : 'above'; this.tooltipPosition = this.isFilePreview ? 'below' : 'above';
this.assignTooltip = this.fileStatus.isUnderApproval this.assignTooltip = this.fileStatus.isUnderApproval
? _('dossier-overview.assign-approver') ? _('dossier-overview.assign-approver')
: _('dossier-overview.assign-reviewer'); : _('dossier-overview.assign-reviewer');
this.buttonType = this.screen === 'file-preview' ? CircleButtonTypes.default : CircleButtonTypes.dark; this.buttonType = this.isFilePreview ? CircleButtonTypes.default : CircleButtonTypes.dark;
this.isWorkable = this.fileStatus.isWorkable;
this.toggleTooltip = this._toggleTooltip; this.toggleTooltip = this._toggleTooltip;
this.canUndoApproval = this.permissionsService.canUndoApproval(this.fileStatus); this.showUndoApproval = this.permissionsService.canUndoApproval(this.fileStatus) && !this.isDossierOverviewWorkflow;
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.fileStatus); this.showUnderReview = this.permissionsService.canSetUnderReview(this.fileStatus) && !this.isDossierOverviewWorkflow;
this.canAssign = this.permissionsService.canAssignUser(this.fileStatus); this.showUnderApproval = this.permissionsService.canSetUnderApproval(this.fileStatus) && !this.isDossierOverviewWorkflow;
this.canDelete = this.permissionsService.canDeleteFile(this.fileStatus); this.showApprove = this.permissionsService.isReadyForApproval(this.fileStatus) && !this.isDossierOverviewWorkflow;
this.canReanalyse = this.permissionsService.canReanalyseFile(this.fileStatus);
this.canSetToUnderReview = this.permissionsService.canSetUnderReview(this.fileStatus);
this.canSetToUnderApproval = this.permissionsService.canSetUnderApproval(this.fileStatus);
this.readyForApproval = this.permissionsService.isReadyForApproval(this.fileStatus);
this.canToggleAnalysis = this.permissionsService.canToggleAnalysis(this.fileStatus); this.canToggleAnalysis = this.permissionsService.canToggleAnalysis(this.fileStatus);
this.showDelete = this.permissionsService.canDeleteFile(this.fileStatus);
this.showOCR = this.fileStatus.canBeOCRed;
this.canReanalyse = this.permissionsService.canReanalyseFile(this.fileStatus);
this.showStatusBar = this.fileStatus.isWorkable && this.isDossierOverviewList;
this.showAssignToSelf = this.permissionsService.canAssignToSelf(this.fileStatus) && this.isDossierOverview;
this.showAssign = this.permissionsService.canAssignUser(this.fileStatus) && this.isDossierOverview;
this.showOpenDocument = this.fileStatus.canBeOpened && this.isDossierOverviewWorkflow;
this.showExcludePages = this.isFilePreview;
this.showDocumentInfo = this.isFilePreview;
} }
} }

View File

@ -33,10 +33,10 @@
<ng-template #nameTemplate let-dossier="entity"> <ng-template #nameTemplate let-dossier="entity">
<div class="cell"> <div class="cell">
<div [matTooltip]="dossier.dossierName" class="table-item-title heading" matTooltipPosition="above"> <div [matTooltip]="dossier.dossierName" class="table-item-title heading mb-6" matTooltipPosition="above">
{{ dossier.dossierName }} {{ dossier.dossierName }}
</div> </div>
<div class="small-label stats-subtitle"> <div class="small-label stats-subtitle mb-6">
<div> <div>
<mat-icon svgIcon="red:template"></mat-icon> <mat-icon svgIcon="red:template"></mat-icon>
{{ dossier.dossierTemplateName }} {{ dossier.dossierTemplateName }}

View File

@ -4,6 +4,7 @@
[actionConfigs]="actionConfigs" [actionConfigs]="actionConfigs"
[fileAttributeConfigs]="fileAttributeConfigs" [fileAttributeConfigs]="fileAttributeConfigs"
[showCloseButton]="true" [showCloseButton]="true"
[viewModeSelection]="viewModeSelection"
> >
<redaction-file-download-btn <redaction-file-download-btn
[dossier]="currentDossier" [dossier]="currentDossier"
@ -37,6 +38,7 @@
<div [class.extended]="collapsedDetails" class="content-container"> <div [class.extended]="collapsedDetails" class="content-container">
<iqser-table <iqser-table
(noDataAction)="fileInput.click()" (noDataAction)="fileInput.click()"
*ngIf="(listingMode$ | async) === listingModes.table"
[bulkActions]="bulkActions" [bulkActions]="bulkActions"
[hasScrollButton]="true" [hasScrollButton]="true"
[itemSize]="80" [itemSize]="80"
@ -49,6 +51,23 @@
noDataButtonIcon="red:upload" noDataButtonIcon="red:upload"
noDataIcon="red:document" noDataIcon="red:document"
></iqser-table> ></iqser-table>
<iqser-workflow
(addElement)="fileInput.click()"
(noDataAction)="fileInput.click()"
*ngIf="(listingMode$ | async) === listingModes.workflow"
[config]="workflowConfig"
[itemClasses]="{ disabled: disabledFn }"
[itemTemplate]="workflowItemTemplate"
[noDataButtonLabel]="'dossier-overview.no-data.action' | translate"
[noDataText]="'dossier-overview.no-data.title' | translate"
[showNoDataButton]="true"
addElementColumn="UNASSIGNED"
addElementIcon="red:upload"
itemHeight="56px"
noDataButtonIcon="red:upload"
noDataIcon="red:document"
></iqser-workflow>
</div> </div>
<div [class.collapsed]="collapsedDetails" class="right-container" iqserHasScrollbar> <div [class.collapsed]="collapsedDetails" class="right-container" iqserHasScrollbar>
@ -74,34 +93,19 @@
<ng-template #filenameTemplate let-fileStatus="entity"> <ng-template #filenameTemplate let-fileStatus="entity">
<div class="cell"> <div class="cell">
<div class="filename-wrapper"> <div>
<div [class.error]="fileStatus.isError" class="table-item-title text-overflow"> <div [class.error]="fileStatus.isError" [matTooltip]="fileStatus.filename" class="table-item-title" matTooltipPosition="above">
<span [matTooltip]="fileStatus.filename" matTooltipPosition="above"> {{ fileStatus.filename }}
{{ fileStatus.filename }}
</span>
</div> </div>
</div> </div>
<div *ngIf="fileStatus.primaryAttribute" class="small-label"> <div *ngIf="fileStatus.primaryAttribute" class="small-label">
<div class="primary-attribute text-overflow"> <div class="primary-attribute">
<span [matTooltip]="fileStatus.primaryAttribute" matTooltipPosition="above"> <span [matTooltip]="fileStatus.primaryAttribute" matTooltipPosition="above">
{{ fileStatus.primaryAttribute }} {{ fileStatus.primaryAttribute }}
</span> </span>
</div> </div>
</div> </div>
<div class="small-label stats-subtitle"> <ng-container *ngTemplateOutlet="statsTemplate; context: { entity: fileStatus }"></ng-container>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>
<div>
<mat-icon svgIcon="red:exclude-pages"></mat-icon>
{{ fileStatus.excludedPagesCount }}
</div>
<div *ngIf="fileStatus.lastOCRTime" [matTooltipPosition]="'above'" [matTooltip]="'dossier-overview.ocr-performed' | translate">
<mat-icon svgIcon="red:ocr"></mat-icon>
{{ fileStatus.lastOCRTime | date: 'mediumDate' }}
</div>
</div>
</div> </div>
</ng-template> </ng-template>
@ -167,10 +171,69 @@
></iqser-status-bar> ></iqser-status-bar>
<redaction-file-actions <redaction-file-actions
(actionPerformed)="calculateData()" (actionPerformed)="actionPerformed()"
*ngIf="!fileStatus.isProcessing" *ngIf="!fileStatus.isProcessing"
[fileStatus]="fileStatus" [fileStatus]="fileStatus"
class="mr-4" class="mr-4"
type="dossier-overview-list"
></redaction-file-actions> ></redaction-file-actions>
</div> </div>
</ng-template> </ng-template>
<ng-template #viewModeSelection>
<div class="view-mode-selection">
<div class="all-caps-label" translate="view-mode.view-as"></div>
<iqser-circle-button
(action)="listingMode = listingModes.workflow"
[attr.aria-expanded]="(listingMode$ | async) === listingModes.workflow"
[tooltip]="'view-mode.workflow' | translate"
icon="iqser:lanes"
></iqser-circle-button>
<iqser-circle-button
(action)="listingMode = listingModes.table"
[attr.aria-expanded]="(listingMode$ | async) === listingModes.table"
[tooltip]="'view-mode.list' | translate"
icon="iqser:list"
></iqser-circle-button>
</div>
</ng-template>
<ng-template #workflowItemTemplate let-fileStatus="entity">
<div class="workflow-item">
<div>
<div class="details">
<div [matTooltip]="fileStatus.filename" class="filename" matTooltipPosition="above">
{{ fileStatus.filename }}
</div>
<ng-container *ngTemplateOutlet="statsTemplate; context: { entity: fileStatus }"></ng-container>
</div>
<div class="user">
<redaction-initials-avatar [userId]="fileStatus.currentReviewer"></redaction-initials-avatar>
</div>
</div>
<redaction-file-actions
(actionPerformed)="actionPerformed($event, fileStatus)"
*ngIf="!fileStatus.isProcessing"
[fileStatus]="fileStatus"
type="dossier-overview-workflow"
></redaction-file-actions>
</div>
</ng-template>
<ng-template #statsTemplate let-fileStatus="entity">
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>
<div>
<mat-icon svgIcon="red:exclude-pages"></mat-icon>
{{ fileStatus.excludedPagesCount }}
</div>
<div *ngIf="fileStatus.lastOCRTime" [matTooltipPosition]="'above'" [matTooltip]="'dossier-overview.ocr-performed' | translate">
<mat-icon svgIcon="red:ocr"></mat-icon>
{{ fileStatus.lastOCRTime | date: 'mediumDate' }}
</div>
</div>
</ng-template>

View File

@ -1,4 +1,5 @@
@use 'variables'; @use 'variables';
@use 'common-mixins';
.file-upload-input { .file-upload-input {
display: none; display: none;
@ -27,6 +28,7 @@
.primary-attribute { .primary-attribute {
padding-top: 6px; padding-top: 6px;
@include common-mixins.line-clamp(1);
} }
&.extend-cols { &.extend-cols {
@ -63,3 +65,56 @@
background-color: inherit; background-color: inherit;
} }
} }
.view-mode-selection {
border-right: 1px solid variables.$separator;
padding-right: 16px;
margin-right: 16px !important;
display: flex;
align-items: center;
> iqser-circle-button:not(:last-child) {
margin-right: 2px;
}
> div {
margin-right: 8px;
}
}
.workflow-item {
padding: 10px;
> div {
display: flex;
justify-content: space-between;
.details {
max-width: calc(100% - 28px);
.filename {
font-weight: 600;
line-height: 18px;
@include common-mixins.line-clamp(1);
}
}
.user {
display: flex;
align-items: flex-end;
}
}
redaction-file-actions {
margin-top: 10px;
display: none;
}
&:hover redaction-file-actions {
display: block;
}
}
.stats-subtitle {
margin-top: 4px;
}

View File

@ -10,6 +10,7 @@ import {
TemplateRef, TemplateRef,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { FileAttributeConfig, FileStatus } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service'; import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
import { FileUploadModel } from '@upload-download/model/file-upload.model'; import { FileUploadModel } from '@upload-download/model/file-upload.model';
@ -34,11 +35,13 @@ import {
DefaultListingServices, DefaultListingServices,
keyChecker, keyChecker,
ListingComponent, ListingComponent,
ListingModes,
LoadingService, LoadingService,
NestedFilter, NestedFilter,
TableColumnConfig, TableColumnConfig,
TableComponent, TableComponent,
Toaster Toaster,
WorkflowConfig
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service'; import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model'; import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
@ -49,8 +52,10 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { annotationFilterChecker } from '@utils/filter-utils'; import { annotationFilterChecker } from '@utils/filter-utils';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { RouterHistoryService } from '@services/router-history.service'; import { RouterHistoryService } from '@services/router-history.service';
import { FileAttributeConfig } from '@redaction/red-ui-http';
import { DossierWrapper } from '@state/model/dossier.wrapper'; import { DossierWrapper } from '@state/model/dossier.wrapper';
import { Router } from '@angular/router';
import { FileActionService } from '../../services/file-action.service';
import StatusEnum = FileStatus.StatusEnum;
@Component({ @Component({
templateUrl: './dossier-overview-screen.component.html', templateUrl: './dossier-overview-screen.component.html',
@ -58,6 +63,7 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }] providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }]
}) })
export class DossierOverviewScreenComponent extends ListingComponent<FileStatusWrapper> implements OnInit, OnDestroy, OnDetach, OnAttach { export class DossierOverviewScreenComponent extends ListingComponent<FileStatusWrapper> implements OnInit, OnDestroy, OnDetach, OnAttach {
readonly listingModes = ListingModes;
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser; readonly currentUser = this._userService.currentUser;
currentDossier = this._appStateService.activeDossier; currentDossier = this._appStateService.activeDossier;
@ -81,6 +87,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
@ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef<never>; @ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef<never>;
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>; @ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>; @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>;
readonly workflowConfig: WorkflowConfig<FileStatusWrapper, StatusEnum>;
protected readonly _primaryKey = 'filename'; protected readonly _primaryKey = 'filename';
@ViewChild(DossierDetailsComponent, { static: false }) @ViewChild(DossierDetailsComponent, { static: false })
private readonly _dossierDetailsComponent: DossierDetailsComponent; private readonly _dossierDetailsComponent: DossierDetailsComponent;
@ -93,6 +100,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
constructor( constructor(
private readonly _toaster: Toaster, private readonly _toaster: Toaster,
protected readonly _injector: Injector, protected readonly _injector: Injector,
private readonly _router: Router,
private readonly _userService: UserService, private readonly _userService: UserService,
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
@ -106,13 +114,52 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
private readonly _statusOverlayService: StatusOverlayService, private readonly _statusOverlayService: StatusOverlayService,
private readonly _userPreferenceService: UserPreferenceService, private readonly _userPreferenceService: UserPreferenceService,
private readonly _fileDropOverlayService: FileDropOverlayService, private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _dossierAttributesService: DossierAttributesService private readonly _dossierAttributesService: DossierAttributesService,
private readonly _fileActionService: FileActionService
) { ) {
super(_injector); super(_injector);
this._loadEntitiesFromState(); this._loadEntitiesFromState();
this.fileAttributeConfigs = this._appStateService.getFileAttributeConfig( this.fileAttributeConfigs = this._appStateService.getFileAttributeConfig(
this.currentDossier.dossierTemplateId this.currentDossier.dossierTemplateId
).fileAttributeConfigs; ).fileAttributeConfigs;
this.workflowConfig = {
columnIdentifierFn: entity => entity.status,
itemVersionFn: (entity: FileStatusWrapper) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
columns: [
{
label: fileStatusTranslations[StatusEnum.UNASSIGNED],
key: StatusEnum.UNASSIGNED,
enterFn: this.unassignFn,
enterPredicate: (entity: FileStatusWrapper) => false,
color: '#D3D5DA'
},
{
label: fileStatusTranslations[StatusEnum.UNDERREVIEW],
enterFn: this.underReviewFn,
enterPredicate: (file: FileStatusWrapper) =>
this.permissionsService.canSetUnderReview(file) ||
this.permissionsService.canAssignToSelf(file) ||
this.permissionsService.canAssignUser(file),
key: StatusEnum.UNDERREVIEW,
color: '#FDBD00'
},
{
label: fileStatusTranslations[StatusEnum.UNDERAPPROVAL],
enterFn: this.underApprovalFn,
enterPredicate: (file: FileStatusWrapper) =>
this.permissionsService.canSetUnderApproval(file) || this.permissionsService.canUndoApproval(file),
key: StatusEnum.UNDERAPPROVAL,
color: '#374C81'
},
{
label: fileStatusTranslations[StatusEnum.APPROVED],
enterFn: this.approveFn,
enterPredicate: (file: FileStatusWrapper) => this.permissionsService.isReadyForApproval(file),
key: StatusEnum.APPROVED,
color: '#48C9F7'
}
]
};
} }
get checkedRequiredFilters() { get checkedRequiredFilters() {
@ -127,10 +174,46 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
return this.fileAttributeConfigs.filter(config => config.displayedInFileList); return this.fileAttributeConfigs.filter(config => config.displayedInFileList);
} }
unassignFn = async (file: FileStatusWrapper) => {
// TODO
console.log('unassign', file);
};
underReviewFn = (file: FileStatusWrapper) => {
this._fileActionService.assignFile('reviewer', null, file, () => this._loadingService.loadWhile(this.reloadDossiers()), true);
};
underApprovalFn = async (file: FileStatusWrapper) => {
if (this._appStateService.activeDossier.approverIds.length > 1) {
this._fileActionService.assignFile('approver', null, file, () => this._loadingService.loadWhile(this.reloadDossiers()), true);
} else {
this._loadingService.start();
await this._fileActionService.setFileUnderApproval(file).toPromise();
await this.reloadDossiers();
this._loadingService.stop();
}
};
approveFn = async (file: FileStatusWrapper) => {
this._loadingService.start();
await this._fileActionService.setFileApproved(file).toPromise();
await this.reloadDossiers();
this._loadingService.stop();
};
actionPerformed(action?: string, fileStatus?: FileStatusWrapper) {
this._calculateData();
if (action === 'navigate') {
this._router.navigate(this.routerLinkFn(fileStatus));
}
}
routerLinkFn = (fileStatus: FileStatusWrapper) => routerLinkFn = (fileStatus: FileStatusWrapper) =>
fileStatus.canBeOpened ? [`/main/dossiers/${this.currentDossier.dossierId}/file/${fileStatus.fileId}`] : []; fileStatus.canBeOpened ? [`/main/dossiers/${this.currentDossier.dossierId}/file/${fileStatus.fileId}`] : [];
disabledFn = (fileStatus: FileStatusWrapper) => fileStatus.excluded; disabledFn = (fileStatus: FileStatusWrapper) => fileStatus.excluded;
lastOpenedFn = (fileStatus: FileStatusWrapper) => fileStatus.lastOpened; lastOpenedFn = (fileStatus: FileStatusWrapper) => fileStatus.lastOpened;
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
@ -139,7 +222,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
try { try {
this._fileDropOverlayService.initFileDropHandling(); this._fileDropOverlayService.initFileDropHandling();
this.calculateData(); this._calculateData();
this.addSubscription = timer(0, 7500).subscribe(async () => { this.addSubscription = timer(0, 7500).subscribe(async () => {
await this._appStateService.reloadActiveDossierFilesIfNecessary(); await this._appStateService.reloadActiveDossierFilesIfNecessary();
@ -147,7 +230,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
}); });
this.addSubscription = this._appStateService.fileChanged$.subscribe(() => { this.addSubscription = this._appStateService.fileChanged$.subscribe(() => {
this.calculateData(); this._calculateData();
}); });
this.addSubscription = this._appStateService.dossierTemplateChanged$.subscribe(() => { this.addSubscription = this._appStateService.dossierTemplateChanged$.subscribe(() => {
@ -197,10 +280,10 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
async reloadDossiers() { async reloadDossiers() {
await this._appStateService.getFiles(this.currentDossier, false); await this._appStateService.getFiles(this.currentDossier, false);
this.calculateData(); this._calculateData();
} }
calculateData(): void { _calculateData(): void {
if (!this._appStateService.activeDossierId) { if (!this._appStateService.activeDossierId) {
return; return;
} }

View File

@ -92,6 +92,7 @@
(actionPerformed)="fileActionPerformed($event)" (actionPerformed)="fileActionPerformed($event)"
[activeDocumentInfo]="viewDocumentInfo" [activeDocumentInfo]="viewDocumentInfo"
[activeExcludePages]="excludePages" [activeExcludePages]="excludePages"
type="file-preview"
></redaction-file-actions> ></redaction-file-actions>
<iqser-circle-button <iqser-circle-button
@ -107,10 +108,10 @@
(action)="downloadOriginalFile()" (action)="downloadOriginalFile()"
*ngIf="userPreferenceService.areDevFeaturesEnabled" *ngIf="userPreferenceService.areDevFeaturesEnabled"
[tooltip]="'file-preview.download-original-file' | translate" [tooltip]="'file-preview.download-original-file' | translate"
[type]="circleButtonTypes.primary"
class="ml-8" class="ml-8"
icon="red:download" icon="red:download"
tooltipPosition="below" tooltipPosition="below"
[type]="circleButtonTypes.primary"
></iqser-circle-button> ></iqser-circle-button>
<!-- End Dev Mode Features--> <!-- End Dev Mode Features-->

View File

@ -8,6 +8,7 @@ import { isArray } from 'rxjs/internal-compatibility';
import { DossiersDialogService } from './dossiers-dialog.service'; import { DossiersDialogService } from './dossiers-dialog.service';
import { ConfirmationDialogInput } from '../../shared/dialogs/confirmation-dialog/confirmation-dialog.component'; import { ConfirmationDialogInput } from '../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class FileActionService { export class FileActionService {
@ -83,7 +84,7 @@ export class FileActionService {
); );
} }
setFileApproved(fileStatus: FileStatusWrapper | FileStatusWrapper[]) { setFileApproved(fileStatus: FileStatusWrapper | FileStatusWrapper[]): Observable<any> {
if (!isArray(fileStatus)) { if (!isArray(fileStatus)) {
fileStatus = [fileStatus]; fileStatus = [fileStatus];
} }
@ -116,11 +117,7 @@ export class FileActionService {
assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file?: FileStatusWrapper, callback?: Function, ignoreChanged = false) { assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file?: FileStatusWrapper, callback?: Function, ignoreChanged = false) {
const files = file ? [file] : [this._appStateService.activeFile]; const files = file ? [file] : [this._appStateService.activeFile];
const data = { mode, files, ignoreChanged }; const data = { mode, files, ignoreChanged };
this._dialogService.openDialog('assignFile', $event, data, async () => { this._dialogService.openDialog('assignFile', $event, data, callback);
if (callback) {
callback();
}
});
} }
private async _assignReviewerToCurrentUser(fileStatus: FileStatusWrapper | FileStatusWrapper[], callback?: Function) { private async _assignReviewerToCurrentUser(fileStatus: FileStatusWrapper | FileStatusWrapper[], callback?: Function) {

View File

@ -15,7 +15,9 @@
<div (click)="resetFilters()" *ngIf="showResetFilters$ | async" class="reset-filters" translate="reset-filters"></div> <div (click)="resetFilters()" *ngIf="showResetFilters$ | async" class="reset-filters" translate="reset-filters"></div>
</div> </div>
<div *ngIf="showCloseButton || actionConfigs || buttonConfigs" class="actions"> <div *ngIf="showCloseButton || actionConfigs || buttonConfigs || viewModeSelection" class="actions">
<ng-container [ngTemplateOutlet]="viewModeSelection"></ng-container>
<ng-container *ngFor="let config of buttonConfigs; trackBy: trackByLabel"> <ng-container *ngFor="let config of buttonConfigs; trackBy: trackByLabel">
<iqser-icon-button <iqser-icon-button
(action)="config.action($event)" (action)="config.action($event)"
@ -53,10 +55,10 @@
<ng-template #searchBar> <ng-template #searchBar>
<iqser-input-with-action <iqser-input-with-action
[(value)]="searchService.searchValue"
*ngIf="searchPlaceholder && searchService" *ngIf="searchPlaceholder && searchService"
[(value)]="searchService.searchValue"
[class.mr-8]="searchPosition === searchPositions.beforeFilters"
[placeholder]="searchPlaceholder" [placeholder]="searchPlaceholder"
[width]="searchWidth" [width]="searchWidth"
[class.mr-8]="searchPosition === searchPositions.beforeFilters"
></iqser-input-with-action> ></iqser-input-with-action>
</ng-template> </ng-template>

View File

@ -1,3 +1,5 @@
@import '../../../../../assets/styles/variables';
.ml-6 { .ml-6 {
margin-left: 6px; margin-left: 6px;
} }

View File

@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, Optional, Output } from '@angular/core'; import { Component, EventEmitter, Input, Optional, Output, TemplateRef } from '@angular/core';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model'; import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model'; import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { FilterService, SearchService } from '@iqser/common-ui'; import { FilterService, IconButtonTypes, Listable, SearchService } from '@iqser/common-ui';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { combineLatest, Observable, of } from 'rxjs'; import { combineLatest, Observable, of } from 'rxjs';
import { SearchPosition, SearchPositions } from '@shared/components/page-header/models/search-positions.type'; import { SearchPosition, SearchPositions } from '@shared/components/page-header/models/search-positions.type';
@ -12,13 +12,15 @@ import { FileAttributeConfig } from '@redaction/red-ui-http';
templateUrl: './page-header.component.html', templateUrl: './page-header.component.html',
styleUrls: ['./page-header.component.scss'] styleUrls: ['./page-header.component.scss']
}) })
export class PageHeaderComponent<T> { export class PageHeaderComponent<T extends Listable> {
readonly searchPositions = SearchPositions; readonly searchPositions = SearchPositions;
readonly iconButtonTypes = IconButtonTypes;
@Input() pageLabel: string; @Input() pageLabel: string;
@Input() showCloseButton: boolean; @Input() showCloseButton: boolean;
@Input() actionConfigs: readonly ActionConfig[]; @Input() actionConfigs: readonly ActionConfig[];
@Input() buttonConfigs: readonly ButtonConfig[]; @Input() buttonConfigs: readonly ButtonConfig[];
@Input() viewModeSelection: TemplateRef<unknown>;
@Input() fileAttributeConfigs: readonly FileAttributeConfig[]; @Input() fileAttributeConfigs: readonly FileAttributeConfig[];
@Input() searchPlaceholder: string; @Input() searchPlaceholder: string;
@Input() searchWidth: number | 'full'; @Input() searchWidth: number | 'full';

View File

@ -69,5 +69,4 @@ const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, Rea
} }
] ]
}) })
export class SharedModule { export class SharedModule {}
}

View File

@ -688,6 +688,7 @@
}, },
"ocr-file": "OCR Document", "ocr-file": "OCR Document",
"ocr-performed": "OCR was performed for this file.", "ocr-performed": "OCR was performed for this file.",
"open-document": "Open Document",
"quick-filters": { "quick-filters": {
"assigned-to-me": "Assigned to me", "assigned-to-me": "Assigned to me",
"assigned-to-others": "Assigned to others", "assigned-to-others": "Assigned to others",
@ -1502,6 +1503,11 @@
"expand": "Show Details", "expand": "Show Details",
"title": "Users" "title": "Users"
}, },
"view-mode": {
"list": "List",
"view-as": "View as:",
"workflow": "Workflow"
},
"watermark": "Watermark", "watermark": "Watermark",
"watermark-screen": { "watermark-screen": {
"action": { "action": {

View File

@ -36,7 +36,6 @@ $dark: $black;
$btn-bg-hover: $grey-4; $btn-bg-hover: $grey-4;
$btn-bg: $grey-6; $btn-bg: $grey-6;
$filter-bg: $grey-2;
$quick-filter-border: $grey-5; $quick-filter-border: $grey-5;
$separator: rgba(226, 228, 233, 0.9); $separator: rgba(226, 228, 233, 0.9);

View File

@ -81,6 +81,8 @@
mat-icon { mat-icon {
width: 10px; width: 10px;
height: 10px;
line-height: 13px;
margin-right: 6px; margin-right: 6px;
} }

View File

@ -241,10 +241,18 @@ section.settings {
margin-top: 32px; margin-top: 32px;
} }
.mb-6 {
margin-bottom: 6px;
}
.mb-8 { .mb-8 {
margin-bottom: 8px !important; margin-bottom: 8px !important;
} }
.mb-12 {
margin-bottom: 12px !important;
}
.ml-8 { .ml-8 {
margin-left: 8px; margin-left: 8px;
} }

View File

@ -18,7 +18,11 @@
$iqser-white: vars.$white, $iqser-white: vars.$white,
$iqser-separator: vars.$separator, $iqser-separator: vars.$separator,
$iqser-quick-filter-border: vars.$quick-filter-border, $iqser-quick-filter-border: vars.$quick-filter-border,
$iqser-filter-bg: vars.$filter-bg, $iqser-grey-2: vars.$grey-2,
$iqser-grey-3: vars.$grey-3,
$iqser-grey-4: vars.$grey-4,
$iqser-grey-5: vars.$grey-5,
$iqser-grey-6: vars.$grey-6,
$iqser-helpmode-primary: vars.$green-2 $iqser-helpmode-primary: vars.$green-2
); );

@ -1 +1 @@
Subproject commit ea82e4351f217045e064ff10ee73ebbd190f330d Subproject commit 0dc6d05cef931e2682bb9584ad0d0cb31144e5c6

View File

@ -1,388 +1,388 @@
@charset "UTF-8"; @charset "UTF-8";
@font-face { @font-face {
font-family: "OpenSans Extrabold"; font-family: 'OpenSans Extrabold';
src: url("../../fonts/OpenSans-ExtraBold.ttf") format("truetype"); src: url('../../fonts/OpenSans-ExtraBold.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: "Inter"; font-family: 'Inter';
font-weight: 300; font-weight: 300;
src: url("../../fonts/Inter-VariableFont.ttf") format("truetype"); src: url('../../fonts/Inter-VariableFont.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: "Inter"; font-family: 'Inter';
font-weight: 400; font-weight: 400;
src: url("../../fonts/Inter-VariableFont.ttf") format("truetype"); src: url('../../fonts/Inter-VariableFont.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: "Inter"; font-family: 'Inter';
font-weight: 500; font-weight: 500;
src: url("../../fonts/Inter-VariableFont.ttf") format("truetype"); src: url('../../fonts/Inter-VariableFont.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: "Inter"; font-family: 'Inter';
font-weight: 600; font-weight: 600;
src: url("../../fonts/Inter-VariableFont.ttf") format("truetype"); src: url('../../fonts/Inter-VariableFont.ttf') format('truetype');
} }
@font-face { @font-face {
font-family: "Inter"; font-family: 'Inter';
font-weight: 700; font-weight: 700;
src: url("../../font/Inter-VariableFont.ttf") format("truetype"); src: url('../../font/Inter-VariableFont.ttf') format('truetype');
} }
.portal-header { .portal-header {
z-index: 1; z-index: 1;
height: 450px; height: 450px;
} }
.portal-header::before { .portal-header::before {
background-color: #283241; background-color: #283241;
} }
.portal-header h1 { .portal-header h1 {
font-size: 64px; font-size: 64px;
font-weight: 300; font-weight: 300;
line-height: 87px; line-height: 87px;
margin-top: 68px; margin-top: 68px;
margin-bottom: 40px; margin-bottom: 40px;
} }
.portal-header .portal-search { .portal-header .portal-search {
max-width: 600px; max-width: 600px;
margin: auto; margin: auto;
position: relative; position: relative;
} }
.portal-header .portal-search .search-field { .portal-header .portal-search .search-field {
width: 100%; width: 100%;
border: 1px solid #d3d5da; border: 1px solid #d3d5da;
border-radius: 8px; border-radius: 8px;
background-color: #fff; background-color: #fff;
} }
.portal-header .portal-search .search-field::placeholder { .portal-header .portal-search .search-field::placeholder {
opacity: 0.7; opacity: 0.7;
} }
.portal-header .portal-search .search-field, .portal-header .portal-search .search-field,
.portal-header .portal-search .search-field::placeholder { .portal-header .portal-search .search-field::placeholder {
color: #283241; color: #283241;
font-size: 14px; font-size: 14px;
line-height: 18px; line-height: 18px;
} }
.portal-header .portal-search .search-field { .portal-header .portal-search .search-field {
padding: 12px 17px; padding: 12px 17px;
} }
.portal-header .portal-search .btn { .portal-header .portal-search .btn {
position: absolute; position: absolute;
right: 0; right: 0;
padding: 11px 18px; padding: 11px 18px;
background-color: transparent; background-color: transparent;
color: #283241; color: #283241;
cursor: pointer; cursor: pointer;
border-radius: 0 8px 8px 0; border-radius: 0 8px 8px 0;
} }
.portal-header .portal-search .btn:hover { .portal-header .portal-search .btn:hover {
background-color: #dd4d50; background-color: #dd4d50;
} }
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
.portal-header h1 { .portal-header h1 {
font-size: 42px; font-size: 42px;
font-weight: 300; font-weight: 300;
line-height: 57px; line-height: 57px;
} }
} }
.featured-content-label { .featured-content-label {
display: none; display: none;
} }
.featured-content { .featured-content {
display: none; display: none;
} }
.portal-single-publication .publication-icon { .portal-single-publication .publication-icon {
background-color: #dd4d50; background-color: #dd4d50;
} }
.portal-contents { .portal-contents {
margin-top: 100px; margin-top: 100px;
margin-bottom: 0; margin-bottom: 0;
} }
.portal-contents .inner { .portal-contents .inner {
margin: 0; margin: 0;
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
grid-gap: 24px; grid-gap: 24px;
} }
.portal-contents .inner::before { .portal-contents .inner::before {
content: none; content: none;
} }
.publication-contents { .publication-contents {
padding: 24px 40px; padding: 24px 40px;
border: 1px solid #e2e4e9; border: 1px solid #e2e4e9;
width: 100%; width: 100%;
margin: 0; margin: 0;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
} }
.publication-contents:first-child { .publication-contents:first-child {
grid-column: 1/span 2; grid-column: 1 / span 2;
} }
.publication-contents h4.featured-title, .publication-contents h4.featured-title,
.publication-contents .section-toc-title { .publication-contents .section-toc-title {
font-size: 26px; font-size: 26px;
font-weight: 300; font-weight: 300;
line-height: 36px; line-height: 36px;
margin: 0; margin: 0;
} }
.publication-contents h4.featured-title a, .publication-contents h4.featured-title a,
.publication-contents .section-toc-title a { .publication-contents .section-toc-title a {
color: #283241; color: #283241;
} }
.publication-contents h4.featured-title a:hover, .publication-contents h4.featured-title a:hover,
.publication-contents .section-toc-title a:hover { .publication-contents .section-toc-title a:hover {
color: #283241; color: #283241;
text-decoration: underline; text-decoration: underline;
} }
.publication-contents ul { .publication-contents ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.publication-contents li { .publication-contents li {
margin: 4px 0; margin: 4px 0;
} }
.publication-contents li:first-child { .publication-contents li:first-child {
margin-top: 20px; margin-top: 20px;
} }
.publication-contents li:last-child { .publication-contents li:last-child {
margin-bottom: 40px; margin-bottom: 40px;
} }
.publication-contents li a { .publication-contents li a {
color: #dd4d50; color: #dd4d50;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
.publication-contents li a:hover { .publication-contents li a:hover {
color: #dd4d50; color: #dd4d50;
text-decoration: underline; text-decoration: underline;
} }
.publication-contents h4 span, .publication-contents h4 span,
.publication-contents li::before { .publication-contents li::before {
display: none; display: none;
} }
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
.portal-contents .inner { .portal-contents .inner {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.publication-contents:first-child { .publication-contents:first-child {
grid-column: auto; grid-column: auto;
} }
} }
/* Einleitung */ /* Einleitung */
.cat-panel-1:before { .cat-panel-1:before {
content: "\f277"; content: '\f277';
} }
/* Schnellstart */ /* Schnellstart */
.cat-panel-2:before { .cat-panel-2:before {
content: "\f11e"; content: '\f11e';
} }
/* Voraussetzungen */ /* Voraussetzungen */
.cat-panel-3:before { .cat-panel-3:before {
content: "\f109"; content: '\f109';
} }
/* Benutzermenü und -profil */ /* Benutzermenü und -profil */
.cat-panel-4:before { .cat-panel-4:before {
content: "\f007"; content: '\f007';
} }
/* Benutzeroberflächen im RedactManager */ /* Benutzeroberflächen im RedactManager */
.cat-panel-5:before { .cat-panel-5:before {
content: "\f0d0"; content: '\f0d0';
} }
/* Rollen und Berechtigungen */ /* Rollen und Berechtigungen */
.cat-panel-6:before { .cat-panel-6:before {
content: "\f084"; content: '\f084';
} }
/* Dossier erstellen und verwalten */ /* Dossier erstellen und verwalten */
.cat-panel-7:before { .cat-panel-7:before {
content: "\f07c"; content: '\f07c';
} }
/* Dokumente bearbeiten im Editor */ /* Dokumente bearbeiten im Editor */
.cat-panel-8:before { .cat-panel-8:before {
content: "\f15c"; content: '\f15c';
} }
/* Dossier abschließen und herunterladen */ /* Dossier abschließen und herunterladen */
.cat-panel-9:before { .cat-panel-9:before {
content: "\f019"; content: '\f019';
} }
/* Funktionsübersicht */ /* Funktionsübersicht */
.cat-panel-10:before { .cat-panel-10:before {
content: "\f03a"; content: '\f03a';
} }
/* Glossar */ /* Glossar */
.cat-panel-11:before { .cat-panel-11:before {
content: "\f02d"; content: '\f02d';
} }
/* FAQs (häufige Fragen) */ /* FAQs (häufige Fragen) */
.cat-panel-12:before { .cat-panel-12:before {
content: "\f059"; content: '\f059';
} }
.portal-search-result { .portal-search-result {
background-color: #f5f5f7; background-color: #f5f5f7;
} }
.search-container { .search-container {
padding-bottom: 100px; padding-bottom: 100px;
} }
.search-container h2 { .search-container h2 {
font-size: 42px; font-size: 42px;
font-weight: 300; font-weight: 300;
line-height: 57px; line-height: 57px;
} }
.portal-search-result { .portal-search-result {
padding: 0; padding: 0;
margin-top: 80px; margin-top: 80px;
} }
ul.searchresults { ul.searchresults {
border: 1px solid #e2e4e9; border: 1px solid #e2e4e9;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
margin-top: 32px; margin-top: 32px;
} }
ul.searchresults .search-highlight { ul.searchresults .search-highlight {
font-style: normal; font-style: normal;
} }
li.searchresultitem { li.searchresultitem {
margin: 0 32px; margin: 0 32px;
border-bottom: 1px solid #e2e4e9; border-bottom: 1px solid #e2e4e9;
padding: 32px 8px; padding: 32px 8px;
} }
.searchresultitem.selected-searchresultitem { .searchresultitem.selected-searchresultitem {
background-color: transparent; background-color: transparent;
border-radius: 0; border-radius: 0;
} }
.searchresulttitle { .searchresulttitle {
font-size: 26px; font-size: 26px;
font-weight: 300; font-weight: 300;
line-height: 36px; line-height: 36px;
color: #283241; color: #283241;
} }
.searchresultsnippet { .searchresultsnippet {
margin: 16px 0; margin: 16px 0;
color: #283241; color: #283241;
} }
.search-result-breadcrumbs { .search-result-breadcrumbs {
color: #dd4d50; color: #dd4d50;
} }
.portal-footer, .portal-footer,
.site-footer { .site-footer {
border-top: 1px solid #e2e4e9; border-top: 1px solid #e2e4e9;
padding: 0; padding: 0;
} }
.portal-footer.portal-footer, .portal-footer.portal-footer,
.site-footer.portal-footer { .site-footer.portal-footer {
margin-top: 100px; margin-top: 100px;
} }
.portal-footer .inner, .portal-footer .inner,
.site-footer .inner { .site-footer .inner {
margin: 0; margin: 0;
padding: 8px 0 64px 0; padding: 8px 0 64px 0;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
.portal-footer .inner > *, .portal-footer .inner > *,
.site-footer .inner > * { .site-footer .inner > * {
padding: 0; padding: 0;
} }
.toolbar { .toolbar {
box-shadow: none; box-shadow: none;
padding: 21px 24px; padding: 21px 24px;
margin-bottom: 50px; margin-bottom: 50px;
} }
.topic-content .breadcrumb-container { .topic-content .breadcrumb-container {
margin-top: 40px; margin-top: 40px;
} }
.topic-content .breadcrumb { .topic-content .breadcrumb {
font-size: 14px; font-size: 14px;
line-height: 18px; line-height: 18px;
font-weight: 600; font-weight: 600;
} }
.topic-content .breadcrumb a { .topic-content .breadcrumb a {
color: #283241; color: #283241;
} }
.topic-content .breadcrumb a:hover { .topic-content .breadcrumb a:hover {
color: #dd4d50; color: #dd4d50;
text-decoration: underline; text-decoration: underline;
} }
.topic-content .breadcrumb .breadcrumb-node { .topic-content .breadcrumb .breadcrumb-node {
color: #dd4d50; color: #dd4d50;
} }
main article { main article {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
padding: 0; padding: 0;
} }
section > .titlepage .title { section > .titlepage .title {
margin: 24px 0 16px 0; margin: 24px 0 16px 0;
} }
#topic-content > section > .titlepage h2.title { #topic-content > section > .titlepage h2.title {
margin: 0 0 24px; margin: 0 0 24px;
font-size: 32px; font-size: 32px;
font-weight: 300; font-weight: 300;
line-height: 43px; line-height: 43px;
} }
.image-viewport { .image-viewport {
margin: auto; margin: auto;
} }
.image-viewport img { .image-viewport img {
margin: 16px auto; margin: 16px auto;
box-shadow: 0 3px 12px 5px rgba(40, 50, 65, 0.14); box-shadow: 0 3px 12px 5px rgba(40, 50, 65, 0.14);
} }
.pager { .pager {
margin-top: 30px; margin-top: 30px;
margin-bottom: 30px; margin-bottom: 30px;
padding: 0; padding: 0;
} }
.pager li > a, .pager li > a,
.pager li > span { .pager li > span {
color: #dd4d50; color: #dd4d50;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
line-height: 19px; line-height: 19px;
text-transform: uppercase; text-transform: uppercase;
padding: 0; padding: 0;
background-color: transparent; background-color: transparent;
border: none; border: none;
border-radius: 0; border-radius: 0;
} }
.pager li > a:hover, .pager li > a:hover,
.pager li > span:hover { .pager li > span:hover {
text-decoration: underline; text-decoration: underline;
background-color: transparent; background-color: transparent;
color: #dd4d50; color: #dd4d50;
} }
.warning, .warning,
@ -390,152 +390,152 @@ section > .titlepage .title {
.important, .important,
.caution, .caution,
.tip { .tip {
margin-top: 32px; margin-top: 32px;
margin-bottom: 32px; margin-bottom: 32px;
padding: 16px 24px 16px 68px; padding: 16px 24px 16px 68px;
background-color: #fff; background-color: #fff;
border-left: 4px solid #dd4d50; border-left: 4px solid #dd4d50;
} }
.warning:before, .warning:before,
.note:before, .note:before,
.important:before, .important:before,
.caution:before, .caution:before,
.tip:before { .tip:before {
color: #dd4d50; color: #dd4d50;
width: 20px; width: 20px;
height: 20px; height: 20px;
text-align: center; text-align: center;
left: 24px; left: 24px;
top: calc(50% - 15px); top: calc(50% - 15px);
} }
.warning h3, .warning h3,
.note h3, .note h3,
.important h3, .important h3,
.caution h3, .caution h3,
.tip h3 { .tip h3 {
padding: 0; padding: 0;
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
line-height: 24px; line-height: 24px;
margin-bottom: 8px; margin-bottom: 8px;
} }
.warning p, .warning p,
.note p, .note p,
.important p, .important p,
.caution p, .caution p,
.tip p { .tip p {
line-height: 20px; line-height: 20px;
} }
.topic-content > section > p { .topic-content > section > p {
margin: 24px 0; margin: 24px 0;
} }
.mediaobject { .mediaobject {
margin-top: 20px; margin-top: 20px;
} }
.mediaobject img { .mediaobject img {
border-radius: 4px; border-radius: 4px;
margin: 0; margin: 0;
} }
.mediaobject .caption > p { .mediaobject .caption > p {
font-size: 14px; font-size: 14px;
text-align: center; text-align: center;
font-style: italic; font-style: italic;
margin: 0; margin: 0;
} }
.inlinemediaobject { .inlinemediaobject {
vertical-align: unset; vertical-align: unset;
} }
main ol, main ol,
main ul { main ul {
margin: 0 0 24px; margin: 0 0 24px;
} }
.section-toc { .section-toc {
padding: 24px 40px; padding: 24px 40px;
border: 1px solid #e2e4e9; border: 1px solid #e2e4e9;
width: 100%; width: 100%;
margin: 0; margin: 0;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
} }
.section-toc:first-child { .section-toc:first-child {
grid-column: 1/span 2; grid-column: 1 / span 2;
} }
.section-toc h4.featured-title, .section-toc h4.featured-title,
.section-toc .section-toc-title { .section-toc .section-toc-title {
font-size: 26px; font-size: 26px;
font-weight: 300; font-weight: 300;
line-height: 36px; line-height: 36px;
margin: 0; margin: 0;
} }
.section-toc h4.featured-title a, .section-toc h4.featured-title a,
.section-toc .section-toc-title a { .section-toc .section-toc-title a {
color: #283241; color: #283241;
} }
.section-toc h4.featured-title a:hover, .section-toc h4.featured-title a:hover,
.section-toc .section-toc-title a:hover { .section-toc .section-toc-title a:hover {
color: #283241; color: #283241;
text-decoration: underline; text-decoration: underline;
} }
.section-toc ul { .section-toc ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.section-toc li { .section-toc li {
margin: 4px 0; margin: 4px 0;
} }
.section-toc li:first-child { .section-toc li:first-child {
margin-top: 20px; margin-top: 20px;
} }
.section-toc li:last-child { .section-toc li:last-child {
margin-bottom: 40px; margin-bottom: 40px;
} }
.section-toc li a { .section-toc li a {
color: #dd4d50; color: #dd4d50;
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
.section-toc li a:hover { .section-toc li a:hover {
color: #dd4d50; color: #dd4d50;
text-decoration: underline; text-decoration: underline;
} }
.section-toc h4 span, .section-toc h4 span,
.section-toc li::before { .section-toc li::before {
display: none; display: none;
} }
.section-toc li:first-child { .section-toc li:first-child {
margin-top: 16px; margin-top: 16px;
} }
.section-toc li:last-child { .section-toc li:last-child {
margin-bottom: 8px; margin-bottom: 8px;
} }
.question { .question {
font-weight: 600; font-weight: 600;
} }
.question > td > p { .question > td > p {
margin: 32px 0 18px 0; margin: 32px 0 18px 0;
} }
.question > td:first-child { .question > td:first-child {
padding-right: 4px; padding-right: 4px;
} }
.fixed-toolbar article.topic :target.question:before { .fixed-toolbar article.topic :target.question:before {
content: none; content: none;
} }
body { body {
color: #283241; color: #283241;
background-color: #f5f5f7; background-color: #f5f5f7;
font-family: "Inter", sans-serif; font-family: 'Inter', sans-serif;
scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-filter-bg); scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2);
scrollbar-width: thin; scrollbar-width: thin;
/* Track */ /* Track */
/* Handle */ /* Handle */
} }
body h1, body h1,
body h2, body h2,
@ -545,31 +545,31 @@ body h5,
body h6, body h6,
body p, body p,
body pre { body pre {
margin: 0; margin: 0;
font-family: "Inter", sans-serif; font-family: 'Inter', sans-serif;
} }
body::-webkit-scrollbar { body::-webkit-scrollbar {
width: 11px; width: 11px;
} }
body::-webkit-scrollbar-track { body::-webkit-scrollbar-track {
background: var(--iqser-filter-bg); background: var(--iqser-grey-2);
} }
body::-webkit-scrollbar-thumb { body::-webkit-scrollbar-thumb {
background: var(--iqser-quick-filter-border); background: var(--iqser-quick-filter-border);
} }
body h3 { body h3 {
font-size: 32px; font-size: 32px;
font-weight: 300; font-weight: 300;
line-height: 43px; line-height: 43px;
} }
body p { body p {
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
body a { body a {
color: #dd4d50; color: #dd4d50;
} }
body a:hover { body a:hover {
text-decoration: underline; text-decoration: underline;
color: #dd4d50; color: #dd4d50;
} }