View archived dossiers list, breadcrumbs WIP

This commit is contained in:
Adina Țeudan 2022-02-19 02:40:33 +02:00
parent 84c4a128ef
commit 3d69d42150
36 changed files with 352 additions and 77 deletions

View File

@ -8,6 +8,7 @@ import { NgModule } from '@angular/core';
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
import { DossiersGuard } from '@guards/dossiers.guard';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { ArchivedDossiersGuard } from '@guards/archived-dossiers.guard';
const routes: Routes = [
{
@ -40,6 +41,16 @@ const routes: Routes = [
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
{
path: 'main/archive',
component: BaseScreenComponent,
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard, ArchivedDossiersGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
{
path: 'main/downloads',
component: BaseScreenComponent,

View File

@ -19,7 +19,7 @@ interface MenuItem {
}
const isNavigationStart = event => event instanceof NavigationStart;
const isSearchScreen = url => url.includes('/main/dossiers') && url.includes('/search');
const isSearchScreen: (url: string) => boolean = url => url.includes('/main/dossiers') && url.includes('/search');
@Component({
templateUrl: './base-screen.component.html',
@ -80,7 +80,7 @@ export class BaseScreenComponent {
) {}
private get _hideSearchThisDossier() {
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.routerLink;
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.options?.routerLink;
if (!routerLink) {
return true;
}
@ -99,7 +99,7 @@ export class BaseScreenComponent {
}
private _searchThisDossier(query: string) {
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.routerLink;
const routerLink = this.breadcrumbsService.breadcrumbs[1]?.options?.routerLink;
if (!routerLink) {
return this._search(query, []);
}

View File

@ -9,16 +9,34 @@
<mat-icon *ngIf="!first" svgIcon="iqser:arrow-right"></mat-icon>
<a
[class.clamp]="breadcrumb.clamp"
*ngIf="is(breadcrumb, 'text')"
[class.clamp]="breadcrumb.options.clamp"
[id]="first ? 'navigateToActiveDossiers' : ''"
[matTooltip]="breadcrumb.clamp && (breadcrumb.name$ | async)"
[routerLinkActiveOptions]="breadcrumb.routerLinkActiveOptions || { exact: false }"
[routerLink]="breadcrumb.routerLink"
[matTooltip]="breadcrumb.options.clamp && (breadcrumb.name$ | async)"
[routerLinkActiveOptions]="breadcrumb.options.routerLinkActiveOptions || { exact: false }"
[routerLink]="breadcrumb.options.routerLink"
class="breadcrumb"
routerLinkActive="active"
>
{{ breadcrumb.name$ | async }}
</a>
<ng-container *ngIf="is(breadcrumb, 'dropdown')">
<a [class.active]="breadcrumbs.length === 1" [matMenuTriggerFor]="dropdownMenu" class="breadcrumb">
{{ breadcrumb.name$ | async }}
</a>
<mat-menu #dropdownMenu="matMenu" class="padding-bottom-8">
<a
*ngFor="let option of breadcrumb.options.options"
[routerLink]="option.options.routerLink"
mat-menu-item
routerLinkActive="active"
>
{{ option.name$ | async }}
</a>
</mat-menu>
</ng-container>
</ng-container>
</ng-template>
</div>

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { BreadcrumbsService } from '@services/breadcrumbs.service';
import { Breadcrumb, BreadcrumbDisplayType, BreadcrumbsService } from '@services/breadcrumbs.service';
@Component({
selector: 'redaction-breadcrumbs',
@ -9,4 +9,8 @@ import { BreadcrumbsService } from '@services/breadcrumbs.service';
})
export class BreadcrumbsComponent {
constructor(readonly breadcrumbsService: BreadcrumbsService) {}
is(breadcrumb: Breadcrumb, type: BreadcrumbDisplayType): boolean {
return breadcrumb.type === type;
}
}

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { ArchivedDossiersService } from '@services/entity-services/archived-dossiers.service';
@Injectable({ providedIn: 'root' })
export class ArchivedDossiersGuard implements CanActivate {
constructor(private readonly _archivedDossiersService: ArchivedDossiersService) {}
async canActivate(): Promise<boolean> {
await firstValueFrom(this._archivedDossiersService.loadAll());
return true;
}
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DICTIONARY_TYPE, DOSSIER_TEMPLATE_ID } from '@utils/constants';
import { DictionariesMapService } from '../services/entity-services/dictionaries-map.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
@Injectable({ providedIn: 'root' })
export class DictionaryExistsGuard implements CanActivate {

View File

@ -10,15 +10,15 @@ import {
} from '../../../../../../../../libs/common-ui/src';
import { DossierState, IDossierState } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '../../../../services/entity-services/dossiers.service';
import { DossierStateService } from '../../../../services/entity-services/dossier-state.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { firstValueFrom } from 'rxjs';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { UserService } from '../../../../services/user.service';
import { UserService } from '@services/user.service';
import { HttpStatusCode } from '@angular/common/http';
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { ActivatedRoute } from '@angular/router';
import { DossierTemplatesService } from '../../../../services/entity-services/dossier-templates.service';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
@Component({
templateUrl: './dossier-states-listing-screen.component.html',
@ -32,7 +32,6 @@ import { DossierTemplatesService } from '../../../../services/entity-services/do
export class DossierStatesListingScreenComponent extends ListingComponent<DossierState> implements OnInit, OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
readonly #dossierTemplateId: string;
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-states-listing.table-header.title');
readonly tableColumnConfigs: TableColumnConfig<DossierState>[] = [
@ -40,6 +39,7 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
{ label: _('dossier-states-listing.table-col-names.dossiers-count'), sortByKey: 'dossierCount' },
];
chartData: DoughnutChartConfig[];
readonly #dossierTemplateId: string;
constructor(
protected readonly _injector: Injector,

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BreadcrumbTypes } from '@red/domain';
import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: ArchivedDossiersScreenComponent,
data: { breadcrumbs: [BreadcrumbTypes.archive] },
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ArchiveRoutingModule {}

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-screen/archived-dossiers-screen.component';
import { ArchiveRoutingModule } from './archive-routing.module';
import { CommonUiModule } from '@iqser/common-ui';
import { TableItemComponent } from './components/table-item/table-item.component';
import { ConfigService } from './services/config.service';
import { SharedModule } from '../shared/shared.module';
const components = [TableItemComponent];
const screens = [ArchivedDossiersScreenComponent];
@NgModule({
declarations: [...components, ...screens],
imports: [CommonModule, ArchiveRoutingModule, SharedModule, CommonUiModule],
providers: [ConfigService],
})
export class ArchiveModule {}

View File

@ -0,0 +1,13 @@
<div class="cell">
<redaction-dossiers-listing-dossier-name [dossierStats]="stats$ | async" [dossier]="dossier"></redaction-dossiers-listing-dossier-name>
</div>
<div class="cell small-label">{{ dossier.archivedTime | date: 'd MMM. yyyy' }}</div>
<div class="cell user-column">
<redaction-initials-avatar [user]="dossier.ownerId" [withName]="true"></redaction-initials-avatar>
</div>
<div class="cell">
<redaction-dossier-status [dossier]="dossier"></redaction-dossier-status>
</div>

View File

@ -0,0 +1,28 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { BehaviorSubject, Observable } from 'rxjs';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { switchMap } from 'rxjs/operators';
@Component({
selector: 'redaction-table-item [dossier]',
templateUrl: './table-item.component.html',
styleUrls: ['./table-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TableItemComponent implements OnChanges {
@Input() dossier!: Dossier;
readonly stats$: Observable<DossierStats>;
readonly #ngOnChanges$ = new BehaviorSubject<string>(undefined);
constructor(readonly dossierStatsService: DossierStatsService) {
this.stats$ = this.#ngOnChanges$.pipe(switchMap(dossierId => this.dossierStatsService.watch$(dossierId)));
}
ngOnChanges() {
if (this.dossier) {
this.#ngOnChanges$.next(this.dossier.dossierId);
}
}
}

View File

@ -0,0 +1,21 @@
<section>
<iqser-page-header></iqser-page-header>
<div class="overlay-shadow"></div>
<div class="content-inner">
<div class="content-container">
<iqser-table
[itemSize]="80"
[noDataText]="'archived-dossiers-listing.no-data.title' | translate"
[noMatchText]="'archived-dossiers-listing.no-match.title' | translate"
[tableColumnConfigs]="tableColumnConfigs"
noDataIcon="red:folder"
></iqser-table>
</div>
</div>
</section>
<ng-template #tableItemTemplate let-dossier="entity">
<redaction-table-item [dossier]="dossier"></redaction-table-item>
</ng-template>

View File

@ -0,0 +1,26 @@
import { ChangeDetectionStrategy, Component, forwardRef, Injector } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ConfigService } from '../../services/config.service';
import { DefaultListingServicesTmp, EntitiesService, ListingComponent } from '@iqser/common-ui';
import { ArchivedDossiersService } from '@services/entity-services/archived-dossiers.service';
import { Dossier } from '@red/domain';
@Component({
selector: 'redaction-archived-dossiers-screen',
templateUrl: './archived-dossiers-screen.component.html',
styleUrls: ['./archived-dossiers-screen.component.scss'],
providers: [
...DefaultListingServicesTmp,
{ provide: EntitiesService, useExisting: ArchivedDossiersService },
{ provide: ListingComponent, useExisting: forwardRef(() => ArchivedDossiersScreenComponent) },
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArchivedDossiersScreenComponent extends ListingComponent<Dossier> {
readonly tableColumnConfigs = this._configService.tableConfig;
readonly tableHeaderLabel = _('archived-dossiers-listing.table-header.title');
constructor(protected readonly _injector: Injector, private readonly _configService: ConfigService) {
super(_injector);
}
}

View File

@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { TableColumnConfig } from '@iqser/common-ui';
import { Dossier } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Injectable()
export class ConfigService {
get tableConfig(): TableColumnConfig<Dossier>[] {
return [
{ label: _('archived-dossiers-listing.table-col-names.name'), sortByKey: 'searchKey', width: '2fr' },
{ label: _('archived-dossiers-listing.table-col-names.last-modified'), sortByKey: 'archivedTime' },
{ label: _('archived-dossiers-listing.table-col-names.owner'), class: 'user-column' },
{ label: _('archived-dossiers-listing.table-col-names.dossier-status'), class: 'flex-end', width: '2fr' },
];
}
}

View File

@ -5,6 +5,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { firstValueFrom } from 'rxjs';
import { Dossier } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ArchivedDossiersService } from '@services/entity-services/archived-dossiers.service';
@Component({
templateUrl: './confirm-archive-dossier-dialog.component.html',
@ -17,6 +18,7 @@ export class ConfirmArchiveDossierDialogComponent {
constructor(
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
private readonly _toaster: Toaster,
@Inject(MAT_DIALOG_DATA) readonly dossier: Dossier,
readonly dialogRef: MatDialogRef<ConfirmArchiveDossierDialogComponent>,
@ -29,9 +31,10 @@ export class ConfirmArchiveDossierDialogComponent {
async archiveDossier() {
if (this.valid) {
this._loadingService.start();
await firstValueFrom(this._dossiersService.archive([this.dossier]));
await firstValueFrom(this._archivedDossiersService.archive([this.dossier]));
this._toaster.success(_('dossier-listing.archive.archive-succeeded'), { params: this.dossier });
this.dialogRef.close(true);
this._loadingService.stop();
} else {
this.showToast = true;
}

View File

@ -8,7 +8,7 @@ import { workflowFileStatusTranslations } from '../../../../translations/file-st
import { TranslateChartService } from '@services/translate-chart.service';
import { filter, map, switchMap } from 'rxjs/operators';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { TranslateService } from '@ngx-translate/core';
@Component({

View File

@ -1,13 +0,0 @@
<ng-container *ngIf="dossier.dossierStatusId && currentState">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ currentState.name }}</div>
<redaction-small-chip [color]="currentState.color"></redaction-small-chip>
</div>
</ng-container>
<ng-container *ngIf="!dossier.dossierStatusId || !currentState">
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">{{ 'edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate }}</div>
<redaction-small-chip [color]="'#E2E4E9'"></redaction-small-chip>
</div>
</ng-container>

View File

@ -16,7 +16,7 @@
</div>
<div class="cell">
<redaction-dossiers-listing-status [dossier]="dossier"></redaction-dossiers-listing-status>
<redaction-dossier-status [dossier]="dossier"></redaction-dossier-status>
<redaction-dossiers-listing-actions [dossier]="dossier" [stats]="stats"></redaction-dossiers-listing-actions>
</div>

View File

@ -10,7 +10,7 @@ import { dossierMemberChecker, dossierStateChecker, dossierTemplateChecker, Reda
import { workloadTranslations } from '../../translations/workload-translations';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossierStateService } from '../../../../services/entity-services/dossier-state.service';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
@Injectable()
export class ConfigService {

View File

@ -7,12 +7,10 @@ import { RouterModule, Routes } from '@angular/router';
import { DossiersListingActionsComponent } from './components/dossiers-listing-actions/dossiers-listing-actions.component';
import { SharedModule } from '@shared/shared.module';
import { DossiersListingDetailsComponent } from './components/dossiers-listing-details/dossiers-listing-details.component';
import { DossiersListingDossierNameComponent } from './components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component';
import { ConfigService } from './config.service';
import { TableItemComponent } from './components/table-item/table-item.component';
import { SharedDossiersModule } from '../../shared/shared-dossiers.module';
import { DossierWorkloadColumnComponent } from './components/dossier-workload-column/dossier-workload-column.component';
import { DossiersListingStatusComponent } from './components/dossiers-listing-status/dossiers-listing-status.component';
import { DossierDocumentsStatusComponent } from './components/dossier-documents-status/dossier-documents-status.component';
const routes: Routes = [
@ -29,10 +27,8 @@ const routes: Routes = [
DossiersListingScreenComponent,
DossiersListingActionsComponent,
DossiersListingDetailsComponent,
DossiersListingDossierNameComponent,
DossierWorkloadColumnComponent,
TableItemComponent,
DossiersListingStatusComponent,
DossierDocumentsStatusComponent,
],
providers: [ConfigService],

View File

@ -0,0 +1,6 @@
<div class="flex-align-items-center dossier-status-container">
<div class="dossier-status-text">
{{ currentState?.name || ('edit-dossier-dialog.general-info.form.dossier-status.placeholder' | translate) }}
</div>
<redaction-small-chip [color]="currentState?.color || '#E2E4E9'"></redaction-small-chip>
</div>

View File

@ -1,15 +1,14 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit } from '@angular/core';
import { Dossier } from '../../../../../../../../../../libs/red-domain/src';
import { DossierStateService } from '../../../../../../services/entity-services/dossier-state.service';
import { DossierState } from '@red/domain';
import { Dossier, DossierState } from '@red/domain';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
@Component({
selector: 'redaction-dossiers-listing-status',
templateUrl: './dossiers-listing-status.component.html',
styleUrls: ['./dossiers-listing-status.component.scss'],
selector: 'redaction-dossier-status',
templateUrl: './dossier-status.component.html',
styleUrls: ['./dossier-status.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossiersListingStatusComponent implements OnInit, OnChanges {
export class DossierStatusComponent implements OnInit, OnChanges {
@Input() dossier: Dossier;
currentState: DossierState;

View File

@ -9,7 +9,7 @@
</div>
</div>
<div class="stats-subtitle">
<div *ngIf="dossierStats" class="stats-subtitle">
<div class="small-label">
<mat-icon svgIcon="iqser:document"></mat-icon>
{{ dossierStats.numberOfFiles }}

View File

@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import * as moment from 'moment';
@ -17,11 +16,7 @@ export class DossiersListingDossierNameComponent {
@Input() dossier: Dossier;
@Input() dossierStats: DossierStats;
constructor(
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _dossiersService: DossiersService,
) {}
constructor(private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossiersService: DossiersService) {}
get approachingDueDate(): boolean {
return this._dueDateDaysDiff >= 0 && this._dueDateDaysDiff <= DUE_DATE_WARN_DAYS;

View File

@ -27,6 +27,8 @@ import { TeamMembersComponent } from './components/team-members/team-members.com
import { EditorComponent } from './components/editor/editor.component';
import { ExpandableFileActionsComponent } from './components/expandable-file-actions/expandable-file-actions.component';
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
import { DossierStatusComponent } from '@shared/components/dossier-status/dossier-status.component';
import { DossiersListingDossierNameComponent } from '@shared/components/dossiers-listing-dossier-name/dossiers-listing-dossier-name.component';
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
@ -43,6 +45,8 @@ const components = [
TeamMembersComponent,
ExpandableFileActionsComponent,
ProcessingIndicatorComponent,
DossierStatusComponent,
DossiersListingDossierNameComponent,
...buttons,
];

View File

@ -10,12 +10,18 @@ import { BreadcrumbTypes } from '@red/domain';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions;
export type BreadcrumbDisplayType = 'text' | 'dropdown';
export interface Breadcrumb {
readonly name$: Observable<string>;
readonly routerLink?: string[];
readonly routerLinkActiveOptions?: RouterLinkActiveOptions | undefined;
readonly clamp?: boolean;
readonly type: BreadcrumbDisplayType;
readonly options: {
readonly routerLink?: string[];
readonly routerLinkActiveOptions?: RouterLinkActiveOptions | undefined;
readonly clamp?: boolean;
readonly options?: Breadcrumb[];
readonly activeOption?: Breadcrumb;
};
}
export type Breadcrumbs = List<Breadcrumb>;
@ -63,7 +69,8 @@ export class BreadcrumbsService {
for (const breadcrumb of route.data.breadcrumbs || []) {
switch (breadcrumb) {
case BreadcrumbTypes.main:
this._addMainBreadcrumb();
case BreadcrumbTypes.archive:
this._addMainBreadcrumb(breadcrumb as 'main' | 'archive');
break;
case BreadcrumbTypes.dossier:
this._addDossierBreadcrumb(route);
@ -75,11 +82,34 @@ export class BreadcrumbsService {
}
}
private _addMainBreadcrumb(): void {
this._append({
private _addMainBreadcrumb(type: 'main' | 'archive'): void {
const activeDossiers: Breadcrumb = {
name$: of(this._translateService.instant('top-bar.navigation-items.dossiers')),
routerLink: ['/main', 'dossiers'],
routerLinkActiveOptions: { exact: true },
type: 'text' as BreadcrumbDisplayType,
options: {
routerLink: ['/main', 'dossiers'],
routerLinkActiveOptions: { exact: true },
},
};
const archivedDossiers: Breadcrumb = {
name$: of(this._translateService.instant('top-bar.navigation-items.archived-dossiers')),
type: 'text' as BreadcrumbDisplayType,
options: {
routerLink: ['/main', 'archive'],
routerLinkActiveOptions: { exact: true },
},
};
const activeOption = type === 'main' ? activeDossiers : archivedDossiers;
this._append({
name$: activeOption.name$,
type: 'dropdown' as BreadcrumbDisplayType,
options: {
options: [activeDossiers, archivedDossiers],
activeOption,
},
});
}
@ -87,9 +117,12 @@ export class BreadcrumbsService {
const dossierId = route.paramMap.get(DOSSIER_ID);
this._append({
name$: this._dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')),
routerLink: ['/main', 'dossiers', dossierId],
routerLinkActiveOptions: { exact: true },
clamp: true,
type: 'text' as BreadcrumbDisplayType,
options: {
routerLink: ['/main', 'dossiers', dossierId],
routerLinkActiveOptions: { exact: true },
clamp: true,
},
});
}
@ -98,8 +131,11 @@ export class BreadcrumbsService {
const fileId = route.paramMap.get(FILE_ID);
this._append({
name$: this._filesMapService.watch$(dossierId, fileId).pipe(pluck('filename')),
routerLink: ['/main', 'dossiers', dossierId, 'file', fileId],
clamp: true,
type: 'text' as BreadcrumbDisplayType,
options: {
routerLink: ['/main', 'dossiers', dossierId, 'file', fileId],
clamp: true,
},
});
}
}

View File

@ -0,0 +1,53 @@
import { Injectable, Injector } from '@angular/core';
import { EntitiesService, mapEach, Toaster } from '@iqser/common-ui';
import { Dossier, IDossier } from '@red/domain';
import { catchError, mapTo, switchMap, tap } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import { DossierStateService } from '@services/entity-services/dossier-state.service';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DossiersService } from '@services/entity-services/dossiers.service';
@Injectable({
providedIn: 'root',
})
export class ArchivedDossiersService extends EntitiesService<Dossier, IDossier> {
constructor(
protected readonly _injector: Injector,
private readonly _toaster: Toaster,
private readonly _dossierStateService: DossierStateService,
private readonly _dossierStatsService: DossierStatsService,
private readonly _dossiersService: DossiersService,
) {
super(_injector, Dossier, 'archived-dossiers');
}
loadAll(): Observable<Dossier[]> {
const dossierIds = (dossiers: Dossier[]) => dossiers.map(d => d.id);
return this.getAll().pipe(
mapEach(entity => new Dossier(entity)),
/* Load stats before updating entities */
switchMap(dossiers => this._dossierStatsService.getFor(dossierIds(dossiers)).pipe(mapTo(dossiers))),
switchMap(dossiers => this._dossierStateService.loadAllForAllTemplates().pipe(mapTo(dossiers))),
tap(dossiers => this.setEntities(dossiers)),
);
}
archive(dossiers: Dossier[]): Observable<unknown> {
const showToast = () => {
this._toaster.error(_('dossier-listing.archive.archive-failed'), { params: dossiers });
return of({});
};
return this._post(
dossiers.map(d => d.id),
`${this._defaultModelPath}/archive`,
).pipe(
tap(() => this.#removeDossiers(dossiers)),
catchError(showToast),
);
}
#removeDossiers(dossiers: Dossier[]): void {
this._dossiersService.setEntities(this._dossiersService.all.filter(dossier => !dossiers.find(d => dossier.id === d.id)));
}
}

View File

@ -118,20 +118,6 @@ export class DossiersService extends EntitiesService<Dossier, IDossier> {
);
}
archive(dossiers: Dossier[]): Observable<unknown> {
const showToast = () => {
this._toaster.error(_('dossier-listing.archive.archive-failed'), { params: dossiers });
return of({});
};
return this._post(
dossiers.map(d => d.id),
'archived-dossiers/archive',
).pipe(
tap(() => this.#removeDossiers(dossiers)),
catchError(showToast),
);
}
@Validate()
restore(@RequiredParam() dossierIds: List): Promise<unknown> {
return firstValueFrom(this._post(dossierIds, 'deleted-dossiers/restore').pipe(switchMap(() => this.loadAll())));

View File

@ -313,6 +313,23 @@
"suggestion-resize": "Suggested Resize"
},
"annotations": "Annotations",
"archived-dossiers-listing": {
"no-data": {
"title": "No archived dossiers."
},
"no-match": {
"title": "No archived dossiers match your current filters."
},
"table-col-names": {
"dossier-status": "Dossier Status",
"last-modified": "Last Modified",
"name": "Name",
"owner": "Owner"
},
"table-header": {
"title": "{length} Archived {length, plural, one{Dossier} other{Dossiers}}"
}
},
"assign-dossier-owner": {
"dialog": {
"approvers": "Approvers",
@ -1722,6 +1739,7 @@
},
"top-bar": {
"navigation-items": {
"archived-dossiers": "Archived Dossiers",
"back": "Back",
"dossiers": "Active Dossiers",
"my-account": {

View File

@ -22,6 +22,7 @@ export class Dossier implements IDossier, IListable {
readonly status: DossierStatus;
readonly watermarkEnabled: boolean;
readonly watermarkPreviewEnabled: boolean;
readonly archivedTime: string;
readonly hasReviewers: boolean;
constructor(dossier: IDossier) {
@ -43,6 +44,7 @@ export class Dossier implements IDossier, IListable {
this.status = dossier.status;
this.watermarkEnabled = dossier.watermarkEnabled;
this.watermarkPreviewEnabled = dossier.watermarkPreviewEnabled;
this.archivedTime = dossier.archivedTime;
this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
}

View File

@ -21,4 +21,5 @@ export interface IDossier {
readonly status: DossierStatus;
readonly watermarkEnabled: boolean;
readonly watermarkPreviewEnabled: boolean;
readonly archivedTime: string;
}

View File

@ -1,7 +1,8 @@
export type BreadcrumbType = 'main' | 'dossier' | 'file';
export type BreadcrumbType = 'main' | 'dossier' | 'file' | 'archive';
export const BreadcrumbTypes = {
main: 'main' as BreadcrumbType,
dossier: 'dossier' as BreadcrumbType,
file: 'file' as BreadcrumbType,
archive: 'archive' as BreadcrumbType,
};