Merge branch 'master' into table-component

This commit is contained in:
Adina Țeudan 2021-09-15 23:59:54 +03:00
commit 7c8cf1f150
27 changed files with 283 additions and 178 deletions

View File

@ -3,6 +3,7 @@
<div class="red-top-bar">
<div class="top-bar-row">
<div *ngIf="!currentUser.isUser" class="menu-placeholder"></div>
<div *ngIf="currentUser.isUser" class="menu visible-lt-lg">
<button [matMenuTriggerFor]="menuNav" mat-flat-button>
<mat-icon svgIcon="red:menu"></mat-icon>
@ -25,11 +26,13 @@
</button>
</mat-menu>
</div>
<div *ngIf="currentUser.isUser" class="menu flex-2 visible-lg breadcrumbs-container">
<a *ngIf="(isDossiersView$ | async) === false" class="breadcrumb back" redactionNavigateLastDossiersScreen>
<mat-icon svgIcon="red:expand"></mat-icon>
{{ 'top-bar.navigation-items.back' | translate }}
</a>
<ng-container *ngIf="isDossiersView$ | async">
<a
[routerLinkActiveOptions]="{ exact: true }"
@ -38,7 +41,9 @@
routerLinkActive="active"
translate="top-bar.navigation-items.dossiers"
></a>
<mat-icon *ngIf="appStateService.activeDossier" svgIcon="red:arrow-right"></mat-icon>
<a
*ngIf="appStateService.activeDossier"
[routerLinkActiveOptions]="{ exact: true }"
@ -48,7 +53,9 @@
>
{{ appStateService.activeDossier.dossierName }}
</a>
<mat-icon *ngIf="appStateService.activeFile" svgIcon="red:arrow-right"></mat-icon>
<a
*ngIf="appStateService.activeFile"
[routerLink]="'/main/dossiers/' + appStateService.activeDossierId + '/file/' + appStateService.activeFile.fileId"
@ -59,22 +66,22 @@
</a>
</ng-container>
</div>
<div class="center logo-wrapper">
<redaction-hidden-action (action)="userPreferenceService.toggleDevFeatures()">
<redaction-logo></redaction-logo>
</redaction-hidden-action>
<div class="app-name">{{ titleService.getTitle() }}</div>
</div>
<div class="menu right flex-2">
<div class="buttons">
<iqser-circle-button
(action)="openSpotlightSearch()"
<redaction-spotlight-search
*ngIf="(isSearchScreen$ | async) === false"
[icon]="'iqser:search'"
[tooltip]="'search.header-label' | translate"
tooltipPosition="below"
[placeholder]="'search.placeholder' | translate"
[actions]="searchActions"
iqserHelpMode="search"
></iqser-circle-button>
></redaction-spotlight-search>
<redaction-notifications iqserHelpMode="notifications"></redaction-notifications>
</div>

View File

@ -33,3 +33,7 @@
margin-right: 2px;
}
}
redaction-spotlight-search {
margin-right: 16px !important;
}

View File

@ -6,10 +6,7 @@ import { NavigationStart, Router } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { SpotlightSearchComponent } from '@components/spotlight-search/spotlight-search.component';
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
import { SpotlightSearchDialogData } from '@components/spotlight-search/spotlight-search-dialog-data';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { distinctUntilChanged, filter, map, startWith } from 'rxjs/operators';
@ -38,7 +35,7 @@ export class BaseScreenComponent {
readonly currentUser = this.userService.currentUser;
readonly isDossiersView$ = this._navigationStart$.pipe(map(isDossiersView));
readonly isSearchScreen$ = this._navigationStart$.pipe(map(isSearchScreen));
readonly userMenuItems: MenuItem[] = [
readonly userMenuItems: readonly MenuItem[] = [
{
name: _('top-bar.navigation-items.my-account.children.my-profile'),
routerLink: '/main/my-profile',
@ -61,6 +58,19 @@ export class BaseScreenComponent {
show: this.currentUser.isManager
}
];
readonly searchActions: readonly SpotlightSearchAction[] = [
{
text: this._translateService.instant('search.this-dossier'),
icon: 'red:enter',
hide: (): boolean => !this.appStateService.activeDossier,
action: (query): void => this._search(query, this.appStateService.activeDossier.dossierId)
},
{
text: this._translateService.instant('search.entire-platform'),
icon: 'red:enter',
action: (query): void => this._search(query)
}
];
constructor(
readonly appStateService: AppStateService,
@ -69,33 +79,9 @@ export class BaseScreenComponent {
readonly titleService: Title,
readonly fileDownloadService: FileDownloadService,
private readonly _router: Router,
private readonly _translateService: TranslateService,
private readonly _dialog: MatDialog
private readonly _translateService: TranslateService
) {}
openSpotlightSearch() {
const spotlightSearchActions: SpotlightSearchAction[] = [
{
text: this._translateService.instant('search.this-dossier'),
icon: 'red:enter',
hide: !this.appStateService.activeDossier,
action: query => this._search(query, this.appStateService.activeDossier.dossierId)
},
{
text: this._translateService.instant('search.entire-platform'),
icon: 'red:enter',
action: query => this._search(query)
}
];
this._dialog.open(SpotlightSearchComponent, {
data: {
actionsConfig: spotlightSearchActions,
placeholder: this._translateService.instant('search.placeholder')
} as SpotlightSearchDialogData
});
}
private _search(query: string, dossierId?: string) {
const queryParams = { query, dossierId };
this._router.navigate(['main/dossiers/search'], { queryParams }).then();

View File

@ -2,5 +2,5 @@ export interface SpotlightSearchAction {
readonly text: string;
readonly action: (query: string) => void;
readonly icon?: string;
readonly hide?: boolean;
readonly hide?: () => boolean;
}

View File

@ -1,6 +0,0 @@
import { SpotlightSearchAction } from './spotlight-search-action';
export interface SpotlightSearchDialogData {
readonly actionsConfig: SpotlightSearchAction[];
readonly placeholder: string;
}

View File

@ -1,28 +1,28 @@
<form [formGroup]="formGroup" class="spotlight-wrapper">
<div class="search d-flex">
<input [placeholder]="data.placeholder" autocomplete="off" class="spotlight-row" formControlName="query" id="query" type="text" />
<iqser-input-with-action
(click)="openMenuIfValue()"
(valueChange)="valueChanges$.next($event)"
[placeholder]="placeholder"
></iqser-input-with-action>
<mat-icon *ngIf="(showActions$ | async) === false" [svgIcon]="'iqser:search'" class="mr-34"></mat-icon>
<mat-menu #menu="matMenu" xPosition="after">
<ng-template matMenuContent>
<div class="wrapper">
<button
(click)="item.action(valueChanges$.getValue())"
*ngFor="let item of shownActions; let index = index"
[class.highlight]="(currentActionIdx$ | async) === index"
class="spotlight-row pointer"
>
<mat-icon [svgIcon]="item.icon"></mat-icon>
<span>{{ item.text }}</span>
</button>
</div>
</ng-template>
</mat-menu>
<iqser-circle-button
(action)="dialogRef.close()"
*ngIf="showActions$ | async"
class="mr-24"
icon="iqser:close"
></iqser-circle-button>
</div>
<!-- https://material.angular.io/components/menu/overview#toggling-the-menu-programmatically -->
<!-- To toggle menu programmatically a matMenuTriggerFor directive is needed -->
<div [matMenuTriggerFor]="menu"></div>
<div class="divider"></div>
<ng-container *ngIf="showActions$ | async">
<button
(click)="item.action(formGroup.get('query').value); dialogRef.close()"
*ngFor="let item of shownActions; let index = index"
[class.highlight]="(currentActionIdx$ | async) === index"
class="spotlight-row pointer"
>
<mat-icon [svgIcon]="item.icon" class="mr-16"></mat-icon>
<span>{{ item.text }}</span>
</button>
</ng-container>
</form>
<!-- A hack to avoid subscribing in component -->
<ng-container *ngIf="showActions$ | async"></ng-container>

View File

@ -1,50 +1,38 @@
@import '../../../assets/styles/variables';
.spotlight-wrapper {
overflow: hidden;
position: absolute;
top: 15%;
left: 0;
right: 0;
border-radius: 10px;
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.3);
}
.spotlight-row {
width: 300px;
display: flex;
align-items: center;
height: 60px;
height: 40px;
text-align: left;
font-size: 16px;
font-weight: 500;
font-size: 13px;
border: none;
outline: none;
color: $accent;
padding: 0 24px;
background-color: $white;
}
.spotlight-row,
.spotlight-wrapper {
width: 750px;
margin: auto;
}
.highlight {
border-radius: 4px;
background-color: $grey-2;
}
.search {
background-color: $white;
align-items: center;
.wrapper {
width: 300px;
}
.divider {
height: 1px;
background-color: rgba(226, 228, 233, 0.9);
::ng-deep .mat-menu-content {
padding: 8px !important;
}
button {
padding: 0;
}
mat-icon {
width: 14px;
margin-left: 13px;
margin-right: 8px;
height: 14px;
width: 14px;
}

View File

@ -1,59 +1,63 @@
import { ChangeDetectionStrategy, Component, HostListener, Inject } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { SpotlightSearchDialogData } from '@components/spotlight-search/spotlight-search-dialog-data';
import { ChangeDetectionStrategy, Component, HostListener, Input, ViewChild } from '@angular/core';
import { debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs';
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
import { MatMenuTrigger } from '@angular/material/menu';
@Component({
selector: 'redaction-spotlight-search',
selector: 'redaction-spotlight-search [actions]',
templateUrl: './spotlight-search.component.html',
styleUrls: ['./spotlight-search.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpotlightSearchComponent {
private readonly _currentActionIdx$ = new BehaviorSubject(0);
@Input() actions: readonly SpotlightSearchAction[];
@Input() placeholder?: string;
@ViewChild(MatMenuTrigger) private readonly _menuTrigger!: MatMenuTrigger;
private readonly _currentActionIdx$ = new BehaviorSubject(0);
readonly currentActionIdx$ = this._currentActionIdx$.asObservable().pipe(distinctUntilChanged());
readonly shownActions = this.data.actionsConfig.filter(a => !a.hide);
readonly formGroup = this._formBuilder.group({ query: [''] });
readonly showActions$ = this.formGroup.get('query').valueChanges.pipe(
startWith(''),
readonly valueChanges$ = new BehaviorSubject<string>('');
readonly showActions$ = this.valueChanges$.pipe(
debounceTime(300),
map(value => value !== '')
map(value => value !== ''),
tap(show => (show ? this._menuTrigger.openMenu() : this._menuTrigger.closeMenu()))
);
constructor(
private readonly _formBuilder: FormBuilder,
readonly dialogRef: MatDialogRef<SpotlightSearchComponent>,
@Inject(MAT_DIALOG_DATA)
readonly data: SpotlightSearchDialogData
) {}
get shownActions(): readonly SpotlightSearchAction[] {
return this.actions.filter(a => !a.hide?.());
}
@HostListener('document:keydown', ['$event'])
handleKeyDown(event: KeyboardEvent): void {
if (['ArrowDown', 'ArrowUp'].includes(event.code)) {
event.preventDefault();
return event.stopPropagation();
openMenuIfValue(): void {
const value = this.valueChanges$.getValue();
if (value && value !== '') {
this._menuTrigger.openMenu();
}
}
@HostListener('document:keyup', ['$event'])
handleKeyUp(event: KeyboardEvent): void {
if (['ArrowDown', 'ArrowUp'].includes(event.code)) {
event.preventDefault();
event.stopPropagation();
const index = this._currentActionIdx + event.code === 'ArrowDown' ? 1 : -1;
return this._currentActionIdx$.next(this._fixIndexOutOfBound(index));
}
@HostListener('document:keydown.arrowDown', ['$event'])
@HostListener('document:keydown.arrowUp', ['$event'])
handleKeyDown(event: KeyboardEvent): void {
event.preventDefault();
event.stopPropagation();
}
@HostListener('document:keyup.arrowDown', ['$event'])
handleKeyUpArrowDown(event: KeyboardEvent): void {
this.handleKeyDown(event);
const index = this._currentActionIdx + 1;
this._currentActionIdx$.next(index >= this.actions.length ? 0 : index);
}
@HostListener('document:keyup.arrowUp', ['$event'])
handleKeyUpArrowUp(event: KeyboardEvent): void {
this.handleKeyDown(event);
const index = this._currentActionIdx - 1;
this._currentActionIdx$.next(index < 0 ? this.actions.length - 1 : index);
}
private get _currentActionIdx(): number {
return this._currentActionIdx$.getValue();
}
private _fixIndexOutOfBound(index: number): number {
const indexOutOfBound = index < 0 || index >= this.shownActions.length;
return indexOutOfBound ? Math.abs(this.shownActions.length - Math.abs(index)) : index;
}
}

View File

@ -3,8 +3,9 @@ import { AppStateService } from '@state/app-state.service';
import { Router } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { CircleButtonTypes, LoadingService } from '@iqser/common-ui';
import { CircleButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { UserService } from '@services/user.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'redaction-dossier-template-actions',
@ -20,6 +21,7 @@ export class DossierTemplateActionsComponent implements OnInit {
constructor(
private readonly _router: Router,
private readonly _toaster: Toaster,
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
private readonly _appStateService: AppStateService,
@ -44,6 +46,18 @@ export class DossierTemplateActionsComponent implements OnInit {
openDeleteDossierTemplateDialog($event?: MouseEvent) {
this._dialogService.openDialog('confirm', $event, null, async () => {
this._loadingService.start();
await this._dossierTemplateControllerService
.deleteDossierTemplates([this.dossierTemplateId])
.toPromise()
.catch(error => {
if (error.status === 409) {
this._toaster.error(_('dossier-templates-listing.error.conflict'));
} else {
this._toaster.error(_('dossier-templates-listing.error.generic'));
}
this._loadingService.stop();
});
await this._dossierTemplateControllerService.deleteDossierTemplates([this.dossierTemplateId]).toPromise();
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();

View File

@ -2,9 +2,10 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UserControllerService } from '@redaction/red-ui-http';
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { rolesTranslations } from '../../../../../translations/roles-translations';
import { UserWrapper } from '@services/user.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Component({
selector: 'redaction-user-details',
@ -24,6 +25,7 @@ export class UserDetailsComponent implements OnInit {
constructor(
private readonly _formBuilder: FormBuilder,
private readonly _toaster: Toaster,
private readonly _dialogService: AdminDialogService,
private readonly _loadingService: LoadingService,
private readonly _userControllerService: UserControllerService
@ -99,12 +101,24 @@ export class UserDetailsComponent implements OnInit {
const userData = { ...this.userForm.getRawValue(), roles: this.activeRoles };
if (!this.user) {
await this._userControllerService.createUser(userData).toPromise();
await this._userControllerService
.createUser(userData)
.toPromise()
.then(() => {
this.closeDialog.emit(true);
})
.catch(error => {
if (error.status === 409) {
this._toaster.error(_('add-edit-user.error.email-already-used'));
} else {
this._toaster.error(_('add-edit-user.error.generic'));
}
this._loadingService.stop();
});
} else {
await this._userControllerService.updateProfile(userData, this.user.id).toPromise();
this.closeDialog.emit(true);
}
this.closeDialog.emit(true);
}
async delete() {

View File

@ -9,7 +9,8 @@ import {
IconButtonTypes,
ListingComponent,
LoadingService,
TableColumnConfig
TableColumnConfig,
Toaster
} from '@iqser/common-ui';
import { DossierTemplateControllerService } from '@redaction/red-ui-http';
import { UserService } from '@services/user.service';
@ -39,6 +40,7 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
constructor(
protected readonly _injector: Injector,
private readonly _toaster: Toaster,
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
private readonly _appStateService: AppStateService,
@ -101,7 +103,16 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
}
private async _deleteTemplates(templateIds: string[] = this.entitiesService.selected.map(d => d.dossierTemplateId)) {
await this._dossierTemplateControllerService.deleteDossierTemplates(templateIds).toPromise();
await this._dossierTemplateControllerService
.deleteDossierTemplates(templateIds)
.toPromise()
.catch(error => {
if (error.status === 409) {
this._toaster.error(_('dossier-templates-listing.error.conflict'));
} else {
this._toaster.error(_('dossier-templates-listing.error.generic'));
}
});
this.entitiesService.setSelected([]);
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();

View File

@ -8,3 +8,10 @@
margin-right: 2px;
}
}
.disabled {
> div {
background-color: $grey-2;
color: $grey-7;
}
}

View File

@ -1,6 +1,14 @@
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Dossier } from '@redaction/red-ui-http';
import { CircleButtonTypes, DefaultListingServices, Listable, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
import {
CircleButtonTypes,
DefaultListingServices,
ListingComponent,
LoadingService,
Listable,
TableColumnConfig,
SortingOrders
} from '@iqser/common-ui';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import * as moment from 'moment';
import { DossiersService } from '../../../dossier/services/dossiers.service';
@ -61,6 +69,10 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
this._configureTableColumns();
this._loadingService.start();
await this._loadDossiersData();
this.sortingService.setSortingOption({
column: 'softDeletedTime',
order: SortingOrders.desc
});
this._loadingService.stop();
}
@ -117,7 +129,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
}
private async _loadDossiersData(): Promise<void> {
this.entitiesService.setEntities(this._toListItems(await this._dossiersService.getDeleted()));
this.entitiesService.setEntities(this._toListItems(await this._dossiersService.getDeleted()).filter(d => d.canRestore));
}
private _canRestoreDossier(restoreDate: string): boolean {
@ -136,7 +148,9 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
id: dossier.dossierId,
...dossier,
restoreDate,
canRestore: this._canRestoreDossier(restoreDate)
canRestore: this._canRestoreDossier(restoreDate),
// Because of migrations, for some this is not set
softDeletedTime: dossier.softDeletedTime || '-'
};
}

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { AppStateService } from '@state/app-state.service';
@ -16,7 +16,7 @@ import { filter } from 'rxjs/operators';
templateUrl: './file-actions.component.html',
styleUrls: ['./file-actions.component.scss']
})
export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnChanges {
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
@ -186,6 +186,12 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
this.actionPerformed.emit(this.fileStatus?.excluded ? 'enable-analysis' : 'disable-analysis');
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.fileStatus) {
this._setup();
}
}
private _setup() {
this.statusBarConfig = [{ color: this.fileStatus.status, length: 1 }];
this.tooltipPosition = this.screen === 'file-preview' ? 'below' : 'above';

View File

@ -105,7 +105,7 @@ export class EditDossierDialogComponent {
}
updatedDossier() {
this._toaster.success(_('edit-dossier-dialog.change-successful'));
this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this.dossierWrapper.dossierName } });
this.dossierWrapper = this._appStateService.getDossierById(this.dossierWrapper.dossierId);
this._changeRef.detectChanges();
this.afterSave();

View File

@ -13,6 +13,7 @@ import { EditDossierDialogComponent } from '../edit-dossier-dialog.component';
import { Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IconButtonTypes } from '@iqser/common-ui';
import { ConfirmationDialogInput, TitleColors } from '../../../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
@Component({
selector: 'redaction-edit-dossier-general-info',
@ -106,14 +107,29 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
}
async deleteDossier() {
await this._appStateService.deleteDossier(this.dossierWrapper);
this._editDossierDialogRef.componentInstance.afterSave();
this._editDossierDialogRef.close();
this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted());
const data = new ConfirmationDialogInput({
title: _('confirmation-dialog.delete-dossier.title'),
titleColor: TitleColors.PRIMARY,
question: _('confirmation-dialog.delete-dossier.question'),
// details: _('confirmation-dialog.delete-dossier.details'),
confirmationText: _('confirmation-dialog.delete-dossier.confirmation-text'),
requireInput: true,
denyText: _('confirmation-dialog.delete-dossier.deny-text'),
translateParams: {
dossierName: this.dossierWrapper.dossierName,
dossiersCount: 1
}
});
this._dialogService.openDialog('confirm', null, data, async () => {
await this._appStateService.deleteDossier(this.dossierWrapper);
this._editDossierDialogRef.componentInstance.afterSave();
this._editDossierDialogRef.close();
this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted());
});
}
private _notifyDossierDeleted() {
this._toaster.success(_('edit-dossier-dialog.delete-successful'));
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: { dossierName: this.dossierWrapper.dossierName } });
}
private _filterInvalidDossierTemplates() {

View File

@ -1,8 +1,5 @@
<section>
<redaction-page-header
[buttonConfigs]="buttonConfigs"
[searchPlaceholder]="'dossier-listing.search' | translate"
></redaction-page-header>
<redaction-page-header [buttonConfigs]="buttonConfigs"></redaction-page-header>
<div class="overlay-shadow"></div>

View File

@ -16,7 +16,7 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { UserPreferenceService } from '@services/user-preference.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { DefaultListingServices, ListingComponent, NestedFilter, TableColumnConfig, TableComponent } from '@iqser/common-ui';
import { DefaultListingServices, keyChecker, ListingComponent, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { workloadTranslations } from '../../translations/workload-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations';
@ -264,7 +264,7 @@ export class DossierListingScreenComponent
slug: 'dossierTemplateFilters',
label: this._translateService.instant('filters.dossier-templates'),
icon: 'red:template',
hide: this.filterService.getGroup('dossierTemplateFilters')?.filters?.length <= 1,
hide: dossierTemplateFilters.length <= 1,
filters: dossierTemplateFilters,
checker: dossierTemplateChecker
});
@ -275,6 +275,19 @@ export class DossierListingScreenComponent
filters: quickFilters,
checker: (dw: DossierWrapper) => quickFilters.reduce((acc, f) => acc || (f.checked && f.checker(dw)), false)
});
const dossierFilters = this.entitiesService.all.map<NestedFilter>(dossier => ({
key: dossier.dossierName,
label: dossier.dossierName
}));
this.filterService.addFilterGroup({
slug: 'dossierNameFilter',
label: this._translateService.instant('dossier-listing.filters.label'),
icon: 'red:folder',
filters: dossierFilters,
filterceptionPlaceholder: this._translateService.instant('dossier-listing.filters.search'),
checker: keyChecker('dossierName')
});
}
private _createQuickFilters() {

View File

@ -3,7 +3,6 @@
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
[actionConfigs]="actionConfigs"
[fileAttributeConfigs]="fileAttributeConfigs"
[searchPlaceholder]="'dossier-overview.search' | translate"
[showCloseButton]="true"
>
<redaction-file-download-btn

View File

@ -107,9 +107,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
private readonly _statusOverlayService: StatusOverlayService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _dossierAttributesService: DossierAttributesService,
private readonly _renderer: Renderer2,
private readonly _elementRef: ElementRef
private readonly _dossierAttributesService: DossierAttributesService
) {
super(_injector);
this._loadEntitiesFromState();
@ -456,6 +454,20 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
(this.checkedNotRequiredFilters.length === 0 ||
this.checkedNotRequiredFilters.reduce((acc, f) => acc || f.checker(file), false))
});
const filesNamesFilters = this.entitiesService.all.map<NestedFilter>(file => ({
key: file.filename,
label: file.filename
}));
this.filterService.addFilterGroup({
slug: 'filesNamesFilter',
label: this._translateService.instant('dossier-overview.filters.label'),
icon: 'red:document',
filters: filesNamesFilters,
checker: keyChecker('filename'),
filterceptionPlaceholder: this._translateService.instant('dossier-overview.filters.search')
});
}
private _createQuickFilters() {

View File

@ -10,7 +10,8 @@ import {
FilterService,
LoadingService,
NestedFilter,
processFilters
processFilters,
Toaster
} from '@iqser/common-ui';
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
@ -20,7 +21,6 @@ import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
import { FileActionService } from '../../services/file-action.service';
import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { Toaster } from '@iqser/common-ui';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { PermissionsService } from '@services/permissions.service';
import { timer } from 'rxjs';
@ -295,6 +295,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const secondaryFilters = this._filterService.getGroup('secondaryFilters')?.filters;
this._filterService.addFilterGroup({
slug: 'secondaryFilters',
filterTemplate: this._filterTemplate,
filters: processFilters(secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters)
});
console.log('[REDACTION] Process time: ' + (new Date().getTime() - processStartTime) + 'ms');

View File

@ -31,7 +31,6 @@ export class IconsModule {
'download',
'entries',
'enter',
'error',
'exclude-pages',
'exit-fullscreen',
'expand',

View File

@ -12,7 +12,8 @@ export class ServerErrorInterceptor implements HttpInterceptor {
return next.handle(req).pipe(
map((event: HttpEvent<any>) => event),
catchError((error: HttpErrorResponse) => {
if (error.status >= 500 || error.status === 0) {
if (error.status >= 500) {
// || error.status === 0
this._errorService.set(error);
}
return throwError(error);

View File

@ -565,7 +565,10 @@
"reanalyse": {
"action": "Analysieren Sie das gesamte Dossier"
},
"search": "Dossiername ...",
"filters": {
"label": "Dossiername",
"search": "Dossiername..."
},
"stats": {
"analyzed-pages": "Seiten",
"charts": {
@ -658,7 +661,10 @@
"reanalyse": {
"action": "Datei analysieren"
},
"search": "Dokumentname...",
"filters": {
"label": "Dokumentname",
"search": "Dokumentname..."
},
"table-col-names": {
"added-on": "Hinzugefügt",
"assigned-to": "Zugewiesen an",
@ -1212,7 +1218,6 @@
},
"search": {
"entire-platform": "",
"header-label": "",
"placeholder": "",
"this-dossier": ""
},

View File

@ -100,6 +100,10 @@
"save": "Save User",
"save-changes": "Save Changes"
},
"error": {
"email-already-used": "This e-mail address is already in use by a different user!",
"generic": "Failed to save user!"
},
"form": {
"email": "Email",
"first-name": "First Name",
@ -573,6 +577,10 @@
"edit": {
"action": "Edit Dossier"
},
"filters": {
"label": "Dossier Name",
"search": "Dossier name..."
},
"no-data": {
"action": "New Dossier",
"title": "You currently have no dossiers."
@ -589,7 +597,6 @@
"reanalyse": {
"action": "Analyze entire dossier"
},
"search": "Dossier name...",
"stats": {
"analyzed-pages": "Pages",
"charts": {
@ -649,6 +656,10 @@
"file-processing": "Processing"
}
},
"filters": {
"label": "Document Name",
"search": "Document name..."
},
"header-actions": {
"edit": "Edit Dossier",
"upload-document": "Upload Document"
@ -682,7 +693,6 @@
"reanalyse": {
"action": "Analyze File"
},
"search": "Document name...",
"table-col-names": {
"added-on": "Added",
"assigned-to": "Assigned to",
@ -709,6 +719,10 @@
"delete": "Delete Selected Dossier Templates"
},
"dictionaries": "{length} {length, plural, one{dictionary} other{dictionaries}}",
"error": {
"conflict": "Cannot delete this DossierTemplate! At least on Dossier uses this template!",
"generic": "Cannot delete this DossierTemplate!"
},
"no-data": {
"title": "There are no dossier templates yet."
},
@ -781,8 +795,8 @@
"no-image-attributes": "There are no image attributes",
"upload-image": "Upload Image"
},
"change-successful": "Dossier was updated.",
"delete-successful": "Dossier was deleted.",
"change-successful": "Dossier {dossierName} was updated.",
"delete-successful": "Dossier {dossierName} was deleted.",
"deleted-documents": {
"action": {
"delete": "Delete Forever",
@ -847,11 +861,11 @@
"unsaved-changes": "You have unsaved changes. Save or revert before changing the tab."
},
"error": {
"generic": "Oops! Something went wrong...",
"reload": "Reload",
"http": {
"generic": "Action failed with code {status}"
}
},
"reload": "Reload",
"title": "Oops! Something went wrong..."
},
"exact-date": "{day} {month} {year} at {hour}:{minute}",
"file": "File",
@ -1029,7 +1043,7 @@
},
"filter-menu": {
"filter-options": "Filter options",
"filter-types": "Filter types",
"filter-types": "Filter",
"label": "Filter",
"with-comments": "Only annotations with comments",
"with-reason-changes": "Only redactions with reason changes"
@ -1301,7 +1315,7 @@
"title": "Reports",
"upload-document": "Upload a Document"
},
"reset-filters": "Reset Filters",
"reset-filters": "Reset",
"reset-password-dialog": {
"actions": {
"cancel": "Cancel",
@ -1349,9 +1363,8 @@
"table-header": "{length} search {length, plural, one{result} other{results}}"
},
"search": {
"entire-platform": "entire platform",
"header-label": "Search entire platform",
"placeholder": "Search for documents or document content",
"entire-platform": "accross all dossiers",
"placeholder": "Search documents...",
"this-dossier": "in this dossier"
},
"smtp-auth-config": {

View File

@ -1,6 +1,6 @@
{
"name": "redaction",
"version": "2.214.0",
"version": "2.218.0",
"private": true,
"license": "MIT",
"scripts": {

Binary file not shown.