281 lines
12 KiB
TypeScript
281 lines
12 KiB
TypeScript
import { AsyncPipe, NgIf } from '@angular/common';
|
|
import { Component, ElementRef, HostListener, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
import {
|
|
CircleButtonTypes,
|
|
CustomError,
|
|
ErrorService,
|
|
HasScrollbarDirective,
|
|
IqserListingModule,
|
|
IqserPermissionsService,
|
|
ListingComponent,
|
|
ListingModes,
|
|
listingProvidersFactory,
|
|
LoadingService,
|
|
TableColumnConfig,
|
|
TableComponent,
|
|
WorkflowConfig,
|
|
} from '@iqser/common-ui';
|
|
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
|
|
import { getParam, OnAttach, OnDetach, shareLast } from '@iqser/common-ui/lib/utils';
|
|
import { TranslateModule } from '@ngx-translate/core';
|
|
import {
|
|
Dossier,
|
|
DOSSIER_ID,
|
|
DossierAttributeConfig,
|
|
DossierAttributeWithValue,
|
|
File,
|
|
IFileAttributeConfig,
|
|
WorkflowFileStatus,
|
|
} from '@red/domain';
|
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
|
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
|
import { DossierAttributesService } from '@services/entity-services/dossier-attributes.service';
|
|
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
|
import { FilesMapService } from '@services/files/files-map.service';
|
|
import { FilesService } from '@services/files/files.service';
|
|
import { PermissionsService } from '@services/permissions.service';
|
|
import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component';
|
|
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
|
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
|
import { FileUploadService } from '@upload-download/services/file-upload.service';
|
|
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
|
|
import { Roles } from '@users/roles';
|
|
import { UserPreferenceService } from '@users/user-preference.service';
|
|
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
|
import { merge, Observable } from 'rxjs';
|
|
import { filter, skip, switchMap, tap } from 'rxjs/operators';
|
|
import { DossierOverviewBulkActionsComponent } from '../components/bulk-actions/dossier-overview-bulk-actions.component';
|
|
import { DossierDetailsComponent } from '../components/dossier-details/dossier-details.component';
|
|
import { DossierOverviewScreenHeaderComponent } from '../components/screen-header/dossier-overview-screen-header.component';
|
|
import { TableItemComponent } from '../components/table-item/table-item.component';
|
|
import { WorkflowItemComponent } from '../components/workflow-item/workflow-item.component';
|
|
import { ConfigService } from '../config.service';
|
|
import { BulkActionsService } from '../services/bulk-actions.service';
|
|
|
|
@Component({
|
|
templateUrl: './dossier-overview-screen.component.html',
|
|
styleUrls: ['./dossier-overview-screen.component.scss'],
|
|
providers: [...listingProvidersFactory(DossierOverviewScreenComponent), ConfigService, BulkActionsService, dossiersServiceProvider],
|
|
standalone: true,
|
|
imports: [
|
|
DossierOverviewScreenHeaderComponent,
|
|
AsyncPipe,
|
|
IqserListingModule,
|
|
TranslateModule,
|
|
NgIf,
|
|
WorkflowItemComponent,
|
|
DossierDetailsComponent,
|
|
DossierOverviewBulkActionsComponent,
|
|
TableItemComponent,
|
|
TypeFilterComponent,
|
|
HasScrollbarDirective,
|
|
],
|
|
})
|
|
export default class DossierOverviewScreenComponent extends ListingComponent<File> implements OnInit, OnAttach, OnDetach, OnDestroy {
|
|
#dossier: Dossier;
|
|
@ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true })
|
|
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
|
|
@ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef;
|
|
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
|
|
#fileAttributeConfigs: IFileAttributeConfig[];
|
|
readonly listingModes = ListingModes;
|
|
readonly circleButtonTypes = CircleButtonTypes;
|
|
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
|
|
collapsedDetails = false;
|
|
dossierAttributes: DossierAttributeWithValue[] = [];
|
|
tableColumnConfigs: readonly TableColumnConfig<File>[];
|
|
displayedInFileListAttributes: IFileAttributeConfig[] = [];
|
|
displayedAttributes: IFileAttributeConfig[] = [];
|
|
displayedWorkflowAttributes: IFileAttributeConfig[] = [];
|
|
readonly listingMode$ = this.configService.listingMode$.pipe(
|
|
tap(() => this.#computeAllFilters()),
|
|
shareLast(),
|
|
);
|
|
readonly dossier$: Observable<Dossier>;
|
|
readonly files$: Observable<File[]>;
|
|
readonly dossierId = getParam(DOSSIER_ID);
|
|
readonly dossierTemplateId: string;
|
|
readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus>;
|
|
readonly dossierAttributes$: Observable<DossierAttributeConfig[]>;
|
|
|
|
constructor(
|
|
readonly configService: ConfigService,
|
|
private readonly _errorService: ErrorService,
|
|
private readonly _filesService: FilesService,
|
|
readonly permissionsService: PermissionsService,
|
|
iqserPermissionsService: IqserPermissionsService,
|
|
private readonly _loadingService: LoadingService,
|
|
private readonly _fileMapService: FilesMapService,
|
|
private readonly _dossiersService: DossiersService,
|
|
private readonly _fileUploadService: FileUploadService,
|
|
private readonly _statusOverlayService: StatusOverlayService,
|
|
private readonly _fileAttributesService: FileAttributesService,
|
|
private readonly _userPreferenceService: UserPreferenceService,
|
|
private readonly _fileDropOverlayService: FileDropOverlayService,
|
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
|
private readonly _dossierAttributesService: DossierAttributesService,
|
|
private readonly _dossiersCacheService: DossiersCacheService,
|
|
) {
|
|
super();
|
|
this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.#dossier = dossier)));
|
|
this.dossierAttributes$ = this._dossierAttributesService.all$.pipe(tap(() => this.#updateDossierAttributes()));
|
|
this.#dossier = _dossiersService.find(this.dossierId);
|
|
this.dossierTemplateId = this.#dossier.dossierTemplateId;
|
|
const hasRss = iqserPermissionsService.has(Roles.getRss);
|
|
this.workflowConfig = hasRss ? configService.workflowConfigRss() : configService.workflowConfig();
|
|
this.files$ = merge(this.#files$, this.#dossierFilesChange$).pipe(shareLast());
|
|
this.#updateFileAttributes();
|
|
}
|
|
|
|
get checkedRequiredFilters(): NestedFilter[] {
|
|
return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
|
|
}
|
|
|
|
get checkedNotRequiredFilters(): NestedFilter[] {
|
|
return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
|
|
}
|
|
|
|
get #files$() {
|
|
return this._fileMapService.get$(this.dossierId).pipe(
|
|
tap(files => this.entitiesService.setEntities(files)),
|
|
tap(() => this.#computeAllFilters()),
|
|
);
|
|
}
|
|
|
|
get #dossierFilesChange$() {
|
|
return this._dossiersService.dossierFileChanges$.pipe(
|
|
filter(dossierId => dossierId === this.dossierId && !!this._dossiersCacheService.get(dossierId)),
|
|
switchMap(dossierId => this._filesService.loadAll(dossierId)),
|
|
);
|
|
}
|
|
|
|
disabledFn = (file: File) => file.excluded;
|
|
|
|
lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;
|
|
|
|
async ngOnInit(): Promise<void> {
|
|
this.#computeAllFilters();
|
|
this.#setRemovableSubscriptions();
|
|
this.#initFileDropHandling();
|
|
|
|
this._loadingService.stop();
|
|
}
|
|
|
|
ngOnAttach() {
|
|
this.#initFileDropHandling();
|
|
this.#setRemovableSubscriptions();
|
|
this.#updateFileAttributes();
|
|
this._tableComponent?.scrollToLastIndex();
|
|
}
|
|
|
|
override ngOnDetach() {
|
|
this.#cleanupFileDropHandling();
|
|
super.ngOnDetach();
|
|
}
|
|
|
|
override ngOnDestroy() {
|
|
this.#cleanupFileDropHandling();
|
|
super.ngOnDestroy();
|
|
}
|
|
|
|
@HostListener('drop', ['$event'])
|
|
onDrop(event: DragEvent): void {
|
|
if (this.permissionsService.canUploadFiles(this.#dossier)) {
|
|
handleFileDrop(event, this.#dossier, this.#uploadFiles.bind(this));
|
|
}
|
|
}
|
|
|
|
@HostListener('dragover', ['$event'])
|
|
onDragOver(event): void {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
}
|
|
|
|
async uploadFiles(files: Files): Promise<void> {
|
|
await this.#uploadFiles(convertFiles(files, this.#dossier));
|
|
(this._fileInput as any).nativeElement.value = null;
|
|
}
|
|
|
|
#cleanupFileDropHandling() {
|
|
if (this.permissionsService.canUploadFiles(this.#dossier)) {
|
|
this._fileDropOverlayService.cleanupFileDropHandling();
|
|
}
|
|
}
|
|
|
|
#initFileDropHandling(): void {
|
|
if (this.permissionsService.canUploadFiles(this.#dossier)) {
|
|
this._fileDropOverlayService.initFileDropHandling(this.dossierId);
|
|
}
|
|
}
|
|
|
|
async #updateDossierAttributes(): Promise<void> {
|
|
try {
|
|
this.dossierAttributes = await this._dossierAttributesService.getWithValues(this.#dossier);
|
|
} catch (e) {
|
|
console.error('[DOSSIER ATTRIBUTES] Error: ', e);
|
|
}
|
|
}
|
|
|
|
#setRemovableSubscriptions(): void {
|
|
this.addActiveScreenSubscription = this._dossiersService
|
|
.getEntityDeleted$(this.dossierId)
|
|
.pipe(tap(() => this.#handleDeletedDossier()))
|
|
.subscribe();
|
|
|
|
this.addActiveScreenSubscription = this._dossierTemplatesService
|
|
.getEntityChanged$(this.#dossier.dossierTemplateId)
|
|
.pipe(
|
|
skip(1),
|
|
tap(() => this.#updateFileAttributes()),
|
|
)
|
|
.subscribe();
|
|
}
|
|
|
|
#handleDeletedDossier(): void {
|
|
this._errorService.set(
|
|
new CustomError(_('error.deleted-entity.dossier.label'), _('error.deleted-entity.dossier.action'), 'iqser:expand'),
|
|
);
|
|
}
|
|
|
|
#updateFileAttributes() {
|
|
const attributes = this._fileAttributesService.getFileAttributeConfig(this.#dossier.dossierTemplateId);
|
|
this.#fileAttributeConfigs = attributes?.fileAttributeConfigs || [];
|
|
this.displayedInFileListAttributes = this.#fileAttributeConfigs.filter(config => config.displayedInFileList);
|
|
this.displayedAttributes = this.displayedInFileListAttributes.filter(c => c.displayedInFileList);
|
|
this.displayedWorkflowAttributes = this.#getDisplayedWorkflowAttributes(this.displayedAttributes);
|
|
this.tableColumnConfigs = this.configService.tableConfig(this.displayedAttributes);
|
|
this.#computeAllFilters();
|
|
}
|
|
|
|
#getDisplayedWorkflowAttributes(displayedAttributes: IFileAttributeConfig[]): IFileAttributeConfig[] {
|
|
let primaryAttribute = displayedAttributes.find(c => c.primaryAttribute);
|
|
if (!primaryAttribute) {
|
|
primaryAttribute = this.#fileAttributeConfigs.find(config => config.primaryAttribute);
|
|
return primaryAttribute ? [primaryAttribute, ...displayedAttributes] : displayedAttributes;
|
|
}
|
|
return displayedAttributes.sort((c1, c2) => (c1.primaryAttribute ? -1 : c2.primaryAttribute ? 1 : 0));
|
|
}
|
|
|
|
async #uploadFiles(files: FileUploadModel[]) {
|
|
const fileCount = await this._fileUploadService.uploadFiles(files, this.dossierId);
|
|
if (fileCount) {
|
|
this._statusOverlayService.openUploadStatusOverlay();
|
|
}
|
|
}
|
|
|
|
#computeAllFilters() {
|
|
const filterGroups = this.configService.filterGroups(
|
|
this.entitiesService.all,
|
|
this.#fileAttributeConfigs,
|
|
this.#dossier.dossierTemplateId,
|
|
this._needsWorkFilterTemplate,
|
|
() => this.checkedRequiredFilters,
|
|
() => this.checkedNotRequiredFilters,
|
|
);
|
|
this.filterService.addFilterGroups(filterGroups, true);
|
|
}
|
|
}
|