RED-3796: Working downloads & search

This commit is contained in:
Adina Țeudan 2022-05-06 18:12:51 +03:00
parent 3739e7a468
commit 56765ea81e
26 changed files with 86 additions and 83 deletions

View File

@ -46,6 +46,23 @@ const routes: Routes = [
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
{
path: 'downloads',
component: DownloadsListScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard],
},
},
{
path: 'search',
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
{
path: `:${DOSSIER_TEMPLATE_ID}`,
children: [
@ -80,23 +97,6 @@ const routes: Routes = [
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
{
path: 'downloads',
component: DownloadsListScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard],
},
},
{
path: 'search',
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, DossiersGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'],
},
},
],
},
{

View File

@ -22,7 +22,7 @@ export class TableItemComponent implements OnChanges {
ngOnChanges() {
if (this.dossier) {
this.#ngOnChanges$.next(this.dossier.dossierId);
this.#ngOnChanges$.next(this.dossier.id);
}
}
}

View File

@ -36,20 +36,20 @@
</div>
</div>
</div>
<div class="flex-2">
<div class="flex-3">
<redaction-simple-doughnut-chart
[config]="translateChartService.translateDossierStates(dossierTemplate.dossiersChartData, dossierTemplate.id)"
[radius]="70"
[radius]="63"
[strokeWidth]="15"
[subtitle]="'dossier-template-stats.active-dossiers' | translate: { count: dossierTemplate.numberOfActiveDossiers }"
direction="row"
totalType="sum"
></redaction-simple-doughnut-chart>
</div>
<div class="flex-2">
<div class="flex-3">
<redaction-simple-doughnut-chart
[config]="translateChartService.translateWorkflowStatus(dossierTemplate.documentsChartData)"
[radius]="70"
[radius]="63"
[strokeWidth]="15"
[subtitle]="'dossier-template-stats.total-documents' | translate"
direction="row"

View File

@ -28,6 +28,7 @@
padding: 24px;
display: flex;
flex-direction: column;
overflow: hidden;
&:not(:first-child) {
justify-content: center;

View File

@ -28,14 +28,14 @@ export class DossierDetailsStatsComponent implements OnInit {
) {}
ngOnInit() {
this.dossierStats$ = this._dossierStatsService.watch$(this.dossier.dossierId);
this.dossierStats$ = this._dossierStatsService.watch$(this.dossier.id);
this.dossierTemplateName = this._dossierTemplatesService.find(this.dossier.dossierTemplateId)?.name || '-';
}
openEditDossierDialog(section: string): void {
const data = { dossierId: this.dossier.dossierId, section };
const data = { dossierId: this.dossier.id, section };
this._dialogService.openDialog('editDossier', null, data, async () => {
await firstValueFrom(this._filesService.loadAll(this.dossier.dossierId));
await firstValueFrom(this._filesService.loadAll(this.dossier.id));
});
}
}

View File

@ -51,7 +51,7 @@ export class DossierOverviewScreenHeaderComponent implements OnInit {
) {}
ngOnInit() {
this.actionConfigs = this.configService.actionConfig(this.dossier.dossierId, this.listingService.areSomeSelected$);
this.actionConfigs = this.configService.actionConfig(this.dossier.id, this.listingService.areSomeSelected$);
}
async reanalyseDossier() {

View File

@ -77,7 +77,7 @@ export class AddDossierDialogComponent extends BaseDialogComponent {
await this._router.navigate([savedDossier.routerLink]);
if (options?.addMembers) {
this._dialogService.openDialog('editDossier', null, {
dossierId: savedDossier.dossierId,
dossierId: savedDossier.id,
section: 'members',
});
}

View File

@ -3,7 +3,7 @@
<mat-form-field floatLabel="always">
<mat-label>{{ 'assign-dossier-owner.dialog.single-user' | translate }}</mat-label>
<mat-select formControlName="owner" id="editDossierOwnerSelect">
<mat-option *ngFor="let userId of ownersSelectOptions; let index = index" [value]="userId" [id]="'mat-option-' + index">
<mat-option *ngFor="let userId of ownersSelectOptions; let index = index" [id]="'mat-option-' + index" [value]="userId">
{{ userId | name }}
</mat-option>
</mat-select>
@ -11,12 +11,12 @@
</div>
<ng-container *ngIf="selectedApproversList.length">
<div class="all-caps-label mt-16" translate="assign-dossier-owner.dialog.approvers" id="approversLabel"></div>
<div class="all-caps-label mt-16" id="approversLabel" translate="assign-dossier-owner.dialog.approvers"></div>
<redaction-team-members
(remove)="toggleSelected($event)"
[canAdd]="false"
[canRemove]="hasOwner && !disabled"
[dossierId]="dossier.dossierId"
[dossierId]="dossier.id"
[largeSpacing]="true"
[memberIds]="selectedApproversList"
[perLine]="13"
@ -25,12 +25,12 @@
</ng-container>
<ng-container *ngIf="(selectedReviewers$ | async)?.length">
<div class="all-caps-label mt-16" translate="assign-dossier-owner.dialog.reviewers" id="reviewersLabel"></div>
<div class="all-caps-label mt-16" id="reviewersLabel" translate="assign-dossier-owner.dialog.reviewers"></div>
<redaction-team-members
(remove)="toggleSelected($event)"
[canAdd]="false"
[canRemove]="hasOwner && !disabled"
[dossierId]="dossier.dossierId"
[dossierId]="dossier.id"
[largeSpacing]="true"
[memberIds]="selectedReviewers$ | async"
[perLine]="13"

View File

@ -187,7 +187,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
dossierTemplateId: [
{
value: this.dossier.dossierTemplateId,
disabled: this._dossierStatsService.get(this.dossier.dossierId).hasFiles || !this.dossier.isActive,
disabled: this._dossierStatsService.get(this.dossier.id).hasFiles || !this.dossier.isActive,
},
Validators.required,
],

View File

@ -1,6 +1,6 @@
<div (longPress)="forceReanalysisAction($event)" class="action-buttons" redactionLongPress>
<iqser-circle-button
(action)="openEditDossierDialog($event, dossier.dossierId)"
(action)="openEditDossierDialog($event, dossier.id)"
*ngIf="currentUser.isUser"
[icon]="currentUser.isManager ? 'iqser:edit' : 'red:info'"
[scrollableParentView]="scrollableParentView"

View File

@ -44,7 +44,7 @@ export class DossiersListingActionsComponent implements OnChanges {
}
ngOnChanges() {
this.files = this.filesMapService.get(this.dossier.dossierId);
this.files = this.filesMapService.get(this.dossier.id);
this.displayReanalyseBtn = this.permissionsService.displayReanalyseBtn(this.dossier) && this.analysisForced;
}

View File

@ -22,7 +22,7 @@ export class TableItemComponent implements OnChanges {
ngOnChanges() {
if (this.dossier) {
this.#ngOnChanges$.next(this.dossier.dossierId);
this.#ngOnChanges$.next(this.dossier.id);
}
}
}

View File

@ -227,12 +227,12 @@ export class ConfigService {
}
private _dossierStatusChecker = (dossier: Dossier, filter: INestedFilter) => {
const stats = this._dossierStatsService.get(dossier.dossierId);
const stats = this._dossierStatsService.get(dossier.id);
return stats?.fileCountPerWorkflowStatus[filter.id];
};
private _annotationFilterChecker = (dossier: Dossier, filter: INestedFilter) => {
const stats = this._dossierStatsService.get(dossier.dossierId);
const stats = this._dossierStatsService.get(dossier.id);
switch (filter.id) {
// case 'analysis': {
// return stats.reanalysisRequired;

View File

@ -54,10 +54,7 @@ export class AcceptRecommendationDialogComponent extends BaseDialogComponent imp
async ngOnInit() {
super.ngOnInit();
this.possibleDictionaries = await this._dictionaryService.getDictionariesOptions(
this._dossier.dossierTemplateId,
this._dossier.dossierId,
);
this.possibleDictionaries = await this._dictionaryService.getDictionariesOptions(this._dossier.dossierTemplateId, this._dossier.id);
this.form.patchValue({
dictionary: this.possibleDictionaries.find(dict => dict.type === this.data.annotations[0].recommendationType).type,
});

View File

@ -76,10 +76,7 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
async ngOnInit() {
super.ngOnInit();
this.possibleDictionaries = await this._dictionaryService.getDictionariesOptions(
this._dossier.dossierTemplateId,
this._dossier.dossierId,
);
this.possibleDictionaries = await this._dictionaryService.getDictionariesOptions(this._dossier.dossierTemplateId, this._dossier.id);
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this._dossier.dossierTemplateId));
this.legalOptions = data.map(lbm => ({
legalBasis: lbm.reason,

View File

@ -427,7 +427,7 @@ export class AnnotationActionsService {
value: text,
};
this._processObsAndEmit(this._manualRedactionService.resizeOrSuggestResize([resizeRequest], data.dossier.dossierId, fileId));
this._processObsAndEmit(this._manualRedactionService.resizeOrSuggestResize([resizeRequest], data.dossier.id, fileId));
});
}

View File

@ -20,10 +20,9 @@ import { RouterHistoryService } from '@services/router-history.service';
import { Dossier, IMatchedDocument, ISearchListItem, ISearchResponse } from '@red/domain';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { PlatformSearchService } from '@services/entity-services/platform-search.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
import { FeaturesService } from '@services/features.service';
import { DOSSIERS_ARCHIVE } from '@utils/constants';
import { DossiersCacheService } from '../../../services/dossiers/dossiers-cache.service';
@Component({
templateUrl: './search-screen.component.html',
@ -66,8 +65,7 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> imp
protected readonly _injector: Injector,
private readonly _activatedRoute: ActivatedRoute,
private readonly _loadingService: LoadingService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
private readonly _dossiersCacheService: DossiersCacheService,
readonly routerHistoryService: RouterHistoryService,
private readonly _translateService: TranslateService,
private readonly _filesMapService: FilesMapService,
@ -83,13 +81,12 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> imp
const checked = dossierIds.includes(id);
return new NestedFilter({ id, label: dossierName, checked });
};
const allDossiers = [...this._activeDossiersService.all, ...this._archivedDossiersService.all];
const dossierNameFilter: IFilterGroup = {
slug: 'dossiers',
label: this._translateService.instant('search-screen.filters.by-dossier'),
filterceptionPlaceholder: this._translateService.instant('search-screen.filters.search-placeholder'),
icon: 'red:folder',
filters: allDossiers.map(dossierToFilter),
filters: this._dossiersCacheService.all.map(dossierToFilter),
checker: keyChecker('dossierId'),
};
this.filterService.addFilterGroups([dossierNameFilter]);
@ -166,10 +163,11 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> imp
}: IMatchedDocument): ISearchListItem {
const file = this._filesMapService.get(dossierId, fileId);
if (!file) {
console.error('Missing file');
return undefined;
}
const dossier = (dossierArchived ? this._archivedDossiersService : this._activeDossiersService).find(dossierId);
const dossier = this._dossiersCacheService.get(dossierId);
return {
id: fileId,

View File

@ -82,7 +82,7 @@ export class DictionaryManagerComponent implements OnChanges {
return;
}
this._onDossierChanged(dossier.dossierTemplateId, dossier.dossierId)
this._onDossierChanged(dossier.dossierTemplateId, dossier.id)
.pipe(take(1))
.subscribe(entries => {
this.diffEditorText = entries;

View File

@ -1,16 +1,25 @@
import { Injectable, Injector } from '@angular/core';
import { switchMap, tap } from 'rxjs/operators';
import { timer } from 'rxjs';
import { CHANGED_CHECK_INTERVAL, DOSSIERS_ROUTE } from '@utils/constants';
import { DossiersService } from './dossiers.service';
import { Observable, timer } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { Dossier } from '@red/domain';
@Injectable({
providedIn: 'root',
})
export class ActiveDossiersService extends DossiersService {
private _initializedRefresh = false;
constructor(protected readonly _injector: Injector) {
super(_injector, 'dossier', DOSSIERS_ROUTE);
}
initializeRefresh() {
if (this._initializedRefresh) {
return;
}
this._initializedRefresh = true;
timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL)
.pipe(
switchMap(() => this.loadOnlyChanged()),
@ -18,4 +27,9 @@ export class ActiveDossiersService extends DossiersService {
)
.subscribe();
}
loadAll(): Observable<Dossier[]> {
this.initializeRefresh();
return super.loadAll();
}
}

View File

@ -3,7 +3,6 @@ import { ActiveDossiersService } from './active-dossiers.service';
import { ArchivedDossiersService } from './archived-dossiers.service';
import { firstValueFrom, forkJoin, merge } from 'rxjs';
import { map, skip, take } from 'rxjs/operators';
import { flatten } from 'lodash-es';
import { Dossier } from '@red/domain';
@Injectable({
@ -27,23 +26,27 @@ export class DossiersCacheService {
return !localStorage.getItem('dossiers');
}
get all(): Dossier[] {
return this._dossiers;
}
async load(): Promise<void> {
await firstValueFrom(
forkJoin([this._activeDossiersService.loadAll().pipe(take(1)), this._archivedDossiersService.loadAll().pipe(take(1))]).pipe(
map(list => flatten(list)),
map(list => list.flat()),
),
);
this.set();
}
set(): void {
const dossiers = flatten([this._activeDossiersService.all, this._archivedDossiersService.all]);
const dossiers = [this._activeDossiersService.all, this._archivedDossiersService.all].flat();
this._dossiers = dossiers;
localStorage.setItem('dossiers', JSON.stringify(dossiers));
this.changed$.emit();
}
get(dossierId: string) {
return this._dossiers.find(dossier => dossier.dossierId === dossierId);
return this._dossiers.find(dossier => dossier.id === dossierId);
}
}

View File

@ -2,20 +2,17 @@ import { Injectable, Injector } from '@angular/core';
import { GenericService } from '@iqser/common-ui';
import { Dossier, IMatchedDocument, ISearchInput, ISearchRequest, ISearchResponse } from '@red/domain';
import { Observable, of, zip } from 'rxjs';
import { mapTo, switchMap } from 'rxjs/operators';
import { ActiveDossiersService } from '../dossiers/active-dossiers.service';
import { map, switchMap } from 'rxjs/operators';
import { FilesMapService } from './files-map.service';
import { FilesService } from './files.service';
import { ArchivedDossiersService } from '../dossiers/archived-dossiers.service';
import { DossiersService } from '../dossiers/dossiers.service';
import { DossiersCacheService } from '../dossiers/dossiers-cache.service';
@Injectable({ providedIn: 'root' })
export class PlatformSearchService extends GenericService<ISearchResponse> {
constructor(
protected readonly _injector: Injector,
private readonly _filesService: FilesService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
private readonly _dossiersCacheService: DossiersCacheService,
private readonly _filesMapService: FilesMapService,
) {
super(_injector, 'search-v2');
@ -42,17 +39,16 @@ export class PlatformSearchService extends GenericService<ISearchResponse> {
return this._post(body).pipe(switchMap(searchValue => this._loadMissingFiles$(searchValue)));
}
private _dossiersWithMissingFiles(searchResponse: ISearchResponse, service: DossiersService): Dossier[] {
const documentsOfType = searchResponse.matchedDocuments.filter(document => service.has(document.dossierId));
private _dossiersWithMissingFiles(searchResponse: ISearchResponse): Dossier[] {
const documentsOfType = searchResponse.matchedDocuments.filter(document => this._dossiersCacheService.get(document.dossierId));
const fileNotLoaded = ({ dossierId, fileId }: IMatchedDocument) => !this._filesMapService.get(dossierId, fileId);
const dossiersWithNotLoadedFiles = documentsOfType.filter(fileNotLoaded).map(document => document.dossierId);
return Array.from(new Set(dossiersWithNotLoadedFiles)).map(dossierId => service.find(dossierId));
return Array.from(new Set(dossiersWithNotLoadedFiles)).map(dossierId => this._dossiersCacheService.get(dossierId));
}
private _loadMissingFiles$(searchResponse: ISearchResponse): Observable<ISearchResponse> {
const services = [this._activeDossiersService, this._archivedDossiersService];
const dossiers = services.map(service => this._dossiersWithMissingFiles(searchResponse, service)).flat();
return dossiers.length ? this._loadFilesFor$(dossiers).pipe(mapTo(searchResponse)) : of(searchResponse);
const dossiers = this._dossiersWithMissingFiles(searchResponse);
return dossiers.length ? this._loadFilesFor$(dossiers).pipe(map(() => searchResponse)) : of(searchResponse);
}
private _loadFilesFor$(dossiers: Dossier[]) {

View File

@ -34,7 +34,7 @@ export class TrashService extends GenericService<TrashItem> {
this._toaster.error(_('dossier-listing.delete.delete-failed'), { params: dossier });
return of({});
};
return this.delete(dossier.dossierId, 'dossier').pipe(
return this.delete(dossier.id, 'dossier').pipe(
switchMap(() => this._activeDossiersService.loadAll()),
catchError(showToast),
);

View File

@ -33,7 +33,7 @@ export class PermissionsService {
}
displayReanalyseBtn(dossier: Dossier): boolean {
return this.isApprover(dossier) && !!this._filesMapService.get(dossier.dossierId).find(f => f.analysisRequired);
return this.isApprover(dossier) && !!this._filesMapService.get(dossier.id).find(f => f.analysisRequired);
}
canUploadFiles(dossier: Dossier): boolean {

@ -1 +1 @@
Subproject commit dc9323a0ec4d2cdc8ffbb31df67995531160462c
Subproject commit 224e078b583a33c1e4a621c071d0a24d2d49d4c6

View File

@ -25,6 +25,7 @@ export class Dossier implements IDossier, IListable {
readonly hasReviewers: boolean;
readonly routerLink: string;
readonly dossiersListRouterLink: string;
readonly id: string;
constructor(dossier: IDossier) {
this.dossierId = dossier.dossierId;
@ -47,15 +48,12 @@ export class Dossier implements IDossier, IListable {
this.archivedTime = dossier.archivedTime;
this.hasReviewers = !!this.memberIds && this.memberIds.length > 1;
this.id = this.dossierId;
const routerPath = (this.isArchived ? ARCHIVE_ROUTE : DOSSIERS_ROUTE) as string;
this.dossiersListRouterLink = `/main/${this.dossierTemplateId}/${routerPath}`;
this.routerLink = `${this.dossiersListRouterLink}/${this.dossierId}`;
}
get id(): string {
return this.dossierId;
}
get searchKey(): string {
return this.dossierName;
}

View File

@ -16,6 +16,7 @@ export class TrashDossier extends TrashItem {
readonly dueDate?: string;
readonly ownerId: string;
readonly softDeletedTime: string;
readonly id: string;
constructor(
dossier: IDossier,
@ -35,10 +36,8 @@ export class TrashDossier extends TrashItem {
// Because of migrations, for some this is not set
this.softDeletedTime = dossier.softDeletedTime || '-';
this.canRestore = this.canRestore && this._hasRestoreRights;
}
get id(): string {
return this.dossierId;
this.id = this.dossierId;
}
get searchKey(): string {