Merge branch 'master' into table-component
This commit is contained in:
commit
7c8cf1f150
@ -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>
|
||||
|
||||
@ -33,3 +33,7 @@
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
redaction-spotlight-search {
|
||||
margin-right: 16px !important;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -2,5 +2,5 @@ export interface SpotlightSearchAction {
|
||||
readonly text: string;
|
||||
readonly action: (query: string) => void;
|
||||
readonly icon?: string;
|
||||
readonly hide?: boolean;
|
||||
readonly hide?: () => boolean;
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import { SpotlightSearchAction } from './spotlight-search-action';
|
||||
|
||||
export interface SpotlightSearchDialogData {
|
||||
readonly actionsConfig: SpotlightSearchAction[];
|
||||
readonly placeholder: string;
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -8,3 +8,10 @@
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
> div {
|
||||
background-color: $grey-2;
|
||||
color: $grey-7;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 || '-'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
|
||||
[actionConfigs]="actionConfigs"
|
||||
[fileAttributeConfigs]="fileAttributeConfigs"
|
||||
[searchPlaceholder]="'dossier-overview.search' | translate"
|
||||
[showCloseButton]="true"
|
||||
>
|
||||
<redaction-file-download-btn
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -31,7 +31,6 @@ export class IconsModule {
|
||||
'download',
|
||||
'entries',
|
||||
'enter',
|
||||
'error',
|
||||
'exclude-pages',
|
||||
'exit-fullscreen',
|
||||
'expand',
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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": ""
|
||||
},
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "redaction",
|
||||
"version": "2.214.0",
|
||||
"version": "2.218.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user