Merge branch 'master' into VM/NotificationsPreferences
This commit is contained in:
commit
3bbba77f45
@ -64,7 +64,7 @@ export class BaseScreenComponent {
|
||||
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)
|
||||
action: (query): void => this._search(query, this.appStateService.activeDossier.id)
|
||||
},
|
||||
{
|
||||
text: this._translateService.instant('search.entire-platform'),
|
||||
|
||||
@ -21,7 +21,6 @@ export class DownloadsListScreenComponent extends ListingComponent<DownloadStatu
|
||||
@ViewChild('sizeTemplate', { static: true }) sizeTemplate: TemplateRef<never>;
|
||||
@ViewChild('creationDateTemplate', { static: true }) creationDateTemplate: TemplateRef<never>;
|
||||
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'storageId';
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
import { AuditModel } from '@redaction/red-ui-http';
|
||||
import { Listable } from '@iqser/common-ui';
|
||||
|
||||
export class AuditModelWrapper implements Listable {
|
||||
constructor(public auditModel: AuditModel) {}
|
||||
|
||||
get category(): string {
|
||||
return this.auditModel.category;
|
||||
}
|
||||
|
||||
get details(): any {
|
||||
return this.auditModel.details;
|
||||
}
|
||||
|
||||
get message(): string {
|
||||
return this.auditModel.message;
|
||||
}
|
||||
|
||||
get recordId(): string {
|
||||
return this.auditModel.recordId;
|
||||
}
|
||||
|
||||
get recordDate(): string {
|
||||
return this.auditModel.recordDate;
|
||||
}
|
||||
|
||||
get objectId(): string {
|
||||
return this.auditModel.objectId;
|
||||
}
|
||||
|
||||
get userId(): string {
|
||||
return this.auditModel.userId;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.auditModel.recordDate;
|
||||
}
|
||||
}
|
||||
30
apps/red-ui/src/app/models/audit.model.ts
Normal file
30
apps/red-ui/src/app/models/audit.model.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { IAudit } from '@redaction/red-ui-http';
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
|
||||
export class Audit implements IAudit, IListable {
|
||||
readonly category?: string;
|
||||
readonly details?: unknown;
|
||||
readonly message?: string;
|
||||
readonly objectId?: string;
|
||||
readonly recordDate?: string;
|
||||
readonly recordId?: string;
|
||||
readonly userId?: string;
|
||||
|
||||
constructor(audit: IAudit) {
|
||||
this.category = audit.category;
|
||||
this.details = audit.details;
|
||||
this.message = audit.message;
|
||||
this.objectId = audit.objectId;
|
||||
this.recordDate = audit.recordDate;
|
||||
this.recordId = audit.recordId;
|
||||
this.userId = audit.userId;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.recordDate;
|
||||
}
|
||||
|
||||
get searchKey(): string {
|
||||
return this.recordDate;
|
||||
}
|
||||
}
|
||||
36
apps/red-ui/src/app/models/dictionary.ts
Normal file
36
apps/red-ui/src/app/models/dictionary.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { IDictionary } from '@redaction/red-ui-http';
|
||||
import { IListable, List } from '@iqser/common-ui';
|
||||
|
||||
export class Dictionary implements IDictionary, IListable {
|
||||
readonly addToDictionaryAction: boolean;
|
||||
readonly caseInsensitive: boolean;
|
||||
readonly description?: string;
|
||||
readonly dossierTemplateId?: string;
|
||||
readonly entries?: List;
|
||||
readonly hexColor?: string;
|
||||
readonly hint: boolean;
|
||||
readonly label?: string;
|
||||
readonly rank?: number;
|
||||
readonly recommendation: boolean;
|
||||
|
||||
constructor(dictionary: IDictionary) {
|
||||
this.addToDictionaryAction = !!dictionary.addToDictionaryAction;
|
||||
this.caseInsensitive = !!dictionary.caseInsensitive;
|
||||
this.description = dictionary.description;
|
||||
this.dossierTemplateId = dictionary.dossierTemplateId;
|
||||
this.entries = dictionary.entries;
|
||||
this.hexColor = dictionary.hexColor;
|
||||
this.hint = !!dictionary.hint;
|
||||
this.label = dictionary.label;
|
||||
this.rank = dictionary.rank;
|
||||
this.recommendation = !!dictionary.recommendation;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
get searchKey(): string {
|
||||
return this.label;
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
import { DossierAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { IDossierAttributeConfig } from '@redaction/red-ui-http';
|
||||
|
||||
export type DossierAttributeWithValue = DossierAttributeConfig & { value: any };
|
||||
export type DossierAttributeWithValue = IDossierAttributeConfig & { value: any };
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { UserWrapper } from '@services/user.service';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import { isArray } from 'rxjs/internal-compatibility';
|
||||
import { User } from '@models/user';
|
||||
|
||||
export class AnnotationPermissions {
|
||||
canUndo = true;
|
||||
@ -14,7 +14,7 @@ export class AnnotationPermissions {
|
||||
canChangeLegalBasis = true;
|
||||
canRecategorizeImage = true;
|
||||
|
||||
static forUser(isApprover: boolean, user: UserWrapper, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
||||
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
||||
if (!isArray(annotations)) {
|
||||
annotations = [annotations];
|
||||
}
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
import { DossierTemplateModel, FileAttributesConfig } from '@redaction/red-ui-http';
|
||||
import { Listable } from '@iqser/common-ui';
|
||||
|
||||
export class DossierTemplateModelWrapper implements Listable {
|
||||
dictionariesCount = 0;
|
||||
totalDictionaryEntries = 0;
|
||||
|
||||
constructor(public dossierTemplateModel: DossierTemplateModel, public fileAttributesConfig: FileAttributesConfig) {}
|
||||
|
||||
get id() {
|
||||
return this.dossierTemplateModel.dossierTemplateId;
|
||||
}
|
||||
|
||||
get createdBy() {
|
||||
return this.dossierTemplateModel.createdBy;
|
||||
}
|
||||
|
||||
get dateAdded() {
|
||||
return this.dossierTemplateModel.dateAdded;
|
||||
}
|
||||
|
||||
get dateModified() {
|
||||
return this.dossierTemplateModel.dateModified;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return this.dossierTemplateModel.description;
|
||||
}
|
||||
|
||||
get dossierTemplateId() {
|
||||
return this.dossierTemplateModel.dossierTemplateId;
|
||||
}
|
||||
|
||||
get downloadFileTypes() {
|
||||
return this.dossierTemplateModel.downloadFileTypes;
|
||||
}
|
||||
|
||||
get modifiedBy() {
|
||||
return this.dossierTemplateModel.modifiedBy;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.dossierTemplateModel.name;
|
||||
}
|
||||
|
||||
get reportTemplateIds() {
|
||||
return this.dossierTemplateModel.reportTemplateIds;
|
||||
}
|
||||
|
||||
get validFrom() {
|
||||
return this.dossierTemplateModel.validFrom;
|
||||
}
|
||||
|
||||
get validTo() {
|
||||
return this.dossierTemplateModel.validTo;
|
||||
}
|
||||
}
|
||||
44
apps/red-ui/src/app/models/file/dossier-template.ts
Normal file
44
apps/red-ui/src/app/models/file/dossier-template.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { DownloadFileType, FileAttributesConfig, IDossierTemplate, List } from '@redaction/red-ui-http';
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
|
||||
export class DossierTemplate implements IDossierTemplate, IListable {
|
||||
readonly createdBy?: string;
|
||||
readonly dateAdded?: string;
|
||||
readonly dateModified?: string;
|
||||
readonly description?: string;
|
||||
readonly dossierTemplateId?: string;
|
||||
readonly downloadFileTypes?: List<DownloadFileType>;
|
||||
readonly modifiedBy?: string;
|
||||
readonly name?: string;
|
||||
readonly reportTemplateIds?: List;
|
||||
readonly validFrom?: string;
|
||||
readonly validTo?: string;
|
||||
dictionariesCount = 0;
|
||||
totalDictionaryEntries = 0;
|
||||
|
||||
constructor(dossierTemplate: IDossierTemplate, public fileAttributesConfig: FileAttributesConfig) {
|
||||
this.createdBy = dossierTemplate.createdBy;
|
||||
this.dateAdded = dossierTemplate.dateAdded;
|
||||
this.dateModified = dossierTemplate.dateModified;
|
||||
this.description = dossierTemplate.description;
|
||||
this.dossierTemplateId = dossierTemplate.dossierTemplateId;
|
||||
this.downloadFileTypes = dossierTemplate.downloadFileTypes;
|
||||
this.modifiedBy = dossierTemplate.modifiedBy;
|
||||
this.name = dossierTemplate.name;
|
||||
this.reportTemplateIds = dossierTemplate.reportTemplateIds;
|
||||
this.validFrom = dossierTemplate.validFrom;
|
||||
this.validTo = dossierTemplate.validTo;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.dossierTemplateId;
|
||||
}
|
||||
|
||||
get searchKey(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
get routerLink(): string {
|
||||
return `/main/admin/dossier-templates/${this.dossierTemplateId}/dictionaries`;
|
||||
}
|
||||
}
|
||||
30
apps/red-ui/src/app/models/file/file-attribute-config.ts
Normal file
30
apps/red-ui/src/app/models/file/file-attribute-config.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { FileAttributeConfigType, IFileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
|
||||
export class FileAttributeConfig implements IFileAttributeConfig, IListable {
|
||||
readonly id: string;
|
||||
readonly csvColumnHeader?: string;
|
||||
readonly editable?: boolean;
|
||||
readonly label?: string;
|
||||
readonly placeholder?: string;
|
||||
readonly primaryAttribute?: boolean;
|
||||
readonly displayedInFileList?: boolean;
|
||||
readonly filterable?: boolean;
|
||||
readonly type?: FileAttributeConfigType;
|
||||
|
||||
constructor(fileAttributeConfig: IFileAttributeConfig) {
|
||||
this.id = fileAttributeConfig.id;
|
||||
this.csvColumnHeader = fileAttributeConfig.csvColumnHeader;
|
||||
this.editable = !!fileAttributeConfig.editable;
|
||||
this.label = fileAttributeConfig.label;
|
||||
this.placeholder = fileAttributeConfig.placeholder;
|
||||
this.primaryAttribute = fileAttributeConfig.primaryAttribute;
|
||||
this.displayedInFileList = fileAttributeConfig.displayedInFileList;
|
||||
this.filterable = !!fileAttributeConfig.filterable;
|
||||
this.type = fileAttributeConfig.type;
|
||||
}
|
||||
|
||||
get searchKey(): string {
|
||||
return this.label;
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import { RedactionChangeLog, RedactionLog, ViewedPages } from '@redaction/red-ui-http';
|
||||
import { FileStatusWrapper } from './file-status.wrapper';
|
||||
import { UserWrapper } from '@services/user.service';
|
||||
import { File } from './file';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||
import { ViewMode } from './view-mode';
|
||||
import { TypeValueWrapper } from './type-value.wrapper';
|
||||
import { TypeValue } from './type-value';
|
||||
import { User } from '@models/user';
|
||||
|
||||
export class AnnotationData {
|
||||
visibleAnnotations: AnnotationWrapper[];
|
||||
@ -13,7 +13,7 @@ export class AnnotationData {
|
||||
|
||||
export class FileDataModel {
|
||||
constructor(
|
||||
public fileStatus: FileStatusWrapper,
|
||||
public file: File,
|
||||
public fileData: Blob,
|
||||
public redactionLog: RedactionLog,
|
||||
public redactionChangeLog: RedactionChangeLog,
|
||||
@ -21,15 +21,15 @@ export class FileDataModel {
|
||||
) {}
|
||||
|
||||
getAnnotations(
|
||||
dictionaryData: { [p: string]: TypeValueWrapper },
|
||||
currentUser: UserWrapper,
|
||||
dictionaryData: { [p: string]: TypeValue },
|
||||
currentUser: User,
|
||||
viewMode: ViewMode,
|
||||
areDevFeaturesEnabled: boolean
|
||||
): AnnotationData {
|
||||
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
||||
let allAnnotations = entries
|
||||
.map(entry => AnnotationWrapper.fromData(entry))
|
||||
.filter(ann => !this.fileStatus.excludedPages.includes(ann.pageNumber));
|
||||
.filter(ann => !this.file.excludedPages.includes(ann.pageNumber));
|
||||
|
||||
if (!areDevFeaturesEnabled) {
|
||||
allAnnotations = allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
import { Listable } from '@iqser/common-ui';
|
||||
import { FileAttributesConfig, FileStatus } from '@redaction/red-ui-http';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
|
||||
const processingStatuses = [
|
||||
FileStatus.StatusEnum.REPROCESS,
|
||||
FileStatus.StatusEnum.FULLREPROCESS,
|
||||
FileStatus.StatusEnum.OCRPROCESSING,
|
||||
FileStatus.StatusEnum.INDEXING,
|
||||
FileStatus.StatusEnum.PROCESSING
|
||||
] as const;
|
||||
|
||||
export class FileStatusWrapper implements FileStatus, Listable {
|
||||
readonly added = this.fileStatus.added;
|
||||
readonly allManualRedactionsApplied = this.fileStatus.allManualRedactionsApplied;
|
||||
readonly analysisDuration = this.fileStatus.analysisDuration;
|
||||
readonly analysisRequired = this.fileStatus.analysisRequired && !this.fileStatus.excluded;
|
||||
readonly approvalDate = this.fileStatus.approvalDate;
|
||||
readonly currentReviewer = this.fileStatus.currentReviewer;
|
||||
readonly dictionaryVersion = this.fileStatus.dictionaryVersion;
|
||||
readonly dossierDictionaryVersion = this.fileStatus.dossierDictionaryVersion;
|
||||
readonly dossierId = this.fileStatus.dossierId;
|
||||
readonly excluded = this.fileStatus.excluded;
|
||||
readonly fileAttributes = this.fileStatus.fileAttributes;
|
||||
readonly fileId = this.fileStatus.fileId;
|
||||
readonly filename = this.fileStatus.filename;
|
||||
readonly hasAnnotationComments = this.fileStatus.hasAnnotationComments;
|
||||
readonly hasHints = this.fileStatus.hasHints;
|
||||
readonly hasImages = this.fileStatus.hasImages;
|
||||
readonly hasRedactions = this.fileStatus.hasRedactions;
|
||||
readonly hasUpdates = this.fileStatus.hasUpdates;
|
||||
readonly lastOCRTime = this.fileStatus.lastOCRTime;
|
||||
readonly lastProcessed = this.fileStatus.lastProcessed;
|
||||
readonly lastReviewer = this.fileStatus.lastReviewer;
|
||||
readonly lastUpdated = this.fileStatus.lastUpdated;
|
||||
readonly lastUploaded = this.fileStatus.lastUploaded;
|
||||
readonly legalBasisVersion = this.fileStatus.legalBasisVersion;
|
||||
readonly numberOfAnalyses = this.fileStatus.numberOfAnalyses;
|
||||
readonly numberOfPages = this.fileStatus.numberOfPages;
|
||||
readonly rulesVersion = this.fileStatus.rulesVersion;
|
||||
readonly status = this._status;
|
||||
readonly uploader = this.fileStatus.uploader;
|
||||
readonly excludedPages = this.fileStatus.excludedPages;
|
||||
readonly hasSuggestions = this.fileStatus.hasSuggestions;
|
||||
readonly dossierTemplateId = this.fileStatus.dossierTemplateId;
|
||||
|
||||
primaryAttribute: string;
|
||||
lastOpened: boolean;
|
||||
|
||||
constructor(readonly fileStatus: FileStatus, public reviewerName: string, fileAttributesConfig?: FileAttributesConfig) {
|
||||
if (fileAttributesConfig) {
|
||||
const primary = fileAttributesConfig.fileAttributeConfigs?.find(c => c.primaryAttribute);
|
||||
if (primary && fileStatus.fileAttributes?.attributeIdToValue) {
|
||||
this.primaryAttribute = fileStatus.fileAttributes?.attributeIdToValue[primary.id];
|
||||
}
|
||||
|
||||
if (!this.primaryAttribute) {
|
||||
// Fallback here
|
||||
this.primaryAttribute = '-';
|
||||
}
|
||||
}
|
||||
if (!this.fileAttributes || !this.fileAttributes.attributeIdToValue) {
|
||||
this.fileAttributes = { attributeIdToValue: {} };
|
||||
}
|
||||
}
|
||||
|
||||
readonly excludedPagesCount = this.excludedPages?.length ?? 0;
|
||||
readonly statusSort = StatusSorter[this.status];
|
||||
readonly pages = this._pages;
|
||||
readonly cacheIdentifier = btoa(this.lastUploaded + this.lastOCRTime);
|
||||
|
||||
readonly hintsOnly = this.hasHints && !this.hasRedactions;
|
||||
readonly hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
|
||||
|
||||
readonly isUnassigned = !this.currentReviewer;
|
||||
readonly isError = this.status === FileStatus.StatusEnum.ERROR;
|
||||
readonly isProcessing = processingStatuses.includes(this.status);
|
||||
readonly isApproved = this.status === FileStatus.StatusEnum.APPROVED;
|
||||
readonly isPending = this.status === FileStatus.StatusEnum.UNPROCESSED;
|
||||
readonly isUnderReview = this.status === FileStatus.StatusEnum.UNDERREVIEW;
|
||||
readonly isUnderApproval = this.status === FileStatus.StatusEnum.UNDERAPPROVAL;
|
||||
readonly canBeApproved = !this.analysisRequired && !this.hasSuggestions;
|
||||
readonly canBeOpened = !this.isError && !this.isPending;
|
||||
readonly isWorkable = !this.isProcessing && this.canBeOpened;
|
||||
readonly canBeOCRed = !this.excluded && !this.lastOCRTime && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(this.status);
|
||||
|
||||
get id() {
|
||||
return this.fileId;
|
||||
}
|
||||
|
||||
private get _pages() {
|
||||
if (this.fileStatus.status === 'ERROR') {
|
||||
return -1;
|
||||
}
|
||||
return this.fileStatus.numberOfPages ? this.fileStatus.numberOfPages : 0;
|
||||
}
|
||||
|
||||
private get _status(): FileStatus.StatusEnum {
|
||||
return this.fileStatus.status === FileStatus.StatusEnum.REPROCESS || this.fileStatus.status === FileStatus.StatusEnum.FULLREPROCESS
|
||||
? FileStatus.StatusEnum.PROCESSING
|
||||
: this.fileStatus.status;
|
||||
}
|
||||
}
|
||||
142
apps/red-ui/src/app/models/file/file.ts
Normal file
142
apps/red-ui/src/app/models/file/file.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
import { FileAttributes, FileAttributesConfig, FileStatus, FileStatuses, IFile, List } from '@redaction/red-ui-http';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
|
||||
const processingStatuses: List<FileStatus> = [
|
||||
FileStatuses.REPROCESS,
|
||||
FileStatuses.FULLREPROCESS,
|
||||
FileStatuses.OCR_PROCESSING,
|
||||
FileStatuses.INDEXING,
|
||||
FileStatuses.PROCESSING
|
||||
] as const;
|
||||
|
||||
export class File implements IFile, IListable {
|
||||
readonly added: string;
|
||||
readonly allManualRedactionsApplied: boolean;
|
||||
readonly analysisDuration: number;
|
||||
readonly analysisRequired: boolean;
|
||||
readonly approvalDate: string;
|
||||
readonly currentReviewer: string;
|
||||
readonly dictionaryVersion: number;
|
||||
readonly dossierDictionaryVersion: number;
|
||||
readonly dossierId: string;
|
||||
readonly excluded: boolean;
|
||||
readonly fileAttributes: FileAttributes;
|
||||
readonly fileId: string;
|
||||
readonly filename: string;
|
||||
readonly hasAnnotationComments: boolean;
|
||||
readonly hasHints: boolean;
|
||||
readonly hasImages: boolean;
|
||||
readonly hasRedactions: boolean;
|
||||
readonly hasUpdates: boolean;
|
||||
readonly lastOCRTime: string;
|
||||
readonly lastProcessed: string;
|
||||
readonly lastReviewer: string;
|
||||
readonly lastUpdated: string;
|
||||
readonly lastUploaded: string;
|
||||
readonly legalBasisVersion: number;
|
||||
readonly numberOfAnalyses: number;
|
||||
readonly numberOfPages: number;
|
||||
readonly rulesVersion: number;
|
||||
readonly status: FileStatus;
|
||||
readonly uploader: string;
|
||||
readonly excludedPages: number[];
|
||||
readonly hasSuggestions: boolean;
|
||||
readonly dossierTemplateId: string;
|
||||
|
||||
primaryAttribute: string;
|
||||
lastOpened: boolean;
|
||||
readonly statusSort: number;
|
||||
readonly cacheIdentifier: string;
|
||||
readonly hintsOnly: boolean;
|
||||
readonly hasNone: boolean;
|
||||
readonly isUnassigned: boolean;
|
||||
readonly isError: boolean;
|
||||
readonly isProcessing: boolean;
|
||||
readonly isApproved: boolean;
|
||||
readonly isPending: boolean;
|
||||
readonly isUnderReview: boolean;
|
||||
readonly isUnderApproval: boolean;
|
||||
readonly canBeApproved: boolean;
|
||||
readonly canBeOpened: boolean;
|
||||
readonly isWorkable: boolean;
|
||||
readonly canBeOCRed: boolean;
|
||||
|
||||
constructor(file: IFile, public reviewerName: string, fileAttributesConfig?: FileAttributesConfig) {
|
||||
this.added = file.added;
|
||||
this.allManualRedactionsApplied = file.allManualRedactionsApplied;
|
||||
this.analysisDuration = file.analysisDuration;
|
||||
this.analysisRequired = file.analysisRequired && !file.excluded;
|
||||
this.approvalDate = file.approvalDate;
|
||||
this.currentReviewer = file.currentReviewer;
|
||||
this.dictionaryVersion = file.dictionaryVersion;
|
||||
this.dossierDictionaryVersion = file.dossierDictionaryVersion;
|
||||
this.dossierId = file.dossierId;
|
||||
this.excluded = file.excluded;
|
||||
this.fileAttributes = file.fileAttributes;
|
||||
this.fileId = file.fileId;
|
||||
this.filename = file.filename;
|
||||
this.hasAnnotationComments = file.hasAnnotationComments;
|
||||
this.hasHints = file.hasHints;
|
||||
this.hasImages = file.hasImages;
|
||||
this.hasRedactions = file.hasRedactions;
|
||||
this.hasUpdates = file.hasUpdates;
|
||||
this.lastOCRTime = file.lastOCRTime;
|
||||
this.lastProcessed = file.lastProcessed;
|
||||
this.lastReviewer = file.lastReviewer;
|
||||
this.lastUpdated = file.lastUpdated;
|
||||
this.lastUploaded = file.lastUploaded;
|
||||
this.legalBasisVersion = file.legalBasisVersion;
|
||||
this.numberOfAnalyses = file.numberOfAnalyses;
|
||||
this.status = ['REPROCESS', 'FULLREPROCESS'].includes(file.status) ? FileStatuses.PROCESSING : file.status;
|
||||
this.isError = this.status === FileStatuses.ERROR;
|
||||
this.numberOfPages = this.isError ? -1 : file.numberOfPages ?? 0;
|
||||
this.rulesVersion = file.rulesVersion;
|
||||
this.uploader = file.uploader;
|
||||
this.excludedPages = file.excludedPages;
|
||||
this.hasSuggestions = file.hasSuggestions;
|
||||
this.dossierTemplateId = file.dossierTemplateId;
|
||||
|
||||
this.statusSort = StatusSorter[this.status];
|
||||
this.cacheIdentifier = btoa(this.lastUploaded + this.lastOCRTime);
|
||||
this.hintsOnly = this.hasHints && !this.hasRedactions;
|
||||
this.hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
|
||||
this.isUnassigned = !this.currentReviewer;
|
||||
this.isProcessing = processingStatuses.includes(this.status);
|
||||
this.isApproved = this.status === FileStatuses.APPROVED;
|
||||
this.isPending = this.status === FileStatuses.UNPROCESSED;
|
||||
this.isUnderReview = this.status === FileStatuses.UNDER_REVIEW;
|
||||
this.isUnderApproval = this.status === FileStatuses.UNDER_APPROVAL;
|
||||
this.canBeApproved = !this.analysisRequired && !this.hasSuggestions;
|
||||
this.canBeOpened = !this.isError && !this.isPending;
|
||||
this.isWorkable = !this.isProcessing && this.canBeOpened;
|
||||
this.canBeOCRed = !this.excluded && !this.lastOCRTime && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(this.status);
|
||||
|
||||
if (fileAttributesConfig) {
|
||||
const primary = fileAttributesConfig.fileAttributeConfigs?.find(c => c.primaryAttribute);
|
||||
if (primary && file.fileAttributes?.attributeIdToValue) {
|
||||
this.primaryAttribute = file.fileAttributes?.attributeIdToValue[primary.id];
|
||||
}
|
||||
|
||||
if (!this.primaryAttribute) {
|
||||
// Fallback here
|
||||
this.primaryAttribute = '-';
|
||||
}
|
||||
}
|
||||
if (!this.fileAttributes || !this.fileAttributes.attributeIdToValue) {
|
||||
this.fileAttributes = { attributeIdToValue: {} };
|
||||
}
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.fileId;
|
||||
}
|
||||
|
||||
get searchKey(): string {
|
||||
return this.filename;
|
||||
}
|
||||
|
||||
get routerLink(): string | undefined {
|
||||
return this.canBeOpened ? `/main/dossiers/${this.dossierId}/file/${this.fileId}` : undefined;
|
||||
}
|
||||
}
|
||||
41
apps/red-ui/src/app/models/file/type-value.ts
Normal file
41
apps/red-ui/src/app/models/file/type-value.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
import { ITypeValue } from '@redaction/red-ui-http';
|
||||
|
||||
export class TypeValue implements ITypeValue, IListable {
|
||||
readonly type: string;
|
||||
readonly addToDictionaryAction: boolean;
|
||||
readonly caseInsensitive: boolean;
|
||||
readonly description?: string;
|
||||
readonly dossierTemplateId?: string;
|
||||
readonly hexColor?: string;
|
||||
readonly label?: string;
|
||||
readonly hint: boolean;
|
||||
readonly rank?: number;
|
||||
readonly recommendation: boolean;
|
||||
entries: string[] = [];
|
||||
|
||||
constructor(typeValue: ITypeValue, readonly virtual = false) {
|
||||
this.type = typeValue.type;
|
||||
this.addToDictionaryAction = !!typeValue.addToDictionaryAction;
|
||||
this.caseInsensitive = !!typeValue.caseInsensitive;
|
||||
this.description = typeValue.description;
|
||||
this.dossierTemplateId = typeValue.dossierTemplateId;
|
||||
this.hexColor = typeValue.hexColor;
|
||||
this.hint = !!typeValue.hint;
|
||||
this.rank = typeValue.rank;
|
||||
this.recommendation = !!typeValue.recommendation;
|
||||
this.label = typeValue.label;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
get searchKey(): string {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
get routerLink(): string {
|
||||
return `/main/admin/dossier-templates/${this.dossierTemplateId}/dictionaries/${this.type}`;
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
import { Listable } from '@iqser/common-ui';
|
||||
import { TypeValue } from '@redaction/red-ui-http';
|
||||
|
||||
export class TypeValueWrapper implements Listable {
|
||||
entries: string[] = [];
|
||||
|
||||
constructor(public typeValue: TypeValue, public label?: string, public virtual?: boolean) {
|
||||
this.label = label || typeValue.label;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.typeValue.type;
|
||||
}
|
||||
|
||||
get addToDictionaryAction() {
|
||||
return this.typeValue.addToDictionaryAction;
|
||||
}
|
||||
|
||||
get caseInsensitive() {
|
||||
return this.typeValue.caseInsensitive;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return this.typeValue.description;
|
||||
}
|
||||
|
||||
get dossierTemplateId() {
|
||||
return this.typeValue.dossierTemplateId;
|
||||
}
|
||||
|
||||
get hexColor() {
|
||||
return this.typeValue.hexColor;
|
||||
}
|
||||
|
||||
get hint() {
|
||||
return this.typeValue.hint;
|
||||
}
|
||||
|
||||
get rank() {
|
||||
return this.typeValue.rank;
|
||||
}
|
||||
|
||||
get recommendation() {
|
||||
return this.typeValue.recommendation;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this.typeValue.type;
|
||||
}
|
||||
}
|
||||
28
apps/red-ui/src/app/models/user.ts
Normal file
28
apps/red-ui/src/app/models/user.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { IUser, List } from '@redaction/red-ui-http';
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
import { KeycloakProfile } from 'keycloak-js';
|
||||
|
||||
export class User implements IUser, IListable {
|
||||
readonly email: string;
|
||||
readonly username: string;
|
||||
readonly firstName: string;
|
||||
readonly lastName: string;
|
||||
readonly name: string;
|
||||
readonly searchKey: string;
|
||||
|
||||
readonly isActive = this.roles.length > 0;
|
||||
readonly isManager = this.roles.indexOf('RED_MANAGER') >= 0;
|
||||
readonly isUserAdmin = this.roles.indexOf('RED_USER_ADMIN') >= 0;
|
||||
readonly isUser = this.roles.indexOf('RED_USER') >= 0;
|
||||
readonly isAdmin = this.roles.indexOf('RED_ADMIN') >= 0;
|
||||
readonly hasAnyREDRoles = this.isUser || this.isManager || this.isAdmin || this.isUserAdmin;
|
||||
|
||||
constructor(user: KeycloakProfile | IUser, readonly roles: List, readonly id: string) {
|
||||
this.email = user.email;
|
||||
this.username = user.username || this.email;
|
||||
this.firstName = user.firstName;
|
||||
this.lastName = user.lastName;
|
||||
this.name = this.firstName && this.lastName ? `${this.firstName} ${this.lastName}` : this.username;
|
||||
this.searchKey = this.name + this.username + this.email;
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,15 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
|
||||
import { ITypeValue } from '@redaction/red-ui-http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { TypeValue } from '@models/file/type-value';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { toKebabCase } from '@utils/functions';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-dictionary-dialog',
|
||||
@ -17,19 +18,19 @@ import { toKebabCase } from '@utils/functions';
|
||||
})
|
||||
export class AddEditDictionaryDialogComponent {
|
||||
dictionaryForm: FormGroup;
|
||||
readonly dictionary: TypeValueWrapper;
|
||||
readonly dictionary: TypeValue;
|
||||
technicalName = '';
|
||||
private readonly _dossierTemplateId: string;
|
||||
|
||||
constructor(
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
private readonly _data: { dictionary: TypeValueWrapper; dossierTemplateId: string }
|
||||
private readonly _data: { dictionary: TypeValue; dossierTemplateId: string }
|
||||
) {
|
||||
this.dictionary = _data.dictionary;
|
||||
this._dossierTemplateId = _data.dossierTemplateId;
|
||||
@ -81,17 +82,16 @@ export class AddEditDictionaryDialogComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
async saveDictionary() {
|
||||
const typeValue: TypeValue = this._formToObject();
|
||||
let observable: Observable<any>;
|
||||
async saveDictionary(): Promise<void> {
|
||||
const typeValue: ITypeValue = this._formToObject();
|
||||
let observable: Observable<unknown>;
|
||||
|
||||
if (this.dictionary) {
|
||||
// edit mode
|
||||
observable = this._dictionaryControllerService.updateType(typeValue, this._dossierTemplateId, typeValue.type);
|
||||
observable = this._dictionaryService.updateType(typeValue, this._dossierTemplateId, typeValue.type);
|
||||
} else {
|
||||
// create mode
|
||||
typeValue.dossierTemplateId = this._dossierTemplateId;
|
||||
observable = this._dictionaryControllerService.addType(typeValue);
|
||||
observable = this._dictionaryService.addType({ ...typeValue, dossierTemplateId: this._dossierTemplateId });
|
||||
}
|
||||
|
||||
observable.subscribe(
|
||||
@ -120,7 +120,7 @@ export class AddEditDictionaryDialogComponent {
|
||||
this.technicalName = technicalName;
|
||||
}
|
||||
|
||||
private _formToObject(): TypeValue {
|
||||
private _formToObject(): ITypeValue {
|
||||
return {
|
||||
type: this.dictionary?.type || this.technicalName,
|
||||
label: this.dictionaryForm.get('label').value,
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { Component, Inject, OnDestroy } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { DossierAttributeConfig, FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { DossierAttributeConfigTypes, FileAttributeConfigTypes, IDossierAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AutoUnsubscribe, LoadingService } from '@iqser/common-ui';
|
||||
import { AutoUnsubscribe, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
|
||||
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -15,14 +14,9 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
})
|
||||
export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
dossierAttributeForm: FormGroup;
|
||||
dossierAttribute: DossierAttributeConfig;
|
||||
dossierAttribute: IDossierAttributeConfig;
|
||||
readonly translations = dossierAttributeTypesTranslations;
|
||||
readonly typeOptions = [
|
||||
DossierAttributeConfig.TypeEnum.TEXT,
|
||||
DossierAttributeConfig.TypeEnum.NUMBER,
|
||||
DossierAttributeConfig.TypeEnum.DATE,
|
||||
DossierAttributeConfig.TypeEnum.IMAGE
|
||||
];
|
||||
readonly typeOptions = Object.keys(DossierAttributeConfigTypes);
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
@ -31,7 +25,7 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
|
||||
private readonly _toaster: Toaster,
|
||||
readonly dialogRef: MatDialogRef<AddEditDossierAttributeDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
readonly data: { readonly dossierAttribute: DossierAttributeConfig }
|
||||
readonly data: { readonly dossierAttribute: IDossierAttributeConfig }
|
||||
) {
|
||||
super();
|
||||
this.dossierAttribute = data.dossierAttribute;
|
||||
@ -44,7 +38,7 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
|
||||
disabled: true
|
||||
}
|
||||
}),
|
||||
type: [this.dossierAttribute?.type || FileAttributeConfig.TypeEnum.TEXT, Validators.required]
|
||||
type: [this.dossierAttribute?.type || FileAttributeConfigTypes.TEXT, Validators.required]
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,7 +59,7 @@ export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribe impl
|
||||
saveFileAttribute() {
|
||||
this._loadingService.start();
|
||||
|
||||
const attribute: DossierAttributeConfig = {
|
||||
const attribute: IDossierAttributeConfig = {
|
||||
id: this.dossierAttribute?.id,
|
||||
editable: true,
|
||||
...this.dossierAttributeForm.getRawValue()
|
||||
|
||||
@ -5,9 +5,9 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import * as moment from 'moment';
|
||||
import { Moment } from 'moment';
|
||||
import {
|
||||
Dossier,
|
||||
DossierTemplateControllerService,
|
||||
DossierTemplateModel,
|
||||
DownloadFileType,
|
||||
IDossierTemplate,
|
||||
ReportTemplate,
|
||||
ReportTemplateControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
@ -23,8 +23,8 @@ export class AddEditDossierTemplateDialogComponent implements OnInit {
|
||||
dossierTemplateForm: FormGroup;
|
||||
hasValidFrom: boolean;
|
||||
hasValidTo: boolean;
|
||||
downloadTypesEnum: Dossier.DownloadFileTypesEnum[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
downloadTypes: { key: Dossier.DownloadFileTypesEnum; label: string }[] = this.downloadTypesEnum.map(type => ({
|
||||
downloadTypesEnum: DownloadFileType[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
downloadTypes: { key: DownloadFileType; label: string }[] = this.downloadTypesEnum.map(type => ({
|
||||
key: type,
|
||||
label: downloadTypesTranslations[type]
|
||||
}));
|
||||
@ -39,7 +39,7 @@ export class AddEditDossierTemplateDialogComponent implements OnInit {
|
||||
private readonly _dossierTemplateController: DossierTemplateControllerService,
|
||||
private readonly _reportTemplateController: ReportTemplateControllerService,
|
||||
public dialogRef: MatDialogRef<AddEditDossierTemplateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public dossierTemplate: DossierTemplateModel
|
||||
@Inject(MAT_DIALOG_DATA) readonly dossierTemplate: IDossierTemplate
|
||||
) {
|
||||
this.dossierTemplateForm = this._formBuilder.group({
|
||||
name: [this.dossierTemplate?.name, Validators.required],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { FileAttributeConfigTypes, IFileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
|
||||
|
||||
@ -12,9 +12,9 @@ import { fileAttributeTypesTranslations } from '../../translations/file-attribut
|
||||
})
|
||||
export class AddEditFileAttributeDialogComponent {
|
||||
fileAttributeForm: FormGroup;
|
||||
fileAttribute: FileAttributeConfig;
|
||||
fileAttribute: IFileAttributeConfig;
|
||||
dossierTemplateId: string;
|
||||
readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
|
||||
readonly typeOptions = Object.keys(FileAttributeConfigTypes);
|
||||
translations = fileAttributeTypesTranslations;
|
||||
|
||||
constructor(
|
||||
@ -22,7 +22,7 @@ export class AddEditFileAttributeDialogComponent {
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
public dialogRef: MatDialogRef<AddEditFileAttributeDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public data: { fileAttribute: FileAttributeConfig; dossierTemplateId: string }
|
||||
public data: { fileAttribute: IFileAttributeConfig; dossierTemplateId: string }
|
||||
) {
|
||||
this.fileAttribute = data.fileAttribute;
|
||||
this.dossierTemplateId = data.dossierTemplateId;
|
||||
@ -30,7 +30,7 @@ export class AddEditFileAttributeDialogComponent {
|
||||
this.fileAttributeForm = this._formBuilder.group({
|
||||
label: [this.fileAttribute?.label, Validators.required],
|
||||
csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required],
|
||||
type: [this.fileAttribute?.type || FileAttributeConfig.TypeEnum.TEXT, Validators.required],
|
||||
type: [this.fileAttribute?.type || FileAttributeConfigTypes.TEXT, Validators.required],
|
||||
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
|
||||
primaryAttribute: [this.fileAttribute?.primaryAttribute],
|
||||
filterable: [this.fileAttribute?.filterable],
|
||||
@ -57,7 +57,7 @@ export class AddEditFileAttributeDialogComponent {
|
||||
}
|
||||
|
||||
async saveFileAttribute() {
|
||||
const fileAttribute: FileAttributeConfig = {
|
||||
const fileAttribute: IFileAttributeConfig = {
|
||||
id: this.fileAttribute?.id,
|
||||
editable: !this.fileAttributeForm.get('readonly').value,
|
||||
...this.fileAttributeForm.getRawValue()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { UserWrapper } from '@services/user.service';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-user-dialog',
|
||||
@ -10,7 +10,7 @@ import { UserWrapper } from '@services/user.service';
|
||||
export class AddEditUserDialogComponent {
|
||||
resettingPassword = false;
|
||||
|
||||
constructor(readonly dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: UserWrapper) {}
|
||||
constructor(readonly dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: User) {}
|
||||
|
||||
toggleResetPassword() {
|
||||
this.resettingPassword = !this.resettingPassword;
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
import { UserControllerService } from '@redaction/red-ui-http';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { LoadingService } from '@iqser/common-ui';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-reset-password',
|
||||
@ -13,8 +14,8 @@ export class ResetPasswordComponent {
|
||||
readonly passwordForm = this._formBuilder.group({
|
||||
temporaryPassword: [null, Validators.required]
|
||||
});
|
||||
@Input() user: UserWrapper;
|
||||
@Output() toggleResetPassword = new EventEmitter();
|
||||
@Input() user: User;
|
||||
@Output() readonly toggleResetPassword = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
|
||||
@ -4,8 +4,8 @@ import { UserControllerService } from '@redaction/red-ui-http';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
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';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-details',
|
||||
@ -15,9 +15,10 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
export class UserDetailsComponent implements OnInit {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
|
||||
@Input() user: UserWrapper;
|
||||
@Output() toggleResetPassword = new EventEmitter();
|
||||
@Output() closeDialog = new EventEmitter<any>();
|
||||
@Input() user: User;
|
||||
@Output() readonly toggleResetPassword = new EventEmitter();
|
||||
@Output() readonly closeDialog = new EventEmitter();
|
||||
|
||||
userForm: FormGroup;
|
||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||
readonly translations = rolesTranslations;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { IFileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@ -9,7 +9,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
styleUrls: ['./confirm-delete-file-attribute-dialog.component.scss']
|
||||
})
|
||||
export class ConfirmDeleteFileAttributeDialogComponent {
|
||||
fileAttribute: FileAttributeConfig;
|
||||
fileAttribute: IFileAttributeConfig;
|
||||
checkboxes = [
|
||||
{ value: false, label: _('confirm-delete-file-attribute.impacted-documents') },
|
||||
{ value: false, label: _('confirm-delete-file-attribute.lost-details') }
|
||||
@ -18,7 +18,7 @@ export class ConfirmDeleteFileAttributeDialogComponent {
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ConfirmDeleteFileAttributeDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: FileAttributeConfig
|
||||
@Inject(MAT_DIALOG_DATA) public data: IFileAttributeConfig
|
||||
) {
|
||||
this.fileAttribute = data;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { UserControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { LoadingService } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserWrapper } from '@services/user.service';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-confirm-delete-users-dialog',
|
||||
@ -24,7 +24,7 @@ export class ConfirmDeleteUsersDialogComponent {
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _userControllerService: UserControllerService,
|
||||
readonly dialogRef: MatDialogRef<ConfirmDeleteUsersDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly users: UserWrapper[]
|
||||
@Inject(MAT_DIALOG_DATA) readonly users: User[]
|
||||
) {
|
||||
this.dossiersCount = this._appStateService.allDossiers.filter(dw => {
|
||||
for (const user of this.users) {
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { Colors } from '@redaction/red-ui-http';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DefaultColorType } from '@models/default-color-key.model';
|
||||
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-edit-color-dialog',
|
||||
@ -23,7 +24,7 @@ export class EditColorDialogComponent {
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dialogRef: MatDialogRef<EditColorDialogComponent>,
|
||||
@ -51,7 +52,7 @@ export class EditColorDialogComponent {
|
||||
};
|
||||
|
||||
try {
|
||||
await this._dictionaryControllerService.setColors(colors, this._dossierTemplateId).toPromise();
|
||||
await this._dictionaryService.setColors(colors, this._dossierTemplateId).toPromise();
|
||||
this._dialogRef.close(true);
|
||||
const color = this._translateService.instant(defaultColorsTranslations[this.colorKey]);
|
||||
this._toaster.info(_('edit-color-dialog.success'), { params: { color: color } });
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { Field } from '../file-attributes-csv-import-dialog.component';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { FileAttributeConfigTypes } from '@redaction/red-ui-http';
|
||||
import { CircleButtonTypes, DefaultListingServices, ListingComponent, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { fileAttributeTypesTranslations } from '../../../translations/file-attribute-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -28,20 +28,15 @@ export class ActiveFieldsListingComponent extends ListingComponent<Field> implem
|
||||
readonly translations = fileAttributeTypesTranslations;
|
||||
readonly tableHeaderLabel = _('file-attributes-csv-import.table-header.title');
|
||||
tableColumnConfigs: TableColumnConfig<Field>[];
|
||||
readonly typeOptions = [
|
||||
FileAttributeConfig.TypeEnum.TEXT,
|
||||
FileAttributeConfig.TypeEnum.NUMBER,
|
||||
FileAttributeConfig.TypeEnum.DATE
|
||||
] as const;
|
||||
readonly typeOptions = Object.keys(FileAttributeConfigTypes);
|
||||
@ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef<never>;
|
||||
@ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef<never>;
|
||||
@ViewChild('readonlyTemplate', { static: true }) readonlyTemplate: TemplateRef<never>;
|
||||
@ViewChild('primaryTemplate', { static: true }) primaryTemplate: TemplateRef<never>;
|
||||
@Input() entities: Field[];
|
||||
@Output() entitiesChange = new EventEmitter<Field[]>();
|
||||
@Output() setHoveredColumn = new EventEmitter<string>();
|
||||
@Output() toggleFieldActive = new EventEmitter<Field>();
|
||||
protected readonly _primaryKey = 'csvColumn';
|
||||
@Output() readonly entitiesChange = new EventEmitter<Field[]>();
|
||||
@Output() readonly setHoveredColumn = new EventEmitter<string>();
|
||||
@Output() readonly toggleFieldActive = new EventEmitter<Field>();
|
||||
|
||||
constructor(protected readonly _injector: Injector) {
|
||||
super(_injector);
|
||||
|
||||
@ -100,7 +100,7 @@
|
||||
(click)="toggleFieldActive(field)"
|
||||
(mouseenter)="setHoveredColumn(field.csvColumn)"
|
||||
(mouseleave)="setHoveredColumn()"
|
||||
*ngFor="let field of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
||||
*ngFor="let field of sortedDisplayedEntities$ | async"
|
||||
class="csv-header-pill-wrapper"
|
||||
>
|
||||
<div [class.selected]="isActive(field)" class="csv-header-pill">
|
||||
|
||||
@ -2,17 +2,23 @@ import { Component, Inject, Injector } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import * as Papa from 'papaparse';
|
||||
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
|
||||
import {
|
||||
FileAttributeConfigType,
|
||||
FileAttributeConfigTypes,
|
||||
FileAttributesConfig,
|
||||
FileAttributesControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { DefaultListingServices, Listable, ListingComponent, TableColumnConfig, Toaster } from '@iqser/common-ui';
|
||||
import { DefaultListingServices, IListable, ListingComponent, TableColumnConfig, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileAttributeConfig } from '@models/file/file-attribute-config';
|
||||
|
||||
export interface Field extends Listable {
|
||||
export interface Field extends IListable {
|
||||
id: string;
|
||||
csvColumn: string;
|
||||
name: string;
|
||||
type: FileAttributeConfig.TypeEnum;
|
||||
type: FileAttributeConfigType;
|
||||
readonly: boolean;
|
||||
primaryAttribute: boolean;
|
||||
}
|
||||
@ -35,7 +41,6 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<Fie
|
||||
columnSample = [];
|
||||
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
||||
readonly tableHeaderLabel = '';
|
||||
protected readonly _primaryKey = 'csvColumn';
|
||||
|
||||
constructor(
|
||||
private readonly _toaster: Toaster,
|
||||
@ -148,7 +153,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<Fie
|
||||
|
||||
toggleFieldActive(field: Field) {
|
||||
if (!this.isActive(field)) {
|
||||
this.activeFields = [...this.activeFields, field];
|
||||
this.activeFields = [...this.activeFields, { ...field, searchKey: field.csvColumn }];
|
||||
} else {
|
||||
this.activeFields.splice(this.activeFields.indexOf(field), 1);
|
||||
this.activeFields = [...this.activeFields];
|
||||
@ -156,7 +161,7 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<Fie
|
||||
}
|
||||
|
||||
activateAll() {
|
||||
this.activeFields = [...this.allEntities];
|
||||
this.activeFields = [...this.allEntities.map(item => ({ ...item, searchKey: item.csvColumn }))];
|
||||
}
|
||||
|
||||
deactivateAll() {
|
||||
@ -165,17 +170,15 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<Fie
|
||||
|
||||
async save() {
|
||||
const newPrimary = !!this.activeFields.find(attr => attr.primaryAttribute);
|
||||
|
||||
let fileAttributeConfigs = this.data.existingConfiguration.fileAttributeConfigs;
|
||||
if (newPrimary) {
|
||||
this.data.existingConfiguration.fileAttributeConfigs.forEach(attr => (attr.primaryAttribute = false));
|
||||
fileAttributeConfigs = fileAttributeConfigs.map(attr => new FileAttributeConfig({ ...attr, primaryAttribute: false }));
|
||||
}
|
||||
|
||||
const fileAttributes = {
|
||||
...this.baseConfigForm.getRawValue(),
|
||||
fileAttributeConfigs: [
|
||||
...this.data.existingConfiguration.fileAttributeConfigs.filter(
|
||||
a => !this.allEntities.find(entity => entity.csvColumn === a.csvColumnHeader)
|
||||
),
|
||||
...fileAttributeConfigs.filter(a => !this.allEntities.find(entity => entity.csvColumn === a.csvColumnHeader)),
|
||||
...this.activeFields.map(field => ({
|
||||
id: field.id,
|
||||
csvColumnHeader: field.csvColumn,
|
||||
@ -228,7 +231,8 @@ export class FileAttributesCsvImportDialogComponent extends ListingComponent<Fie
|
||||
id: btoa(csvColumn),
|
||||
csvColumn,
|
||||
name: csvColumn,
|
||||
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
|
||||
searchKey: csvColumn,
|
||||
type: isNumber ? FileAttributeConfigTypes.NUMBER : FileAttributeConfigTypes.TEXT,
|
||||
readonly: false,
|
||||
primaryAttribute: false
|
||||
};
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { AuditControllerService, AuditModel, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http';
|
||||
import { AuditControllerService, AuditResponse, AuditSearchRequest, IAudit } from '@redaction/red-ui-http';
|
||||
import { Moment } from 'moment';
|
||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||
import { DefaultListingServices, KeysOf, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { DefaultListingServices, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { auditCategoriesTranslations } from '../../translations/audit-categories-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AuditModelWrapper } from '../../../../models/audit-model-wrapper.model';
|
||||
import { Audit } from '@models/audit.model';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
@ -17,22 +17,21 @@ const PAGE_SIZE = 50;
|
||||
styleUrls: ['./audit-screen.component.scss'],
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => AuditScreenComponent) }]
|
||||
})
|
||||
export class AuditScreenComponent extends ListingComponent<AuditModelWrapper> implements OnDestroy, OnInit {
|
||||
export class AuditScreenComponent extends ListingComponent<Audit> implements OnDestroy, OnInit {
|
||||
readonly ALL_CATEGORIES = 'allCategories';
|
||||
readonly ALL_USERS = _('audit-screen.all-users');
|
||||
readonly translations = auditCategoriesTranslations;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
@ViewChild('messageTemplate', { static: true }) messageTemplate: TemplateRef<never>;
|
||||
@ViewChild('dateTemplate', { static: true }) dateTemplate: TemplateRef<never>;
|
||||
@ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef<never>;
|
||||
@ViewChild('categoryTemplate', { static: true }) categoryTemplate: TemplateRef<never>;
|
||||
@ViewChild('messageTemplate', { static: true }) messageTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('dateTemplate', { static: true }) dateTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('categoryTemplate', { static: true }) categoryTemplate: TemplateRef<unknown>;
|
||||
filterForm: FormGroup;
|
||||
categories: string[] = [];
|
||||
userIds: Set<string>;
|
||||
logs: AuditResponse;
|
||||
tableColumnConfigs: TableColumnConfig<AuditModelWrapper>[];
|
||||
tableColumnConfigs: TableColumnConfig<Audit>[];
|
||||
readonly tableHeaderLabel = _('audit-screen.table-header.title');
|
||||
protected readonly _primaryKey: KeysOf<AuditModelWrapper> = 'recordDate';
|
||||
private _previousFrom: Moment;
|
||||
private _previousTo: Moment;
|
||||
|
||||
@ -133,7 +132,7 @@ export class AuditScreenComponent extends ListingComponent<AuditModelWrapper> im
|
||||
this.categories = data[0].map(c => c.category);
|
||||
this.categories.splice(0, 0, this.ALL_CATEGORIES);
|
||||
this.logs = data[1];
|
||||
const entities = this.logs.data.map((log: AuditModel) => new AuditModelWrapper(log));
|
||||
const entities = this.logs.data.map((log: IAudit) => new Audit(log));
|
||||
this.entitiesService.setEntities(entities);
|
||||
this.userIds = new Set<string>([this.ALL_USERS]);
|
||||
for (const id of this.logs.data.map(log => log.userId).filter(uid => !!uid)) {
|
||||
|
||||
@ -1,15 +1,23 @@
|
||||
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { Colors } from '@redaction/red-ui-http';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { CircleButtonTypes, DefaultListingServices, Listable, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
DefaultListingServices,
|
||||
IListable,
|
||||
ListingComponent,
|
||||
LoadingService,
|
||||
TableColumnConfig
|
||||
} from '@iqser/common-ui';
|
||||
import { DefaultColorType } from '@models/default-color-key.model';
|
||||
import { defaultColorsTranslations } from '../../translations/default-colors-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
|
||||
interface ListItem extends Listable {
|
||||
interface ListItem extends IListable {
|
||||
readonly key: string;
|
||||
readonly value: string;
|
||||
}
|
||||
@ -21,14 +29,13 @@ interface ListItem extends Listable {
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DefaultColorsScreenComponent) }]
|
||||
})
|
||||
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> implements OnInit {
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<never>;
|
||||
@ViewChild('colorTemplate', { static: true }) colorTemplate: TemplateRef<never>;
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('colorTemplate', { static: true }) colorTemplate: TemplateRef<unknown>;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
readonly translations = defaultColorsTranslations;
|
||||
readonly tableHeaderLabel = _('default-colors-screen.table-header.title');
|
||||
tableColumnConfigs: TableColumnConfig<ListItem>[];
|
||||
protected readonly _primaryKey = 'key';
|
||||
private _colorsObj: Colors;
|
||||
|
||||
constructor(
|
||||
@ -38,7 +45,7 @@ export class DefaultColorsScreenComponent extends ListingComponent<ListItem> imp
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService
|
||||
private readonly _dictionaryService: DictionaryService
|
||||
) {
|
||||
super(_injector);
|
||||
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
|
||||
@ -69,7 +76,7 @@ export class DefaultColorsScreenComponent extends ListingComponent<ListItem> imp
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('default-colors-screen.table-col-names.key'),
|
||||
sortByKey: 'key',
|
||||
sortByKey: 'searchKey',
|
||||
template: this.nameTemplate,
|
||||
width: '2fr'
|
||||
},
|
||||
@ -83,11 +90,12 @@ export class DefaultColorsScreenComponent extends ListingComponent<ListItem> imp
|
||||
|
||||
private async _loadColors() {
|
||||
this._loadingService.start();
|
||||
const data = await this._dictionaryControllerService.getColors(this._appStateService.activeDossierTemplateId).toPromise();
|
||||
const data = await this._dictionaryService.getColors(this._appStateService.activeDossierTemplateId).toPromise();
|
||||
this._colorsObj = data;
|
||||
const entities = Object.keys(data).map(key => ({
|
||||
const entities: ListItem[] = Object.keys(data).map(key => ({
|
||||
id: key,
|
||||
key,
|
||||
searchKey: key,
|
||||
value: data[key]
|
||||
}));
|
||||
this.entitiesService.setEntities(entities);
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { catchError, defaultIfEmpty, tap } from 'rxjs/operators';
|
||||
import { forkJoin, of } from 'rxjs';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { TypeValue } from '@models/file/type-value';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
@ -18,8 +17,9 @@ import {
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
|
||||
const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
|
||||
const toChartConfig = (dict: TypeValue): DoughnutChartConfig => ({
|
||||
value: dict.entries?.length ?? 0,
|
||||
color: dict.hexColor,
|
||||
label: dict.label,
|
||||
@ -31,17 +31,16 @@ const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
|
||||
styleUrls: ['./dictionary-listing-screen.component.scss'],
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DictionaryListingScreenComponent) }]
|
||||
})
|
||||
export class DictionaryListingScreenComponent extends ListingComponent<TypeValueWrapper> implements OnInit {
|
||||
export class DictionaryListingScreenComponent extends ListingComponent<TypeValue> implements OnInit {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
readonly tableHeaderLabel = _('dictionary-listing.table-header.title');
|
||||
tableColumnConfigs: TableColumnConfig<TypeValueWrapper>[];
|
||||
tableColumnConfigs: TableColumnConfig<TypeValue>[];
|
||||
chartData: DoughnutChartConfig[] = [];
|
||||
@ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef<never>;
|
||||
@ViewChild('rankTemplate', { static: true }) rankTemplate: TemplateRef<never>;
|
||||
@ViewChild('iconTemplate', { static: true }) iconTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'type';
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
@ -51,15 +50,13 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService
|
||||
private readonly _dictionaryService: DictionaryService
|
||||
) {
|
||||
super(_injector);
|
||||
_loadingService.start();
|
||||
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
|
||||
}
|
||||
|
||||
routerLinkFn = (entity: TypeValueWrapper) => [entity.type];
|
||||
|
||||
ngOnInit(): void {
|
||||
this._configureTableColumns();
|
||||
this._loadDictionaryData();
|
||||
@ -68,7 +65,7 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
|
||||
openDeleteDictionariesDialog($event?: MouseEvent, types = this.entitiesService.selected) {
|
||||
this._dialogService.openDialog('confirm', $event, null, async () => {
|
||||
this._loadingService.start();
|
||||
await this._dictionaryControllerService
|
||||
await this._dictionaryService
|
||||
.deleteTypes(
|
||||
types.map(t => t.type),
|
||||
this._appStateService.activeDossierTemplateId
|
||||
@ -82,7 +79,7 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
|
||||
});
|
||||
}
|
||||
|
||||
openAddEditDictionaryDialog($event?: MouseEvent, dictionary?: TypeValueWrapper) {
|
||||
openAddEditDictionaryDialog($event?: MouseEvent, dictionary?: TypeValue) {
|
||||
this._dialogService.openDialog(
|
||||
'addEditDictionary',
|
||||
$event,
|
||||
@ -104,7 +101,7 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('dictionary-listing.table-col-names.type'),
|
||||
sortByKey: 'label',
|
||||
sortByKey: 'searchKey',
|
||||
width: '2fr',
|
||||
template: this.labelTemplate
|
||||
},
|
||||
@ -142,8 +139,8 @@ export class DictionaryListingScreenComponent extends ListingComponent<TypeValue
|
||||
}
|
||||
|
||||
const dataObs = this.allEntities.map(dict =>
|
||||
this._dictionaryControllerService.getDictionaryForType(this._appStateService.activeDossierTemplateId, dict.type).pipe(
|
||||
tap(values => (dict.entries = values.entries ?? [])),
|
||||
this._dictionaryService.getFor(this._appStateService.activeDossierTemplateId, dict.type).pipe(
|
||||
tap(values => (dict.entries = [...values.entries] ?? [])),
|
||||
catchError(() => {
|
||||
dict.entries = [];
|
||||
return of({});
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -7,8 +6,8 @@ import { saveAs } from 'file-saver';
|
||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
||||
import { DictionarySaveService } from '@shared/services/dictionary-save.service';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
import { TypeValue } from '@models/file/type-value';
|
||||
import { CircleButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||
import { UserService } from '@services/user.service';
|
||||
|
||||
@ -21,7 +20,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
entries: string[] = [];
|
||||
dictionary: TypeValueWrapper;
|
||||
dictionary: TypeValue;
|
||||
|
||||
@ViewChild('dictionaryManager', { static: false })
|
||||
private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||
@ -35,8 +34,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
protected readonly _translateService: TranslateService,
|
||||
private readonly _dictionarySaveService: DictionarySaveService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService
|
||||
private readonly _dictionaryService: DictionaryService
|
||||
) {
|
||||
super(_translateService);
|
||||
}
|
||||
@ -51,7 +49,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
this._activatedRoute.snapshot.params.dossierTemplateId
|
||||
);
|
||||
this.dictionary = this._appStateService.activeDictionary;
|
||||
this._loadEntries();
|
||||
await this._loadEntries();
|
||||
}
|
||||
|
||||
openEditDictionaryDialog($event: any) {
|
||||
@ -75,7 +73,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
$event?.stopPropagation();
|
||||
|
||||
this._dialogService.openDialog('confirm', $event, null, async () => {
|
||||
await this._dictionaryControllerService.deleteTypes([this.dictionary.type], this.dictionary.dossierTemplateId).toPromise();
|
||||
await this._dictionaryService.deleteTypes([this.dictionary.type], this.dictionary.dossierTemplateId).toPromise();
|
||||
await this._appStateService.loadDictionaryData();
|
||||
await this._router.navigate([
|
||||
'/main',
|
||||
@ -110,30 +108,34 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
|
||||
saveEntries(entries: string[]) {
|
||||
this._loadingService.start();
|
||||
this._dictionarySaveService
|
||||
.saveEntries(entries, this.entries, this.dictionary.dossierTemplateId, this.dictionary.type, null)
|
||||
.subscribe(
|
||||
() => {
|
||||
this._loadingService.stop();
|
||||
this._loadEntries();
|
||||
},
|
||||
() => {
|
||||
this._loadingService.stop();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadEntries() {
|
||||
this._loadingService.start();
|
||||
this._dictionaryControllerService.getDictionaryForType(this.dictionary.dossierTemplateId, this.dictionary.type).subscribe(
|
||||
data => {
|
||||
this._loadingService.stop();
|
||||
this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
||||
this._dictionaryService.saveEntries(entries, this.entries, this.dictionary.dossierTemplateId, this.dictionary.type, null).subscribe(
|
||||
async () => {
|
||||
await this._loadEntries();
|
||||
},
|
||||
() => {
|
||||
this._loadingService.stop();
|
||||
this.entries = [];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async _loadEntries() {
|
||||
this._loadingService.start();
|
||||
await this._dictionaryService
|
||||
.getFor(this.dictionary.dossierTemplateId, this.dictionary.type)
|
||||
.toPromise()
|
||||
.then(
|
||||
data => {
|
||||
this._loadingService.stop();
|
||||
this.entries = [...data.entries].sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
||||
},
|
||||
() => {
|
||||
this._loadingService.stop();
|
||||
this.entries = [];
|
||||
}
|
||||
)
|
||||
.catch(() => {
|
||||
this._loadingService.stop();
|
||||
this.entries = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
LoadingService,
|
||||
TableColumnConfig
|
||||
} from '@iqser/common-ui';
|
||||
import { DossierAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { IDossierAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
@ -15,6 +15,7 @@ import { DossierAttributesService } from '@shared/services/controller-wrappers/d
|
||||
import { dossierAttributeTypesTranslations } from '../../translations/dossier-attribute-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { DossierAttributeConfig } from '@state/model/dossier-attribute-config';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-attributes-listing-screen.component.html',
|
||||
@ -34,7 +35,6 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
||||
@ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef<never>;
|
||||
@ViewChild('placeholderTemplate', { static: true }) placeholderTemplate: TemplateRef<never>;
|
||||
@ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'label';
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
@ -54,7 +54,7 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: DossierAttributeConfig) {
|
||||
openConfirmDeleteAttributeDialog($event: MouseEvent, dossierAttribute?: IDossierAttributeConfig) {
|
||||
this._dialogService.openDialog('confirm', $event, null, async () => {
|
||||
this._loadingService.start();
|
||||
const ids = dossierAttribute ? [dossierAttribute.id] : this.entitiesService.selected.map(item => item.id);
|
||||
@ -64,7 +64,7 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
||||
});
|
||||
}
|
||||
|
||||
openAddEditAttributeDialog($event: MouseEvent, dossierAttribute?: DossierAttributeConfig) {
|
||||
openAddEditAttributeDialog($event: MouseEvent, dossierAttribute?: IDossierAttributeConfig) {
|
||||
const dossierTemplateId = this._appStateService.activeDossierTemplateId;
|
||||
|
||||
this._dialogService.openDialog(
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, Templ
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { DossierTemplateModelWrapper } from '@models/file/dossier-template-model.wrapper';
|
||||
import { DossierTemplate } from '@models/file/dossier-template';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
DefaultListingServices,
|
||||
@ -26,17 +26,16 @@ import { RouterHistoryService } from '@services/router-history.service';
|
||||
{ provide: ListingComponent, useExisting: forwardRef(() => DossierTemplatesListingScreenComponent) }
|
||||
]
|
||||
})
|
||||
export class DossierTemplatesListingScreenComponent extends ListingComponent<DossierTemplateModelWrapper> implements OnInit {
|
||||
export class DossierTemplatesListingScreenComponent extends ListingComponent<DossierTemplate> implements OnInit {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
readonly tableHeaderLabel = _('dossier-templates-listing.table-header.title');
|
||||
tableColumnConfigs: TableColumnConfig<DossierTemplateModelWrapper>[];
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<never>;
|
||||
@ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef<never>;
|
||||
@ViewChild('dateAddedTemplate', { static: true }) dateAddedTemplate: TemplateRef<never>;
|
||||
@ViewChild('dateModifiedTemplate', { static: true }) dateModifiedTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'name';
|
||||
tableColumnConfigs: TableColumnConfig<DossierTemplate>[];
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('userTemplate', { static: true }) userTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('dateAddedTemplate', { static: true }) dateAddedTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('dateModifiedTemplate', { static: true }) dateModifiedTemplate: TemplateRef<unknown>;
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
@ -52,8 +51,6 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
|
||||
super(_injector);
|
||||
}
|
||||
|
||||
routerLinkFn = (dossierTemplate: DossierTemplateModelWrapper) => [dossierTemplate.dossierTemplateId, 'dictionaries'];
|
||||
|
||||
ngOnInit(): void {
|
||||
this._configureTableColumns();
|
||||
this.loadDossierTemplatesData();
|
||||
@ -81,7 +78,7 @@ export class DossierTemplatesListingScreenComponent extends ListingComponent<Dos
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('dossier-templates-listing.table-col-names.name'),
|
||||
sortByKey: 'name',
|
||||
sortByKey: 'searchKey',
|
||||
template: this.nameTemplate
|
||||
},
|
||||
{
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
TemplateRef,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
|
||||
import { FileAttributesConfig, FileAttributesControllerService, IFileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
@ -24,6 +24,7 @@ import {
|
||||
import { fileAttributeTypesTranslations } from '../../translations/file-attribute-types-translations';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileAttributeConfig } from '@models/file/file-attribute-config';
|
||||
|
||||
@Component({
|
||||
templateUrl: './file-attributes-listing-screen.component.html',
|
||||
@ -41,14 +42,13 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
readonly translations = fileAttributeTypesTranslations;
|
||||
readonly tableHeaderLabel = _('file-attributes-listing.table-header.title');
|
||||
tableColumnConfigs: TableColumnConfig<FileAttributeConfig>[];
|
||||
@ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef<never>;
|
||||
@ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef<never>;
|
||||
@ViewChild('readonlyTemplate', { static: true }) readonlyTemplate: TemplateRef<never>;
|
||||
@ViewChild('csvColumnHeaderTemplate', { static: true }) csvColumnHeaderTemplate: TemplateRef<never>;
|
||||
@ViewChild('filterableTemplate', { static: true }) filterableTemplate: TemplateRef<never>;
|
||||
@ViewChild('displayedInFileListTemplate', { static: true }) displayedInFileListTemplate: TemplateRef<never>;
|
||||
@ViewChild('primaryAttributeTemplate', { static: true }) primaryAttributeTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'label';
|
||||
@ViewChild('labelTemplate', { static: true }) labelTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('typeTemplate', { static: true }) typeTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('readonlyTemplate', { static: true }) readonlyTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('csvColumnHeaderTemplate', { static: true }) csvColumnHeaderTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('filterableTemplate', { static: true }) filterableTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('displayedInFileListTemplate', { static: true }) displayedInFileListTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('primaryAttributeTemplate', { static: true }) primaryAttributeTemplate: TemplateRef<unknown>;
|
||||
private _existingConfiguration: FileAttributesConfig;
|
||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||
|
||||
@ -70,7 +70,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: IFileAttributeConfig) {
|
||||
this._dialogService.openDialog(
|
||||
'addEditFileAttribute',
|
||||
$event,
|
||||
@ -86,7 +86,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
);
|
||||
}
|
||||
|
||||
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: IFileAttributeConfig) {
|
||||
this._dialogService.openDialog('deleteFileAttribute', $event, fileAttribute, async () => {
|
||||
this._loadingService.start();
|
||||
if (fileAttribute) {
|
||||
@ -126,7 +126,7 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('file-attributes-listing.table-col-names.name'),
|
||||
sortByKey: 'label',
|
||||
sortByKey: 'searchKey',
|
||||
width: '2fr',
|
||||
template: this.labelTemplate
|
||||
},
|
||||
@ -170,7 +170,8 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
.getFileAttributesConfiguration(this._appStateService.activeDossierTemplateId)
|
||||
.toPromise();
|
||||
this._existingConfiguration = response;
|
||||
this.entitiesService.setEntities(response?.fileAttributeConfigs || []);
|
||||
const fileAttributeConfig = response?.fileAttributeConfigs.map(item => new FileAttributeConfig(item)) || [];
|
||||
this.entitiesService.setEntities(fileAttributeConfig);
|
||||
} catch (e) {}
|
||||
|
||||
this._loadingService.stop();
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { Dossier } from '@redaction/red-ui-http';
|
||||
import { IDossier } from '@redaction/red-ui-http';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
DefaultListingServices,
|
||||
Listable,
|
||||
IListable,
|
||||
ListingComponent,
|
||||
LoadingService,
|
||||
SortingOrders,
|
||||
@ -20,7 +20,7 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { getLeftDateTime } from '@utils/functions';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
|
||||
interface DossierListItem extends Dossier, Listable {
|
||||
interface DossierListItem extends IDossier, IListable {
|
||||
readonly canRestore: boolean;
|
||||
readonly restoreDate: string;
|
||||
}
|
||||
@ -29,11 +29,7 @@ interface DossierListItem extends Dossier, Listable {
|
||||
templateUrl: './trash-screen.component.html',
|
||||
styleUrls: ['./trash-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
...DefaultListingServices,
|
||||
DossiersService,
|
||||
{ provide: ListingComponent, useExisting: forwardRef(() => TrashScreenComponent) }
|
||||
]
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => TrashScreenComponent) }]
|
||||
})
|
||||
export class TrashScreenComponent extends ListingComponent<DossierListItem> implements OnInit {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
@ -44,8 +40,6 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
@ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef<never>;
|
||||
@ViewChild('deletedTimeTemplate', { static: true }) deletedTimeTemplate: TemplateRef<never>;
|
||||
@ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'dossierName';
|
||||
private readonly _deleteRetentionHours = this._configService.values.DELETE_RETENTION_HOURS;
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
@ -78,7 +72,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
hardDelete(dossiers = this.entitiesService.selected) {
|
||||
hardDelete(dossiers = this.entitiesService.selected): void {
|
||||
const data = new ConfirmationDialogInput({
|
||||
title: _('confirmation-dialog.delete-dossier.title'),
|
||||
titleColor: TitleColors.PRIMARY,
|
||||
@ -97,7 +91,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
});
|
||||
}
|
||||
|
||||
restore(dossiers = this.entitiesService.selected) {
|
||||
restore(dossiers = this.entitiesService.selected): void {
|
||||
this._loadingService.loadWhile(this._restore(dossiers));
|
||||
}
|
||||
|
||||
@ -105,7 +99,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('trash.table-col-names.name'),
|
||||
sortByKey: 'dossierName',
|
||||
sortByKey: 'searchKey',
|
||||
template: this.filenameTemplate
|
||||
},
|
||||
{
|
||||
@ -127,7 +121,7 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
}
|
||||
|
||||
private _getRestoreDate(softDeletedTime: string): string {
|
||||
return moment(softDeletedTime).add(this._deleteRetentionHours, 'hours').toISOString();
|
||||
return moment(softDeletedTime).add(this._configService.values.DELETE_RETENTION_HOURS, 'hours').toISOString();
|
||||
}
|
||||
|
||||
private async _loadDossiersData(): Promise<void> {
|
||||
@ -140,15 +134,16 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft > 0;
|
||||
}
|
||||
|
||||
private _toListItems(dossiers: Dossier[]): DossierListItem[] {
|
||||
private _toListItems(dossiers: IDossier[]): DossierListItem[] {
|
||||
return dossiers.map(dossier => this._toListItem(dossier));
|
||||
}
|
||||
|
||||
private _toListItem(dossier: Dossier): DossierListItem {
|
||||
private _toListItem(dossier: IDossier): DossierListItem {
|
||||
const restoreDate = this._getRestoreDate(dossier.softDeletedTime);
|
||||
return {
|
||||
id: dossier.dossierId,
|
||||
...dossier,
|
||||
searchKey: dossier.dossierName,
|
||||
restoreDate,
|
||||
canRestore: this._canRestoreDossier(restoreDate),
|
||||
// Because of migrations, for some this is not set
|
||||
@ -157,19 +152,19 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
|
||||
}
|
||||
|
||||
private async _restore(dossiers: DossierListItem[]): Promise<void> {
|
||||
const dossierIds = dossiers.map(d => d.dossierId);
|
||||
const dossierIds = dossiers.map(d => d.id);
|
||||
await this._dossiersService.restore(dossierIds);
|
||||
this._removeFromList(dossierIds);
|
||||
}
|
||||
|
||||
private async _hardDelete(dossiers: DossierListItem[]) {
|
||||
const dossierIds = dossiers.map(d => d.dossierId);
|
||||
const dossierIds = dossiers.map(d => d.id);
|
||||
await this._dossiersService.hardDelete(dossierIds);
|
||||
this._removeFromList(dossierIds);
|
||||
}
|
||||
|
||||
private _removeFromList(ids: string[]): void {
|
||||
const entities = this.entitiesService.all.filter(e => !ids.includes(e.dossierId));
|
||||
const entities = this.entitiesService.all.filter(e => !ids.includes(e.id));
|
||||
this.entitiesService.setEntities(entities);
|
||||
this.entitiesService.setSelected([]);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, forwardRef, Injector, OnInit, QueryList, TemplateRef, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { UserControllerService } from '@redaction/red-ui-http';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -18,27 +18,27 @@ import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { rolesTranslations } from '../../../../translations/roles-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
templateUrl: './user-listing-screen.component.html',
|
||||
styleUrls: ['./user-listing-screen.component.scss'],
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => UserListingScreenComponent) }]
|
||||
})
|
||||
export class UserListingScreenComponent extends ListingComponent<UserWrapper> implements OnInit {
|
||||
export class UserListingScreenComponent extends ListingComponent<User> implements OnInit {
|
||||
readonly translations = rolesTranslations;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this.userService.currentUser;
|
||||
readonly canDeleteSelected$ = this._canDeleteSelected$;
|
||||
readonly tableHeaderLabel = _('user-listing.table-header.title');
|
||||
tableColumnConfigs: TableColumnConfig<UserWrapper>[];
|
||||
tableColumnConfigs: TableColumnConfig<User>[];
|
||||
collapsedDetails = false;
|
||||
chartData: DoughnutChartConfig[] = [];
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<never>;
|
||||
@ViewChild('emailTemplate', { static: true }) emailTemplate: TemplateRef<never>;
|
||||
@ViewChild('activeTemplate', { static: true }) activeTemplate: TemplateRef<never>;
|
||||
@ViewChild('rolesTemplate', { static: true }) rolesTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'id';
|
||||
@ViewChildren(InitialsAvatarComponent)
|
||||
private readonly _avatars: QueryList<InitialsAvatarComponent>;
|
||||
|
||||
@ -62,22 +62,21 @@ export class UserListingScreenComponent extends ListingComponent<UserWrapper> im
|
||||
async ngOnInit() {
|
||||
this._configureTableColumns();
|
||||
await this._loadData();
|
||||
this.searchService.setSearchKey('searchKey');
|
||||
}
|
||||
|
||||
openAddEditUserDialog($event: MouseEvent, user?: UserWrapper) {
|
||||
openAddEditUserDialog($event: MouseEvent, user?: User) {
|
||||
this._dialogService.openDialog('addEditUser', $event, user, async () => {
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteUsersDialog(users: UserWrapper[], $event?: MouseEvent) {
|
||||
openDeleteUsersDialog(users: User[], $event?: MouseEvent) {
|
||||
this._dialogService.openDialog('deleteUsers', $event, users, async () => {
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
getDisplayRoles(user: UserWrapper) {
|
||||
getDisplayRoles(user: User) {
|
||||
const separator = ', ';
|
||||
return (
|
||||
user.roles.map(role => this._translateService.instant(this.translations[role])).join(separator) ||
|
||||
@ -85,10 +84,10 @@ export class UserListingScreenComponent extends ListingComponent<UserWrapper> im
|
||||
);
|
||||
}
|
||||
|
||||
async toggleActive(user: UserWrapper) {
|
||||
async toggleActive(user: User) {
|
||||
this._loadingService.start();
|
||||
user.roles = user.isActive ? [] : ['RED_USER'];
|
||||
await this._userControllerService.updateProfile(user, user.id).toPromise();
|
||||
const requestBody = { ...user, roles: user.isActive ? [] : ['RED_USER'] };
|
||||
await this._userControllerService.updateProfile(requestBody, user.id).toPromise();
|
||||
await this._loadData();
|
||||
this._avatars.find(item => item.userId === user.id).detectChanges();
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DossierAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { DossierAttributeConfigType } from '@redaction/red-ui-http';
|
||||
|
||||
export const dossierAttributeTypesTranslations: { [key in DossierAttributeConfig.TypeEnum]: string } = {
|
||||
export const dossierAttributeTypesTranslations: { [key in DossierAttributeConfigType]: string } = {
|
||||
TEXT: _('dossier-attribute-types.text'),
|
||||
NUMBER: _('dossier-attribute-types.number'),
|
||||
DATE: _('dossier-attribute-types.date'),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { FileAttributeConfigType } from '@redaction/red-ui-http';
|
||||
|
||||
export const fileAttributeTypesTranslations: { [key in FileAttributeConfig.TypeEnum]: string } = {
|
||||
export const fileAttributeTypesTranslations: { [key in FileAttributeConfigType]: string } = {
|
||||
TEXT: _('file-attribute-types.text'),
|
||||
NUMBER: _('file-attribute-types.number'),
|
||||
DATE: _('file-attribute-types.date')
|
||||
|
||||
@ -2,7 +2,7 @@ import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
@ -19,8 +19,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
export class DossierOverviewBulkActionsComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
@Output()
|
||||
reload = new EventEmitter();
|
||||
@Output() readonly reload = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -31,14 +30,14 @@ export class DossierOverviewBulkActionsComponent {
|
||||
private readonly _fileActionService: FileActionService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _entitiesService: EntitiesService<FileStatusWrapper>
|
||||
private readonly _entitiesService: EntitiesService<File>
|
||||
) {}
|
||||
|
||||
get dossier() {
|
||||
return this._appStateService?.activeDossier;
|
||||
}
|
||||
|
||||
get selectedFiles(): FileStatusWrapper[] {
|
||||
get selectedFiles(): File[] {
|
||||
return this._entitiesService.selected;
|
||||
}
|
||||
|
||||
@ -91,21 +90,18 @@ export class DossierOverviewBulkActionsComponent {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && file.canBeOCRed, true);
|
||||
}
|
||||
|
||||
get fileStatuses() {
|
||||
return this.selectedFiles.map(file => file.fileStatus.status);
|
||||
get files() {
|
||||
return this.selectedFiles.map(file => file.status);
|
||||
}
|
||||
|
||||
// Under review
|
||||
get canSetToUnderReview() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true);
|
||||
}
|
||||
|
||||
// Under approval
|
||||
get canSetToUnderApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true);
|
||||
}
|
||||
|
||||
// Approve
|
||||
get isReadyForApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true);
|
||||
}
|
||||
@ -114,7 +110,6 @@ export class DossierOverviewBulkActionsComponent {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && file.canBeApproved, true);
|
||||
}
|
||||
|
||||
// Undo approval
|
||||
get canUndoApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true);
|
||||
}
|
||||
@ -156,7 +151,7 @@ export class DossierOverviewBulkActionsComponent {
|
||||
this._assignFiles('approver', true);
|
||||
} else {
|
||||
this._performBulkAction(
|
||||
this._fileActionService.setFileUnderApproval(this.selectedFiles, this._appStateService.activeDossier.approverIds[0])
|
||||
this._fileActionService.setFilesUnderApproval(this.selectedFiles, this._appStateService.activeDossier.approverIds[0])
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -164,16 +159,16 @@ export class DossierOverviewBulkActionsComponent {
|
||||
async reanalyse() {
|
||||
const fileIds = this.selectedFiles.filter(file => file.analysisRequired).map(file => file.fileId);
|
||||
this._performBulkAction(
|
||||
this._reanalysisControllerService.reanalyzeFilesForDossier(fileIds, this._appStateService.activeDossier.dossierId)
|
||||
this._reanalysisControllerService.reanalyzeFilesForDossier(fileIds, this._appStateService.activeDossier.id)
|
||||
);
|
||||
}
|
||||
|
||||
ocr() {
|
||||
this._performBulkAction(this._fileActionService.ocrFile(this.selectedFiles));
|
||||
this._performBulkAction(this._fileActionService.ocrFiles(this.selectedFiles));
|
||||
}
|
||||
|
||||
setToUnderReview() {
|
||||
this._performBulkAction(this._fileActionService.setFileUnderReview(this.selectedFiles));
|
||||
this._performBulkAction(this._fileActionService.setFilesUnderReview(this.selectedFiles));
|
||||
}
|
||||
|
||||
approveDocuments() {
|
||||
@ -187,11 +182,11 @@ export class DossierOverviewBulkActionsComponent {
|
||||
question: _('confirmation-dialog.approve-multiple-files.question')
|
||||
}),
|
||||
() => {
|
||||
this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles));
|
||||
this._performBulkAction(this._fileActionService.setFilesApproved(this.selectedFiles));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles));
|
||||
this._performBulkAction(this._fileActionService.setFilesApproved(this.selectedFiles));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { FileAttributesConfig } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
import { AutoUnsubscribe } from '@iqser/common-ui';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-document-info',
|
||||
@ -11,7 +11,7 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
styleUrls: ['./document-info.component.scss']
|
||||
})
|
||||
export class DocumentInfoComponent extends AutoUnsubscribe implements OnInit {
|
||||
@Input() file: FileStatusWrapper;
|
||||
@Input() file: File;
|
||||
@Output() closeDocumentInfoView = new EventEmitter();
|
||||
|
||||
fileAttributesConfig: FileAttributesConfig;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:user"></mat-icon>
|
||||
<span>{{ 'dossier-overview.dossier-details.stats.people' | translate: { count: activeDossier.memberCount } }}</span>
|
||||
<span>{{ 'dossier-overview.dossier-details.stats.people' | translate: { count: activeDossier.memberIds.length } }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 0 8px;
|
||||
padding: 4px 8px;
|
||||
margin-left: -8px;
|
||||
|
||||
&.link-property {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { DossierTemplateModel } from '@redaction/red-ui-http';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { IDossierTemplate } from '@redaction/red-ui-http';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
|
||||
@Component({
|
||||
@ -13,21 +13,21 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
export class DossierDetailsStatsComponent {
|
||||
attributesExpanded = false;
|
||||
@Input() dossierAttributes: DossierAttributeWithValue[];
|
||||
@Output() openDossierDictionaryDialog = new EventEmitter();
|
||||
@Output() readonly openDossierDictionaryDialog = new EventEmitter();
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService, private readonly _dialogService: DossiersDialogService) {}
|
||||
|
||||
get activeDossier(): DossierWrapper {
|
||||
get activeDossier(): Dossier {
|
||||
return this._appStateService.activeDossier;
|
||||
}
|
||||
|
||||
get dossierTemplate(): DossierTemplateModel {
|
||||
get dossierTemplate(): IDossierTemplate {
|
||||
return this._appStateService.getDossierTemplateById(this.activeDossier.dossierTemplateId);
|
||||
}
|
||||
|
||||
openEditDossierAttributesDialog() {
|
||||
this._dialogService.openDialog('editDossier', null, {
|
||||
dossierWrapper: this.activeDossier,
|
||||
dossier: this.activeDossier,
|
||||
section: 'dossierAttributes'
|
||||
});
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
<div *ngIf="hasFiles" class="mt-24 legend pb-32">
|
||||
<div
|
||||
(click)="filterService.toggleFilter('needsWorkFilters', filter.key)"
|
||||
(click)="filterService.toggleFilter('needsWorkFilters', filter.id)"
|
||||
*ngFor="let filter of needsWorkFilters$ | async"
|
||||
[class.active]="filter.checked"
|
||||
>
|
||||
|
||||
@ -4,12 +4,13 @@ import { groupBy } from '@utils/functions';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { TranslateChartService } from '@services/translate-chart.service';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { FilterService } from '@iqser/common-ui';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FilterService, Toaster } from '@iqser/common-ui';
|
||||
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
|
||||
import { fileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { List } from '@redaction/red-ui-http';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-details',
|
||||
@ -18,12 +19,12 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
})
|
||||
export class DossierDetailsComponent implements OnInit {
|
||||
documentsChartData: DoughnutChartConfig[] = [];
|
||||
owner: UserWrapper;
|
||||
owner: User;
|
||||
editingOwner = false;
|
||||
@Input() dossierAttributes: DossierAttributeWithValue[];
|
||||
@Output() openAssignDossierMembersDialog = new EventEmitter();
|
||||
@Output() openDossierDictionaryDialog = new EventEmitter();
|
||||
@Output() toggleCollapse = new EventEmitter();
|
||||
@Output() readonly openAssignDossierMembersDialog = new EventEmitter();
|
||||
@Output() readonly openDossierDictionaryDialog = new EventEmitter();
|
||||
@Output() readonly toggleCollapse = new EventEmitter();
|
||||
collapseTooltip = _('dossier-details.collapse');
|
||||
expandTooltip = _('dossier-details.expand');
|
||||
|
||||
@ -39,7 +40,7 @@ export class DossierDetailsComponent implements OnInit {
|
||||
private readonly _toaster: Toaster
|
||||
) {}
|
||||
|
||||
get memberIds(): string[] {
|
||||
get memberIds(): List {
|
||||
return this.appStateService.activeDossier.memberIds;
|
||||
}
|
||||
|
||||
@ -66,12 +67,12 @@ export class DossierDetailsComponent implements OnInit {
|
||||
|
||||
const groups = groupBy(this.appStateService.activeDossier?.files, 'status');
|
||||
this.documentsChartData = [];
|
||||
for (const key of Object.keys(groups)) {
|
||||
for (const status of Object.keys(groups)) {
|
||||
this.documentsChartData.push({
|
||||
value: groups[key].length,
|
||||
color: key,
|
||||
label: fileStatusTranslations[key],
|
||||
key: key
|
||||
value: groups[status].length,
|
||||
color: status,
|
||||
label: fileStatusTranslations[status],
|
||||
key: status
|
||||
});
|
||||
}
|
||||
this.documentsChartData.sort(StatusSorter.byStatus);
|
||||
@ -79,7 +80,7 @@ export class DossierDetailsComponent implements OnInit {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
async assignOwner(user: UserWrapper | string) {
|
||||
async assignOwner(user: User | string) {
|
||||
this.owner = typeof user === 'string' ? this._userService.getRedUserById(user) : user;
|
||||
const dw = { ...this.appStateService.activeDossier, ownerId: this.owner.id };
|
||||
await this.appStateService.createOrUpdateDossier(dw);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '../../../../state/model/dossier';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
@ -17,8 +17,8 @@ export class DossierListingActionsComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@Input() dossier: DossierWrapper;
|
||||
@Output() actionPerformed = new EventEmitter<DossierWrapper | undefined>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly actionPerformed = new EventEmitter<Dossier | undefined>();
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
@ -27,14 +27,14 @@ export class DossierListingActionsComponent {
|
||||
private readonly _userService: UserService
|
||||
) {}
|
||||
|
||||
openEditDossierDialog($event: MouseEvent, dossierWrapper: DossierWrapper): void {
|
||||
openEditDossierDialog($event: MouseEvent, dossier: Dossier): void {
|
||||
this._dialogService.openDialog('editDossier', $event, {
|
||||
dossierWrapper,
|
||||
dossier,
|
||||
afterSave: () => this.actionPerformed.emit()
|
||||
});
|
||||
}
|
||||
|
||||
reanalyseDossier($event: MouseEvent, dossier: DossierWrapper): void {
|
||||
reanalyseDossier($event: MouseEvent, dossier: Dossier): void {
|
||||
$event.stopPropagation();
|
||||
this.appStateService.reanalyzeDossier(dossier).then(() => {
|
||||
this.appStateService.loadAllDossiers().then(() => this.actionPerformed.emit());
|
||||
|
||||
@ -1,17 +1,26 @@
|
||||
<div *ngIf="screen === 'dossier-overview'" class="action-buttons">
|
||||
<div *ngIf="isDossierOverviewList" class="action-buttons">
|
||||
<ng-container *ngTemplateOutlet="actions"></ng-container>
|
||||
<iqser-status-bar *ngIf="isWorkable" [configs]="statusBarConfig"></iqser-status-bar>
|
||||
<iqser-status-bar *ngIf="showStatusBar" [configs]="statusBarConfig"></iqser-status-bar>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="screen === 'file-preview'">
|
||||
<ng-container *ngIf="isFilePreview || isDossierOverviewWorkflow">
|
||||
<ng-container *ngTemplateOutlet="actions"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #actions redactionLongPress (longPress)="forceReanalysisAction($event)">
|
||||
<div class="file-actions" *ngIf="fileStatus">
|
||||
<ng-template #actions (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
||||
<div *ngIf="file" class="file-actions">
|
||||
<iqser-circle-button
|
||||
(action)="openDocument()"
|
||||
*ngIf="showOpenDocument"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.open-document' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:collapse"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="openDeleteFileDialog($event)"
|
||||
*ngIf="canDelete"
|
||||
*ngIf="showDelete"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.delete.action' | translate"
|
||||
[type]="buttonType"
|
||||
@ -20,7 +29,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="assign($event)"
|
||||
*ngIf="canAssign && screen === 'dossier-overview'"
|
||||
*ngIf="showAssign"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="assignTooltip | translate"
|
||||
[type]="buttonType"
|
||||
@ -29,7 +38,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="assignToMe($event)"
|
||||
*ngIf="canAssignToSelf && screen === 'dossier-overview'"
|
||||
*ngIf="showAssignToSelf"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.assign-me' | translate"
|
||||
[type]="buttonType"
|
||||
@ -39,7 +48,7 @@
|
||||
<!-- download redacted file-->
|
||||
<redaction-file-download-btn
|
||||
[dossier]="appStateService.activeDossier"
|
||||
[file]="fileStatus"
|
||||
[file]="file"
|
||||
[tooltipClass]="'small'"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[type]="buttonType"
|
||||
@ -47,7 +56,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleViewDocumentInfo()"
|
||||
*ngIf="screen === 'file-preview'"
|
||||
*ngIf="showDocumentInfo"
|
||||
[attr.aria-expanded]="activeDocumentInfo"
|
||||
[tooltip]="'file-preview.document-info' | translate"
|
||||
icon="red:status-info"
|
||||
@ -56,9 +65,9 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleExcludePages()"
|
||||
*ngIf="screen === 'file-preview'"
|
||||
*ngIf="showExcludePages"
|
||||
[attr.aria-expanded]="activeExcludePages"
|
||||
[showDot]="!!fileStatus.excludedPages?.length"
|
||||
[showDot]="!!file.excludedPages?.length"
|
||||
[tooltip]="'file-preview.exclude-pages' | translate"
|
||||
icon="red:exclude-pages"
|
||||
tooltipPosition="below"
|
||||
@ -67,7 +76,7 @@
|
||||
<!-- Ready for approval-->
|
||||
<iqser-circle-button
|
||||
(action)="setFileUnderApproval($event)"
|
||||
*ngIf="canSetToUnderApproval"
|
||||
*ngIf="showUnderApproval"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.under-approval' | translate"
|
||||
[type]="buttonType"
|
||||
@ -77,7 +86,7 @@
|
||||
<!-- Back to review -->
|
||||
<iqser-circle-button
|
||||
(action)="setFileUnderReview($event, true)"
|
||||
*ngIf="canSetToUnderReview"
|
||||
*ngIf="showUnderReview"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.under-review' | translate"
|
||||
[type]="buttonType"
|
||||
@ -87,12 +96,10 @@
|
||||
<!-- Approved-->
|
||||
<iqser-circle-button
|
||||
(action)="setFileApproved($event)"
|
||||
*ngIf="readyForApproval"
|
||||
[disabled]="!fileStatus.canBeApproved"
|
||||
*ngIf="showApprove"
|
||||
[disabled]="!file.canBeApproved"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="
|
||||
fileStatus.canBeApproved ? ('dossier-overview.approve' | translate) : ('dossier-overview.approve-disabled' | translate)
|
||||
"
|
||||
[tooltip]="file.canBeApproved ? ('dossier-overview.approve' | translate) : ('dossier-overview.approve-disabled' | translate)"
|
||||
[type]="buttonType"
|
||||
icon="red:approved"
|
||||
></iqser-circle-button>
|
||||
@ -100,7 +107,7 @@
|
||||
<!-- Back to approval -->
|
||||
<iqser-circle-button
|
||||
(action)="setFileUnderApproval($event)"
|
||||
*ngIf="canUndoApproval"
|
||||
*ngIf="showUndoApproval"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.under-approval' | translate"
|
||||
[type]="buttonType"
|
||||
@ -109,7 +116,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="ocrFile($event)"
|
||||
*ngIf="fileStatus.canBeOCRed"
|
||||
*ngIf="showOCR"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.ocr-file' | translate"
|
||||
[type]="buttonType"
|
||||
@ -119,30 +126,30 @@
|
||||
<!-- reanalyse file preview -->
|
||||
<iqser-circle-button
|
||||
(action)="reanalyseFile($event)"
|
||||
*ngIf="canReanalyse && screen === 'file-preview'"
|
||||
*ngIf="canReanalyse && isFilePreview"
|
||||
[tooltip]="'file-preview.reanalyse-notification' | translate"
|
||||
[type]="circleButtonTypes.warn"
|
||||
icon="iqser:refresh"
|
||||
tooltipClass="warn small"
|
||||
tooltipPosition="below"
|
||||
[type]="circleButtonTypes.warn"
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- reanalyse file listing -->
|
||||
<iqser-circle-button
|
||||
(action)="reanalyseFile($event)"
|
||||
*ngIf="canReanalyse && screen === 'dossier-overview'"
|
||||
*ngIf="canReanalyse && isDossierOverview"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'dossier-overview.reanalyse.action' | translate"
|
||||
icon="iqser:refresh"
|
||||
[type]="circleButtonTypes.dark"
|
||||
icon="iqser:refresh"
|
||||
></iqser-circle-button>
|
||||
<!-- exclude from redaction -->
|
||||
<div class="iqser-input-group">
|
||||
<mat-slide-toggle
|
||||
(change)="toggleAnalysis()"
|
||||
(click)="$event.stopPropagation()"
|
||||
[checked]="!fileStatus?.excluded"
|
||||
[class.mr-24]="screen === 'dossier-overview'"
|
||||
[checked]="!file?.excluded"
|
||||
[class.mr-24]="isDossierOverviewList"
|
||||
[disabled]="!canToggleAnalysis"
|
||||
[matTooltipPosition]="tooltipPosition"
|
||||
[matTooltip]="toggleTooltip | translate"
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
@use 'common-mixins';
|
||||
@use 'variables';
|
||||
|
||||
.file-actions {
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
color: variables.$grey-1;
|
||||
@include common-mixins.no-scroll-bar;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 2px;
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
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 { File } from '@models/file/file';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes, LoadingService, StatusBarConfig, Toaster } from '@iqser/common-ui';
|
||||
import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes, LoadingService, Required, StatusBarConfig, Toaster } from '@iqser/common-ui';
|
||||
import { FileManagementControllerService, FileStatus } from '@redaction/red-ui-http';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { UserPreferenceService } from '../../../../services/user-preference.service';
|
||||
import { LongPressEvent } from '../../../shared/directives/long-press.directive';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-actions',
|
||||
@ -22,28 +22,32 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@Input() fileStatus: FileStatusWrapper;
|
||||
@Input() file: File;
|
||||
@Input() activeDocumentInfo: boolean;
|
||||
@Input() activeExcludePages: boolean;
|
||||
@Output() actionPerformed = new EventEmitter<string>();
|
||||
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
|
||||
screen: 'file-preview' | 'dossier-overview';
|
||||
statusBarConfig?: readonly StatusBarConfig<FileStatus.StatusEnum>[];
|
||||
statusBarConfig?: readonly StatusBarConfig<FileStatus>[];
|
||||
tooltipPosition?: 'below' | 'above';
|
||||
toggleTooltip?: string;
|
||||
assignTooltip?: string;
|
||||
buttonType?: CircleButtonType;
|
||||
isWorkable: boolean;
|
||||
|
||||
canUndoApproval: boolean;
|
||||
canAssignToSelf: boolean;
|
||||
canAssign: boolean;
|
||||
canDelete: boolean;
|
||||
showUndoApproval: boolean;
|
||||
showAssignToSelf: boolean;
|
||||
showAssign: boolean;
|
||||
showDelete: boolean;
|
||||
showOCR: boolean;
|
||||
canReanalyse: boolean;
|
||||
canSetToUnderReview: boolean;
|
||||
canSetToUnderApproval: boolean;
|
||||
readyForApproval: boolean;
|
||||
showUnderReview: boolean;
|
||||
showUnderApproval: boolean;
|
||||
showApprove: boolean;
|
||||
canToggleAnalysis: boolean;
|
||||
showExcludePages: boolean;
|
||||
showDocumentInfo: boolean;
|
||||
showStatusBar: boolean;
|
||||
showOpenDocument: boolean;
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
@ -59,29 +63,39 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
super();
|
||||
}
|
||||
|
||||
get isDossierOverviewList(): boolean {
|
||||
return this.type === 'dossier-overview-list';
|
||||
}
|
||||
|
||||
get isDossierOverviewWorkflow(): boolean {
|
||||
return this.type === 'dossier-overview-workflow';
|
||||
}
|
||||
|
||||
get isFilePreview(): boolean {
|
||||
return this.type === 'file-preview';
|
||||
}
|
||||
|
||||
get isDossierOverview(): boolean {
|
||||
return this.type.startsWith('dossier-overview-list');
|
||||
}
|
||||
|
||||
private get _toggleTooltip(): string {
|
||||
if (!this.currentUser.isManager) {
|
||||
return _('file-preview.toggle-analysis.only-managers');
|
||||
}
|
||||
|
||||
return this.fileStatus?.excluded ? _('file-preview.toggle-analysis.enable') : _('file-preview.toggle-analysis.disable');
|
||||
return this.file?.excluded ? _('file-preview.toggle-analysis.enable') : _('file-preview.toggle-analysis.disable');
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.fileStatus) {
|
||||
this.screen = 'dossier-overview';
|
||||
} else {
|
||||
this.fileStatus = this.appStateService.activeFile;
|
||||
this.screen = 'file-preview';
|
||||
if (!this.file) {
|
||||
this.file = this.appStateService.activeFile;
|
||||
}
|
||||
|
||||
this._setup();
|
||||
this.addSubscription = this.appStateService.fileChanged$
|
||||
.pipe(filter(file => file.fileId === this.fileStatus?.fileId))
|
||||
.subscribe(fileStatus => {
|
||||
this.fileStatus = fileStatus;
|
||||
this._setup();
|
||||
});
|
||||
this.addSubscription = this.appStateService.fileChanged$.pipe(filter(file => file.fileId === this.file?.fileId)).subscribe(file => {
|
||||
this.file = file;
|
||||
this._setup();
|
||||
});
|
||||
|
||||
this.addSubscription = this.appStateService.dossierChanged$.subscribe(() => {
|
||||
this._setup();
|
||||
@ -96,6 +110,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
this.actionPerformed.emit('view-exclude-pages');
|
||||
}
|
||||
|
||||
openDocument() {
|
||||
this.actionPerformed.emit('navigate');
|
||||
}
|
||||
|
||||
openDeleteFileDialog($event: MouseEvent) {
|
||||
this._dialogService.openDialog(
|
||||
'confirm',
|
||||
@ -107,7 +125,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
async () => {
|
||||
this._loadingService.start();
|
||||
await this._fileManagementControllerService
|
||||
.deleteFiles([this.fileStatus.fileId], this.fileStatus.dossierId)
|
||||
.deleteFiles([this.file.fileId], this.file.dossierId)
|
||||
.toPromise()
|
||||
.catch(error => {
|
||||
this._toaster.error(_('error.http.generic'), { params: error });
|
||||
@ -120,8 +138,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
}
|
||||
|
||||
assign($event: MouseEvent) {
|
||||
const mode = this.fileStatus.isUnderApproval ? 'approver' : 'reviewer';
|
||||
const files = [this.fileStatus];
|
||||
const mode = this.file.isUnderApproval ? 'approver' : 'reviewer';
|
||||
const files = [this.file];
|
||||
this._dialogService.openDialog('assignFile', $event, { mode, files }, () => {
|
||||
this.actionPerformed.emit('assign-reviewer');
|
||||
});
|
||||
@ -130,7 +148,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
async assignToMe($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
|
||||
await this._fileActionService.assignToMe(this.fileStatus, () => {
|
||||
await this._fileActionService.assignToMe([this.file], () => {
|
||||
this.reloadDossiers('reanalyse');
|
||||
});
|
||||
}
|
||||
@ -139,7 +157,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
this.addSubscription = this._fileActionService.reanalyseFile(this.fileStatus).subscribe(() => {
|
||||
this.addSubscription = this._fileActionService.reanalyseFile(this.file).subscribe(() => {
|
||||
this.reloadDossiers('reanalyse');
|
||||
});
|
||||
}
|
||||
@ -147,9 +165,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
setFileUnderApproval($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
if (this.appStateService.activeDossier.approverIds.length > 1) {
|
||||
this._fileActionService.assignFile('approver', $event, this.fileStatus, () => this.reloadDossiers('assign-reviewer'), true);
|
||||
this._fileActionService.assignFile('approver', $event, this.file, () => this.reloadDossiers('assign-reviewer'), true);
|
||||
} else {
|
||||
this.addSubscription = this._fileActionService.setFileUnderApproval(this.fileStatus).subscribe(() => {
|
||||
this.addSubscription = this._fileActionService.setFilesUnderApproval([this.file]).subscribe(() => {
|
||||
this.reloadDossiers('set-under-approval');
|
||||
});
|
||||
}
|
||||
@ -157,7 +175,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
|
||||
setFileApproved($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
if (this.fileStatus.hasUpdates) {
|
||||
if (this.file.hasUpdates) {
|
||||
this._dialogService.openDialog(
|
||||
'confirm',
|
||||
$event,
|
||||
@ -176,7 +194,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
|
||||
ocrFile($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.addSubscription = this._fileActionService.ocrFile(this.fileStatus).subscribe(() => {
|
||||
this.addSubscription = this._fileActionService.ocrFiles([this.file]).subscribe(() => {
|
||||
this.reloadDossiers('ocr-file');
|
||||
});
|
||||
}
|
||||
@ -185,7 +203,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
this._fileActionService.assignFile(
|
||||
'reviewer',
|
||||
$event,
|
||||
this.fileStatus,
|
||||
this.file,
|
||||
() => this.reloadDossiers('assign-reviewer'),
|
||||
ignoreDialogChanges
|
||||
);
|
||||
@ -198,47 +216,54 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
|
||||
}
|
||||
|
||||
async toggleAnalysis() {
|
||||
await this._fileActionService.toggleAnalysis(this.fileStatus).toPromise();
|
||||
await this._fileActionService.toggleAnalysis(this.file).toPromise();
|
||||
await this.appStateService.getFiles();
|
||||
this.actionPerformed.emit(this.fileStatus?.excluded ? 'enable-analysis' : 'disable-analysis');
|
||||
this.actionPerformed.emit(this.file?.excluded ? 'enable-analysis' : 'disable-analysis');
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.fileStatus) {
|
||||
if (changes.file) {
|
||||
this._setup();
|
||||
}
|
||||
}
|
||||
|
||||
forceReanalysisAction($event: LongPressEvent) {
|
||||
if (this._userPreferenceService.areDevFeaturesEnabled) {
|
||||
this.canReanalyse = $event.touchEnd ? this.permissionsService.canReanalyseFile(this.fileStatus) : true;
|
||||
this.canReanalyse = $event.touchEnd ? this.permissionsService.canReanalyseFile(this.file) : true;
|
||||
}
|
||||
}
|
||||
|
||||
private _setFileApproved() {
|
||||
this.addSubscription = this._fileActionService.setFileApproved(this.fileStatus).subscribe(() => {
|
||||
this.addSubscription = this._fileActionService.setFilesApproved([this.file]).subscribe(() => {
|
||||
this.reloadDossiers('set-approved');
|
||||
});
|
||||
}
|
||||
|
||||
private _setup() {
|
||||
this.statusBarConfig = [{ color: this.fileStatus.status, length: 1 }];
|
||||
this.tooltipPosition = this.screen === 'file-preview' ? 'below' : 'above';
|
||||
this.assignTooltip = this.fileStatus.isUnderApproval
|
||||
? _('dossier-overview.assign-approver')
|
||||
: _('dossier-overview.assign-reviewer');
|
||||
this.buttonType = this.screen === 'file-preview' ? CircleButtonTypes.default : CircleButtonTypes.dark;
|
||||
this.isWorkable = this.fileStatus.isWorkable;
|
||||
this.statusBarConfig = [{ color: this.file.status, length: 1 }];
|
||||
this.tooltipPosition = this.isFilePreview ? 'below' : 'above';
|
||||
this.assignTooltip = this.file.isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer');
|
||||
this.buttonType = this.isFilePreview ? CircleButtonTypes.default : CircleButtonTypes.dark;
|
||||
this.toggleTooltip = this._toggleTooltip;
|
||||
|
||||
this.canUndoApproval = this.permissionsService.canUndoApproval(this.fileStatus);
|
||||
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.fileStatus);
|
||||
this.canAssign = this.permissionsService.canAssignUser(this.fileStatus);
|
||||
this.canDelete = this.permissionsService.canDeleteFile(this.fileStatus);
|
||||
this.canReanalyse = this.permissionsService.canReanalyseFile(this.fileStatus);
|
||||
this.canSetToUnderReview = this.permissionsService.canSetUnderReview(this.fileStatus);
|
||||
this.canSetToUnderApproval = this.permissionsService.canSetUnderApproval(this.fileStatus);
|
||||
this.readyForApproval = this.permissionsService.isReadyForApproval(this.fileStatus);
|
||||
this.canToggleAnalysis = this.permissionsService.canToggleAnalysis(this.fileStatus);
|
||||
this.showUndoApproval = this.permissionsService.canUndoApproval(this.file) && !this.isDossierOverviewWorkflow;
|
||||
this.showUnderReview = this.permissionsService.canSetUnderReview(this.file) && !this.isDossierOverviewWorkflow;
|
||||
this.showUnderApproval = this.permissionsService.canSetUnderApproval(this.file) && !this.isDossierOverviewWorkflow;
|
||||
this.showApprove = this.permissionsService.isReadyForApproval(this.file) && !this.isDossierOverviewWorkflow;
|
||||
|
||||
this.canToggleAnalysis = this.permissionsService.canToggleAnalysis(this.file);
|
||||
this.showDelete = this.permissionsService.canDeleteFile(this.file);
|
||||
this.showOCR = this.file.canBeOCRed;
|
||||
this.canReanalyse = this.permissionsService.canReanalyseFile(this.file);
|
||||
|
||||
this.showStatusBar = this.file.isWorkable && this.isDossierOverviewList;
|
||||
|
||||
this.showAssignToSelf = this.permissionsService.canAssignToSelf(this.file) && this.isDossierOverview;
|
||||
this.showAssign = this.permissionsService.canAssignUser(this.file) && this.isDossierOverview;
|
||||
|
||||
this.showOpenDocument = this.file.canBeOpened && this.isDossierOverviewWorkflow;
|
||||
|
||||
this.showExcludePages = this.isFilePreview;
|
||||
this.showDocumentInfo = this.isFilePreview;
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
</div>
|
||||
<div
|
||||
(click)="scrollQuickNavLast()"
|
||||
[class.disabled]="activeViewerPage === fileData?.fileStatus?.numberOfPages"
|
||||
[class.disabled]="activeViewerPage === fileData?.file?.numberOfPages"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
|
||||
class="jump"
|
||||
matTooltipPosition="above"
|
||||
@ -141,7 +141,7 @@
|
||||
[verticalPadding]="40"
|
||||
icon="red:document"
|
||||
>
|
||||
<ng-container *ngIf="fileData?.fileStatus?.excludedPages?.includes(activeViewerPage)">
|
||||
<ng-container *ngIf="fileData?.file?.excludedPages?.includes(activeViewerPage)">
|
||||
{{ 'file-preview.tabs.annotations.page-is' | translate }}
|
||||
<a
|
||||
(click)="actionPerformed.emit('view-exclude-pages')"
|
||||
@ -171,15 +171,15 @@
|
||||
</ng-container>
|
||||
|
||||
<redaction-annotations-list
|
||||
[canMultiSelect]="!isReadOnly"
|
||||
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[(multiSelectActive)]="multiSelectActive"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
(deselectAnnotations)="deselectAnnotations.emit($event)"
|
||||
(pagesPanelActive)="pagesPanelActive = $event"
|
||||
(selectAnnotations)="selectAnnotations.emit($event)"
|
||||
(deselectAnnotations)="deselectAnnotations.emit($event)"
|
||||
[(multiSelectActive)]="multiSelectActive"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
|
||||
[canMultiSelect]="!isReadOnly"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
></redaction-annotations-list>
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -187,7 +187,7 @@
|
||||
<redaction-page-exclusion
|
||||
(actionPerformed)="actionPerformed.emit($event)"
|
||||
*ngIf="excludePages"
|
||||
[fileStatus]="fileData.fileStatus"
|
||||
[file]="fileData.file"
|
||||
></redaction-page-exclusion>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, IqserEventTarget, NestedFilter } from '@iqser/common-ui';
|
||||
import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, INestedFilter, IqserEventTarget } from '@iqser/common-ui';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { WebViewerInstance } from '@pdftron/webviewer';
|
||||
@ -43,8 +43,10 @@ export class FileWorkloadComponent {
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
displayedPages: number[] = [];
|
||||
pagesPanelActive = true;
|
||||
readonly displayedAnnotations$ = this._displayedAnnotations$;
|
||||
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
|
||||
private _annotations$ = new BehaviorSubject<AnnotationWrapper[]>([]);
|
||||
|
||||
constructor(
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
@ -53,9 +55,6 @@ export class FileWorkloadComponent {
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService
|
||||
) {}
|
||||
|
||||
private _annotations$ = new BehaviorSubject<AnnotationWrapper[]>([]);
|
||||
readonly displayedAnnotations$ = this._displayedAnnotations$;
|
||||
|
||||
@Input()
|
||||
set annotations(value: AnnotationWrapper[]) {
|
||||
this._annotations$.next(value);
|
||||
@ -78,7 +77,7 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
get isProcessing(): boolean {
|
||||
return this.fileData?.fileStatus?.isProcessing;
|
||||
return this.fileData?.file?.isProcessing;
|
||||
}
|
||||
|
||||
get activeAnnotations(): AnnotationWrapper[] | undefined {
|
||||
@ -93,6 +92,14 @@ export class FileWorkloadComponent {
|
||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
||||
}
|
||||
|
||||
private get _displayedAnnotations$(): Observable<Map<number, AnnotationWrapper[]>> {
|
||||
const primary$ = this._filterService.getFilterModels$('primaryFilters');
|
||||
const secondary$ = this._filterService.getFilterModels$('secondaryFilters');
|
||||
return combineLatest([this._annotations$, primary$, secondary$]).pipe(
|
||||
map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary))
|
||||
);
|
||||
}
|
||||
|
||||
private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') {
|
||||
if (elements.length > 0) {
|
||||
scrollIntoView(elements[0], {
|
||||
@ -120,19 +127,6 @@ export class FileWorkloadComponent {
|
||||
this.deselectAnnotations.emit(this.activeAnnotations);
|
||||
}
|
||||
|
||||
private _filterAnnotations(
|
||||
annotations: AnnotationWrapper[],
|
||||
primary: NestedFilter[],
|
||||
secondary: NestedFilter[] = []
|
||||
): Map<number, AnnotationWrapper[]> {
|
||||
if (!primary) {
|
||||
return;
|
||||
}
|
||||
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(annotations, primary, secondary);
|
||||
this.displayedPages = [...this.displayedAnnotations.keys()];
|
||||
return this.displayedAnnotations;
|
||||
}
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent): void {
|
||||
if (
|
||||
@ -210,7 +204,7 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
scrollQuickNavLast(): void {
|
||||
this.selectPage.emit(this.fileData.fileStatus.numberOfPages);
|
||||
this.selectPage.emit(this.fileData.file.numberOfPages);
|
||||
}
|
||||
|
||||
pageSelectedByClick($event: number): void {
|
||||
@ -232,12 +226,17 @@ export class FileWorkloadComponent {
|
||||
this.selectPage.emit(this._nextPageWithAnnotations());
|
||||
}
|
||||
|
||||
private get _displayedAnnotations$(): Observable<Map<number, AnnotationWrapper[]>> {
|
||||
const primary$ = this._filterService.getFilterModels$('primaryFilters');
|
||||
const secondary$ = this._filterService.getFilterModels$('secondaryFilters');
|
||||
return combineLatest([this._annotations$, primary$, secondary$]).pipe(
|
||||
map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary))
|
||||
);
|
||||
private _filterAnnotations(
|
||||
annotations: AnnotationWrapper[],
|
||||
primary: INestedFilter[],
|
||||
secondary: INestedFilter[] = []
|
||||
): Map<number, AnnotationWrapper[]> {
|
||||
if (!primary) {
|
||||
return;
|
||||
}
|
||||
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(annotations, primary, secondary);
|
||||
this.displayedPages = [...this.displayedAnnotations.keys()];
|
||||
return this.displayedAnnotations;
|
||||
}
|
||||
|
||||
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
import { Dossier } from '../../../../state/model/dossier';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-needs-work-badge',
|
||||
@ -9,7 +9,7 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
styleUrls: ['./needs-work-badge.component.scss']
|
||||
})
|
||||
export class NeedsWorkBadgeComponent {
|
||||
@Input() needsWorkInput: FileStatusWrapper | DossierWrapper;
|
||||
@Input() needsWorkInput: File | Dossier;
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService) {}
|
||||
|
||||
@ -38,19 +38,19 @@ export class NeedsWorkBadgeComponent {
|
||||
}
|
||||
|
||||
get hasImages() {
|
||||
return this.needsWorkInput instanceof FileStatusWrapper && this.needsWorkInput.hasImages;
|
||||
return this.needsWorkInput instanceof File && this.needsWorkInput.hasImages;
|
||||
}
|
||||
|
||||
get hasUpdates() {
|
||||
return this.needsWorkInput instanceof FileStatusWrapper && this.needsWorkInput.hasUpdates;
|
||||
return this.needsWorkInput instanceof File && this.needsWorkInput.hasUpdates;
|
||||
}
|
||||
|
||||
get hasAnnotationComments(): boolean {
|
||||
return this.needsWorkInput instanceof FileStatusWrapper && (<any>this.needsWorkInput).hasAnnotationComments;
|
||||
return this.needsWorkInput instanceof File && (<any>this.needsWorkInput).hasAnnotationComments;
|
||||
}
|
||||
|
||||
reanalysisRequired() {
|
||||
if (this.needsWorkInput instanceof DossierWrapper) {
|
||||
if (this.needsWorkInput instanceof Dossier) {
|
||||
return this.needsWorkInput.reanalysisRequired;
|
||||
} else {
|
||||
return this.needsWorkInput.analysisRequired;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { PageRange, ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { InputWithActionComponent, Toaster, LoadingService } from '@iqser/common-ui';
|
||||
import { InputWithActionComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-page-exclusion',
|
||||
@ -11,7 +11,7 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
styleUrls: ['./page-exclusion.component.scss']
|
||||
})
|
||||
export class PageExclusionComponent implements OnChanges {
|
||||
@Input() fileStatus: FileStatusWrapper;
|
||||
@Input() file: File;
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
|
||||
excludedPagesRanges: PageRange[] = [];
|
||||
@ -25,7 +25,7 @@ export class PageExclusionComponent implements OnChanges {
|
||||
) {}
|
||||
|
||||
ngOnChanges(): void {
|
||||
const excludedPages = (this.fileStatus?.excludedPages || []).sort((p1, p2) => p1 - p2);
|
||||
const excludedPages = (this.file?.excludedPages || []).sort((p1, p2) => p1 - p2);
|
||||
this.excludedPagesRanges = excludedPages.reduce((ranges, page) => {
|
||||
if (!ranges.length) {
|
||||
return [{ startPage: page, endPage: page }];
|
||||
@ -60,8 +60,8 @@ export class PageExclusionComponent implements OnChanges {
|
||||
{
|
||||
pageRanges: pageRanges
|
||||
},
|
||||
this.fileStatus.dossierId,
|
||||
this.fileStatus.fileId
|
||||
this.file.dossierId,
|
||||
this.file.fileId
|
||||
)
|
||||
.toPromise();
|
||||
this._inputComponent.reset();
|
||||
@ -79,8 +79,8 @@ export class PageExclusionComponent implements OnChanges {
|
||||
{
|
||||
pageRanges: [range]
|
||||
},
|
||||
this.fileStatus.dossierId,
|
||||
this.fileStatus.fileId
|
||||
this.file.dossierId,
|
||||
this.file.fileId
|
||||
)
|
||||
.toPromise();
|
||||
this._inputComponent.reset();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class="page">
|
||||
<div #viewer [id]="fileStatus.fileId" class="viewer"></div>
|
||||
<div #viewer [id]="file.fileId" class="viewer"></div>
|
||||
</div>
|
||||
|
||||
<input #compareFileInput (change)="uploadFile($event.target['files'])" class="file-upload-input" type="file" />
|
||||
|
||||
@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { ManualAnnotationService } from '../../services/manual-annotation.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
import { environment } from '@environments/environment';
|
||||
import { AnnotationDrawService } from '../../services/annotation-draw.service';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
@ -43,18 +43,18 @@ import Annotation = Core.Annotations.Annotation;
|
||||
})
|
||||
export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
@Input() fileData: Blob;
|
||||
@Input() fileStatus: FileStatusWrapper;
|
||||
@Input() file: File;
|
||||
@Input() canPerformActions = false;
|
||||
@Input() annotations: AnnotationWrapper[];
|
||||
@Input() shouldDeselectAnnotationsOnPageChange = true;
|
||||
@Input() multiSelectActive: boolean;
|
||||
@Output() fileReady = new EventEmitter();
|
||||
@Output() annotationSelected = new EventEmitter<string[]>();
|
||||
@Output() manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
||||
@Output() pageChanged = new EventEmitter<number>();
|
||||
@Output() keyUp = new EventEmitter<KeyboardEvent>();
|
||||
@Output() viewerReady = new EventEmitter<WebViewerInstance>();
|
||||
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
|
||||
@Output() readonly fileReady = new EventEmitter();
|
||||
@Output() readonly annotationSelected = new EventEmitter<string[]>();
|
||||
@Output() readonly manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
||||
@Output() readonly pageChanged = new EventEmitter<number>();
|
||||
@Output() readonly keyUp = new EventEmitter<KeyboardEvent>();
|
||||
@Output() readonly viewerReady = new EventEmitter<WebViewerInstance>();
|
||||
@Output() readonly annotationsChanged = new EventEmitter<AnnotationWrapper>();
|
||||
@ViewChild('viewer', { static: true }) viewer: ElementRef;
|
||||
@ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
|
||||
instance: WebViewerInstance;
|
||||
@ -146,7 +146,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
compareDocument,
|
||||
mergedDocument,
|
||||
this.instance,
|
||||
this.fileStatus,
|
||||
this.file,
|
||||
() => {
|
||||
this.viewMode = 'COMPARE';
|
||||
},
|
||||
@ -188,7 +188,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
|
||||
this.instance.UI.loadDocument(currentDocument, {
|
||||
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
|
||||
filename: this.file ? this.file.filename : 'document.pdf'
|
||||
});
|
||||
this.instance.UI.disableElements(['closeCompareButton']);
|
||||
this.instance.UI.enableElements(['compareButton']);
|
||||
@ -571,7 +571,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
private _loadDocument() {
|
||||
if (this.fileData) {
|
||||
this.instance.UI.loadDocument(this.fileData, {
|
||||
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
|
||||
filename: this.file ? this.file.filename : 'document.pdf'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Dossier } from '@redaction/red-ui-http';
|
||||
import { IDossier } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '../../../../state/model/dossier';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@Component({
|
||||
@ -16,9 +16,9 @@ export class TeamMembersManagerComponent implements OnInit {
|
||||
teamForm: FormGroup;
|
||||
searchQuery = '';
|
||||
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly save = new EventEmitter<IDossier>();
|
||||
|
||||
@Output() readonly save = new EventEmitter<Dossier>();
|
||||
readonly ownersSelectOptions = this.userService.managerUsers.map(m => m.id);
|
||||
selectedReviewersList: string[] = [];
|
||||
membersSelectOptions: string[] = [];
|
||||
@ -58,7 +58,7 @@ export class TeamMembersManagerComponent implements OnInit {
|
||||
const memberIds = this.selectedMembersList;
|
||||
const approverIds = this.selectedApproversList;
|
||||
const dw = {
|
||||
...this.dossierWrapper,
|
||||
...this.dossier,
|
||||
memberIds,
|
||||
approverIds,
|
||||
ownerId
|
||||
@ -120,15 +120,15 @@ export class TeamMembersManagerComponent implements OnInit {
|
||||
}
|
||||
|
||||
private _updateChanged() {
|
||||
if (this.dossierWrapper.ownerId !== this.selectedOwnerId) {
|
||||
if (this.dossier.ownerId !== this.selectedOwnerId) {
|
||||
this.changed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const initialMembers = this.dossierWrapper.memberIds.sort();
|
||||
const initialMembers = [...this.dossier.memberIds].sort();
|
||||
const currentMembers = this.selectedMembersList.sort();
|
||||
|
||||
const initialApprovers = this.dossierWrapper.approverIds.sort();
|
||||
const initialApprovers = [...this.dossier.approverIds].sort();
|
||||
const currentApprovers = this.selectedApproversList.sort();
|
||||
|
||||
this.changed = this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers);
|
||||
@ -147,9 +147,9 @@ export class TeamMembersManagerComponent implements OnInit {
|
||||
|
||||
private _loadData() {
|
||||
this.teamForm = this._formBuilder.group({
|
||||
owner: [this.dossierWrapper?.ownerId, Validators.required],
|
||||
approvers: [[...this.dossierWrapper?.approverIds]],
|
||||
members: [[...this.dossierWrapper?.memberIds]]
|
||||
owner: [this.dossier?.ownerId, Validators.required],
|
||||
approvers: [[...this.dossier?.approverIds]],
|
||||
members: [[...this.dossier?.memberIds]]
|
||||
});
|
||||
this.teamForm.get('owner').valueChanges.subscribe(owner => {
|
||||
if (!this.isApprover(owner)) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
|
||||
import { CircleButtonTypes } from '@iqser/common-ui';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { List } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-team-members',
|
||||
@ -11,7 +12,7 @@ export class TeamMembersComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@Input() memberIds: string[];
|
||||
@Input() memberIds: List;
|
||||
@Input() perLine: number;
|
||||
@Input() canAdd = true;
|
||||
@Input() largeSpacing = false;
|
||||
@ -30,7 +31,7 @@ export class TeamMembersComponent {
|
||||
return this.perLine - (this.canAdd ? 1 : 0);
|
||||
}
|
||||
|
||||
get displayedMembers(): string[] {
|
||||
get displayedMembers(): List {
|
||||
return this.expandedTeam || !this.overflowCount ? this.memberIds : this.memberIds.slice(0, this.maxTeamMembersBeforeExpand - 1);
|
||||
}
|
||||
|
||||
|
||||
@ -1,57 +1,57 @@
|
||||
<ng-container *ngIf="!filter.icon">
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.key === 'redaction'"
|
||||
*ngIf="filter.id === 'redaction'"
|
||||
[color]="dictionaryColor"
|
||||
label="R"
|
||||
type="square"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.key === 'recommendation'"
|
||||
*ngIf="filter.id === 'recommendation'"
|
||||
[color]="dictionaryColor"
|
||||
label="R"
|
||||
type="hexagon"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon *ngIf="filter.key === 'hint'" [color]="dictionaryColor" label="H" type="circle"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon *ngIf="filter.id === 'hint'" [color]="dictionaryColor" label="H" type="circle"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.key === 'manual-redaction'"
|
||||
*ngIf="filter.id === 'manual-redaction'"
|
||||
[color]="dictionaryColor"
|
||||
label="M"
|
||||
type="square"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.key === 'skipped'"
|
||||
*ngIf="filter.id === 'skipped'"
|
||||
[color]="dictionaryColor"
|
||||
label="S"
|
||||
type="square"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="isSuggestion(filter.key)"
|
||||
*ngIf="isSuggestion(filter.id)"
|
||||
[color]="dictionaryColor"
|
||||
label="S"
|
||||
type="rhombus"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="needsAnalysis(filter.key)"
|
||||
*ngIf="needsAnalysis(filter.id)"
|
||||
[color]="dictionaryColor"
|
||||
label="A"
|
||||
type="square"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.key === 'declined-suggestion'"
|
||||
*ngIf="filter.id === 'declined-suggestion'"
|
||||
[color]="dictionaryColor"
|
||||
label="S"
|
||||
type="rhombus"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon *ngIf="filter.key === 'none'" color="transparent" label="-" type="none"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon *ngIf="filter.id === 'none'" color="transparent" label="-" type="none"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.key === 'updated'"
|
||||
*ngIf="filter.id === 'updated'"
|
||||
[color]="dictionaryColor"
|
||||
label="U"
|
||||
type="square"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon *ngIf="filter.key === 'image'" [color]="dictionaryColor" label="I" type="square"></redaction-annotation-icon>
|
||||
<redaction-annotation-icon *ngIf="filter.id === 'image'" [color]="dictionaryColor" label="I" type="square"></redaction-annotation-icon>
|
||||
|
||||
<div *ngIf="filter.key === 'comment'">
|
||||
<div *ngIf="filter.id === 'comment'">
|
||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { NestedFilter } from '@iqser/common-ui';
|
||||
import { INestedFilter } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-type-filter',
|
||||
@ -8,7 +8,7 @@ import { NestedFilter } from '@iqser/common-ui';
|
||||
styleUrls: ['./type-filter.component.scss']
|
||||
})
|
||||
export class TypeFilterComponent implements OnInit {
|
||||
@Input() filter: NestedFilter;
|
||||
@Input() filter: INestedFilter;
|
||||
|
||||
dictionaryColor: string;
|
||||
|
||||
@ -36,6 +36,6 @@ export class TypeFilterComponent implements OnInit {
|
||||
needsAnalysis = (key: string) => this._needsAnalysisKeys.includes(key);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.dictionaryColor = this._appStateService.getDictionaryColor(this.filter.key);
|
||||
this.dictionaryColor = this._appStateService.getDictionaryColor(this.filter.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { Dossier, DossierTemplateModel, ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http';
|
||||
import { DownloadFileType, IDossier, IDossierTemplate, ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import * as moment from 'moment';
|
||||
@ -16,12 +16,11 @@ export class AddDossierDialogComponent {
|
||||
|
||||
dossierForm: FormGroup;
|
||||
hasDueDate = false;
|
||||
downloadTypesEnum: Dossier.DownloadFileTypesEnum[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
downloadTypes: { key: Dossier.DownloadFileTypesEnum; label: string }[] = this.downloadTypesEnum.map(type => ({
|
||||
downloadTypes: { key: DownloadFileType; label: string }[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'].map((type: DownloadFileType) => ({
|
||||
key: type,
|
||||
label: downloadTypesTranslations[type]
|
||||
}));
|
||||
dossierTemplates: DossierTemplateModel[];
|
||||
dossierTemplates: IDossierTemplate[];
|
||||
availableReportTypes = [];
|
||||
reportTemplateValueMapper = (reportTemplate: ReportTemplate) => reportTemplate.templateId;
|
||||
|
||||
@ -29,7 +28,7 @@ export class AddDossierDialogComponent {
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _reportTemplateController: ReportTemplateControllerService,
|
||||
public dialogRef: MatDialogRef<AddDossierDialogComponent>
|
||||
readonly dialogRef: MatDialogRef<AddDossierDialogComponent>
|
||||
) {
|
||||
this._filterInvalidDossierTemplates();
|
||||
this.dossierForm = this._formBuilder.group(
|
||||
@ -68,21 +67,14 @@ export class AddDossierDialogComponent {
|
||||
}
|
||||
|
||||
async saveDossier() {
|
||||
const dossier: Dossier = this._formToObject();
|
||||
|
||||
const foundDossier = this._appStateService.allDossiers.find(p => p.dossierId === dossier.dossierId);
|
||||
if (foundDossier) {
|
||||
dossier.memberIds = foundDossier.memberIds;
|
||||
}
|
||||
|
||||
const savedDossier = await this._appStateService.createOrUpdateDossier(dossier);
|
||||
const savedDossier = await this._appStateService.createOrUpdateDossier(this._formToObject());
|
||||
if (savedDossier) {
|
||||
this.dialogRef.close({ dossier: savedDossier });
|
||||
}
|
||||
}
|
||||
|
||||
async saveDossierAndAddMembers() {
|
||||
const dossier: Dossier = this._formToObject();
|
||||
const dossier: IDossier = this._formToObject();
|
||||
const savedDossier = await this._appStateService.createOrUpdateDossier(dossier);
|
||||
if (savedDossier) {
|
||||
this.dialogRef.close({ addMembers: true, dossier: savedDossier });
|
||||
@ -124,7 +116,7 @@ export class AddDossierDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
private _formToObject(): Dossier {
|
||||
private _formToObject(): IDossier {
|
||||
return {
|
||||
dossierName: this.dossierForm.get('dossierName').value,
|
||||
description: this.dossierForm.get('description').value,
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { StatusControllerService } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FilesService } from '../../services/files.service';
|
||||
|
||||
class DialogData {
|
||||
mode: 'approver' | 'reviewer';
|
||||
dossier?: DossierWrapper;
|
||||
files?: FileStatusWrapper[];
|
||||
dossier?: Dossier;
|
||||
files?: File[];
|
||||
ignoreChanged?: boolean;
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ export class AssignReviewerApproverDialogComponent {
|
||||
readonly userService: UserService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _dialogRef: MatDialogRef<AssignReviewerApproverDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: DialogData
|
||||
) {
|
||||
@ -69,16 +69,17 @@ export class AssignReviewerApproverDialogComponent {
|
||||
const selectedUser = this.selectedSingleUser;
|
||||
|
||||
if (this.data.mode === 'reviewer') {
|
||||
await this._statusControllerService
|
||||
.setFileReviewerForList(
|
||||
console.log('assign reviewer');
|
||||
await this._filesService
|
||||
.setReviewerFor(
|
||||
this.data.files.map(f => f.fileId),
|
||||
this._appStateService.activeDossierId,
|
||||
selectedUser
|
||||
)
|
||||
.toPromise();
|
||||
} else {
|
||||
await this._statusControllerService
|
||||
.setStatusUnderApprovalForList(
|
||||
await this._filesService
|
||||
.setUnderApprovalFor(
|
||||
this.data.files.map(f => f.fileId),
|
||||
selectedUser,
|
||||
this._appStateService.activeDossierId
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { FileAttributeConfig, FileAttributesControllerService, FileStatus } from '@redaction/red-ui-http';
|
||||
import { FileAttributesControllerService, IFile, IFileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
|
||||
@Component({
|
||||
templateUrl: './document-info-dialog.component.html',
|
||||
@ -11,17 +11,17 @@ import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
})
|
||||
export class DocumentInfoDialogComponent implements OnInit {
|
||||
documentInfoForm: FormGroup;
|
||||
file: FileStatus;
|
||||
attributes: FileAttributeConfig[];
|
||||
file: IFile;
|
||||
attributes: IFileAttributeConfig[];
|
||||
|
||||
private _dossier: DossierWrapper;
|
||||
private _dossier: Dossier;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _fileAttributesService: FileAttributesControllerService,
|
||||
public dialogRef: MatDialogRef<DocumentInfoDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: FileStatus
|
||||
@Inject(MAT_DIALOG_DATA) public data: IFile
|
||||
) {
|
||||
this.file = this.data;
|
||||
this._dossier = this._appStateService.getDossierById(this.file.dossierId);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
|
||||
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Dossier } from '../../../../../state/model/dossier';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { CircleButtonTypes, IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
@ -18,8 +17,8 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Output() updateDossier = new EventEmitter<any>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly updateDossier = new EventEmitter();
|
||||
customAttributes: DossierAttributeWithValue[] = [];
|
||||
imageAttributes: DossierAttributeWithValue[] = [];
|
||||
attributes: DossierAttributeWithValue[] = [];
|
||||
@ -28,7 +27,6 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
|
||||
@ViewChildren('fileInput') private _fileInputs: QueryList<ElementRef>;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _dossierAttributesService: DossierAttributesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
@ -50,11 +48,11 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return !this._permissionsService.isOwner(this.dossierWrapper);
|
||||
return !this._permissionsService.isOwner(this.dossier);
|
||||
}
|
||||
|
||||
get canEdit(): boolean {
|
||||
return this._permissionsService.isOwner(this.dossierWrapper);
|
||||
return this._permissionsService.isOwner(this.dossier);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -70,7 +68,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
|
||||
dossierAttributeId: attr.id,
|
||||
value: this.currentAttrValue(attr)
|
||||
}));
|
||||
await this._dossierAttributesService.setValues(this.dossierWrapper, dossierAttributeList);
|
||||
await this._dossierAttributesService.setValues(this.dossier, dossierAttributeList);
|
||||
await this._loadAttributes();
|
||||
this.updateDossier.emit();
|
||||
this._loadingService.stop();
|
||||
@ -136,7 +134,7 @@ export class EditDossierAttributesComponent implements EditDossierSectionInterfa
|
||||
}
|
||||
|
||||
private async _loadAttributes() {
|
||||
this.attributes = await this._dossierAttributesService.getValues(this.dossierWrapper);
|
||||
this.attributes = await this._dossierAttributesService.getValues(this.dossier);
|
||||
this.customAttributes = this.attributes.filter(attr => !this.isImage(attr));
|
||||
this.imageAttributes = this.attributes.filter(attr => this.isImage(attr));
|
||||
}
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { Component, EventEmitter, forwardRef, Injector, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
DefaultListingServices,
|
||||
Listable,
|
||||
IListable,
|
||||
ListingComponent,
|
||||
LoadingService,
|
||||
SortingOrders,
|
||||
TableColumnConfig
|
||||
} from '@iqser/common-ui';
|
||||
import { FileManagementControllerService, FileStatus, StatusControllerService } from '@redaction/red-ui-http';
|
||||
import { FileManagementControllerService, IFile } from '@redaction/red-ui-http';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import * as moment from 'moment';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
@ -20,8 +20,9 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FilesService } from '../../../services/files.service';
|
||||
|
||||
interface FileListItem extends FileStatus, Listable {
|
||||
interface FileListItem extends IFile, IListable {
|
||||
readonly canRestore: boolean;
|
||||
readonly restoreDate: string;
|
||||
}
|
||||
@ -36,8 +37,8 @@ interface FileListItem extends FileStatus, Listable {
|
||||
]
|
||||
})
|
||||
export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileListItem> implements EditDossierSectionInterface, OnInit {
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Output() updateDossier = new EventEmitter<any>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly updateDossier = new EventEmitter();
|
||||
readonly changed = false;
|
||||
readonly canRestoreSelected$ = this._canRestoreSelected$;
|
||||
disabled: boolean;
|
||||
@ -49,13 +50,12 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
|
||||
@ViewChild('deletedDateTemplate', { static: true }) deletedDateTemplate: TemplateRef<never>;
|
||||
@ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'fileId';
|
||||
|
||||
constructor(
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _statusController: StatusControllerService,
|
||||
private readonly _fileManagementController: FileManagementControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _dialogService: DossiersDialogService
|
||||
@ -91,7 +91,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
async ngOnInit() {
|
||||
this._configureTableColumns();
|
||||
this._loadingService.start();
|
||||
const files = await this._statusController.getDeletedFileStatus(this.dossierWrapper.dossierId).toPromise();
|
||||
const files = await this._filesService.getDeletedFilesFor(this.dossier.id).toPromise();
|
||||
this.entitiesService.setEntities(this._toListItems(files));
|
||||
this.sortingService.setSortingOption({
|
||||
column: 'softDeleted',
|
||||
@ -138,7 +138,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
|
||||
private async _restore(files: FileListItem[]): Promise<void> {
|
||||
const fileIds = files.map(f => f.fileId);
|
||||
await this._fileManagementController.restoreFiles(fileIds, this.dossierWrapper.dossierId).toPromise();
|
||||
await this._fileManagementController.restoreFiles(fileIds, this.dossier.id).toPromise();
|
||||
this._removeFromList(fileIds);
|
||||
await this._appStateService.reloadActiveDossierFiles();
|
||||
this.updateDossier.emit();
|
||||
@ -146,7 +146,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
|
||||
private async _hardDelete(files: FileListItem[]) {
|
||||
const fileIds = files.map(f => f.fileId);
|
||||
await this._fileManagementController.hardDeleteFile(this.dossierWrapper.dossierId, fileIds).toPromise();
|
||||
await this._fileManagementController.hardDeleteFile(this.dossier.id, fileIds).toPromise();
|
||||
this._removeFromList(fileIds);
|
||||
this.updateDossier.emit();
|
||||
}
|
||||
@ -157,16 +157,17 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
this.entitiesService.setSelected([]);
|
||||
}
|
||||
|
||||
private _toListItems(files: FileStatus[]): FileListItem[] {
|
||||
private _toListItems(files: IFile[]): FileListItem[] {
|
||||
return files.map(file => this._toListItem(file));
|
||||
}
|
||||
|
||||
private _toListItem(file: FileStatus): FileListItem {
|
||||
private _toListItem(file: IFile): FileListItem {
|
||||
const restoreDate = this._getRestoreDate(file.softDeleted);
|
||||
return {
|
||||
id: file.fileId,
|
||||
...file,
|
||||
restoreDate,
|
||||
searchKey: file.filename,
|
||||
canRestore: this._canRestoreFile(restoreDate)
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<div class="header-wrapper">
|
||||
<div class="heading">
|
||||
<div>{{ dossierWrapper.type?.label }}</div>
|
||||
<div>{{ dossier.type?.label }}</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:entries"></mat-icon>
|
||||
{{ 'edit-dossier-dialog.dictionary.entries' | translate: { length: (dossierWrapper.type?.entries || []).length } }}
|
||||
{{ 'edit-dossier-dialog.dictionary.entries' | translate: { length: (dossier.type?.entries || []).length } }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -19,13 +19,13 @@
|
||||
[placeholder]="'edit-dossier-dialog.dictionary.display-name.placeholder' | translate"
|
||||
[saveTooltip]="'edit-dossier-dialog.dictionary.display-name.save' | translate"
|
||||
[showPreview]="false"
|
||||
[value]="dossierWrapper.type?.label"
|
||||
[value]="dossier.type?.label"
|
||||
></iqser-editable-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<redaction-dictionary-manager
|
||||
[canEdit]="canEdit"
|
||||
[initialEntries]="dossierWrapper.type?.entries || []"
|
||||
[initialEntries]="dossier.type?.entries || []"
|
||||
[withFloatingActions]="false"
|
||||
></redaction-dictionary-manager>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
||||
import { DictionarySaveService } from '@shared/services/dictionary-save.service';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { CircleButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||
import { Dictionary, DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { IDictionary } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-edit-dossier-dictionary',
|
||||
@ -15,8 +15,8 @@ import { Dictionary, DictionaryControllerService } from '@redaction/red-ui-http'
|
||||
styleUrls: ['./edit-dossier-dictionary.component.scss']
|
||||
})
|
||||
export class EditDossierDictionaryComponent implements EditDossierSectionInterface, OnInit {
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Output() updateDossier: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly updateDossier = new EventEmitter();
|
||||
canEdit = false;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
@ -24,13 +24,12 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dictionarySaveService: DictionarySaveService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _formBuilder: FormBuilder
|
||||
) {
|
||||
this.canEdit = this._permissionsService.isDossierMember(this.dossierWrapper);
|
||||
this.canEdit = this._permissionsService.isDossierMember(this.dossier);
|
||||
}
|
||||
|
||||
get changed() {
|
||||
@ -43,31 +42,31 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
|
||||
|
||||
async ngOnInit() {
|
||||
this._loadingService.start();
|
||||
await this._appStateService.updateDossierDictionary(this.dossierWrapper.dossierTemplateId, this.dossierWrapper.dossierId);
|
||||
await this._appStateService.updateDossierDictionary(this.dossier.dossierTemplateId, this.dossier.id);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
async updateDisplayName(label: string) {
|
||||
const typeValue: Dictionary = { ...this.dossierWrapper.type, label };
|
||||
await this._dictionaryControllerService
|
||||
.updateType(typeValue, this.dossierWrapper.dossierTemplateId, 'dossier_redaction', this.dossierWrapper.dossierId)
|
||||
const typeValue: IDictionary = { ...this.dossier.type, label };
|
||||
await this._dictionaryService
|
||||
.updateType(typeValue, this.dossier.dossierTemplateId, 'dossier_redaction', this.dossier.id)
|
||||
.toPromise();
|
||||
await this._appStateService.updateDossierDictionary(this.dossierWrapper.dossierTemplateId, this.dossierWrapper.dossierId);
|
||||
await this._appStateService.updateDossierDictionary(this.dossier.dossierTemplateId, this.dossier.id);
|
||||
this.updateDossier.emit();
|
||||
}
|
||||
|
||||
async save() {
|
||||
await this._dictionarySaveService
|
||||
await this._dictionaryService
|
||||
.saveEntries(
|
||||
this._dictionaryManager.currentEntries,
|
||||
this._dictionaryManager.initialEntries,
|
||||
this.dossierWrapper.dossierTemplateId,
|
||||
this.dossier.dossierTemplateId,
|
||||
'dossier_redaction',
|
||||
this.dossierWrapper.dossierId,
|
||||
this.dossier.id,
|
||||
false
|
||||
)
|
||||
.toPromise();
|
||||
await this._appStateService.updateDossierDictionary(this.dossierWrapper.dossierTemplateId, this.dossierWrapper.dossierId);
|
||||
await this._appStateService.updateDossierDictionary(this.dossier.dossierTemplateId, this.dossier.id);
|
||||
this.updateDossier.emit();
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Dossier, DossierTemplateModel, ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http';
|
||||
import { DownloadFileType, ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { downloadTypesTranslations } from '../../../../../translations/download-types-translations';
|
||||
|
||||
@ -13,16 +13,14 @@ import { downloadTypesTranslations } from '../../../../../translations/download-
|
||||
})
|
||||
export class EditDossierDownloadPackageComponent implements OnInit, EditDossierSectionInterface {
|
||||
dossierForm: FormGroup;
|
||||
downloadTypesEnum: Dossier.DownloadFileTypesEnum[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
downloadTypes: { key: Dossier.DownloadFileTypesEnum; label: string }[] = this.downloadTypesEnum.map(type => ({
|
||||
downloadTypes: { key: DownloadFileType; label: string }[] = ['ORIGINAL', 'PREVIEW', 'REDACTED'].map((type: DownloadFileType) => ({
|
||||
key: type,
|
||||
label: downloadTypesTranslations[type]
|
||||
}));
|
||||
dossierTemplates: DossierTemplateModel[];
|
||||
availableReportTypes: ReportTemplate[] = [];
|
||||
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Output() updateDossier = new EventEmitter<any>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly updateDossier = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -41,11 +39,11 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
|
||||
get changed() {
|
||||
if (this.dossierForm) {
|
||||
for (const key of Object.keys(this.dossierForm.getRawValue())) {
|
||||
if (this.dossierWrapper[key].length !== this.dossierForm.get(key).value.length) {
|
||||
if (this.dossier[key].length !== this.dossierForm.get(key).value.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const originalItems = [...this.dossierWrapper[key]].sort();
|
||||
const originalItems = [...this.dossier[key]].sort();
|
||||
const newItems = [...this.dossierForm.get(key).value].sort();
|
||||
|
||||
for (let idx = 0; idx < originalItems.length; ++idx) {
|
||||
@ -67,12 +65,12 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
|
||||
|
||||
async ngOnInit() {
|
||||
this.availableReportTypes =
|
||||
(await this._reportTemplateController.getAvailableReportTemplates(this.dossierWrapper.dossierTemplateId).toPromise()) || [];
|
||||
(await this._reportTemplateController.getAvailableReportTemplates(this.dossier.dossierTemplateId).toPromise()) || [];
|
||||
|
||||
this.dossierForm = this._formBuilder.group(
|
||||
{
|
||||
reportTemplateIds: [this.dossierWrapper.reportTemplateIds],
|
||||
downloadFileTypes: [this.dossierWrapper.downloadFileTypes]
|
||||
reportTemplateIds: [this.dossier.reportTemplateIds],
|
||||
downloadFileTypes: [this.dossier.downloadFileTypes]
|
||||
},
|
||||
{
|
||||
validators: control =>
|
||||
@ -85,7 +83,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
|
||||
|
||||
async save() {
|
||||
const dossier = {
|
||||
...this.dossierWrapper,
|
||||
...this.dossier,
|
||||
downloadFileTypes: this.dossierForm.get('downloadFileTypes').value,
|
||||
reportTemplateIds: this.dossierForm.get('reportTemplateIds').value
|
||||
};
|
||||
@ -95,8 +93,8 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
|
||||
|
||||
revert() {
|
||||
this.dossierForm.reset({
|
||||
downloadFileTypes: this.dossierWrapper.downloadFileTypes,
|
||||
reportTemplateIds: this.dossierWrapper.reportTemplateIds
|
||||
downloadFileTypes: this.dossier.downloadFileTypes,
|
||||
reportTemplateIds: this.dossier.reportTemplateIds
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<section class="dialog">
|
||||
<div class="dialog-header heading-l">
|
||||
{{ 'edit-dossier-dialog.header' | translate: { dossierName: dossierWrapper.dossierName } }}
|
||||
{{ 'edit-dossier-dialog.header' | translate: { dossierName: dossier.dossierName } }}
|
||||
</div>
|
||||
|
||||
<div class="dialog-content">
|
||||
@ -22,37 +22,37 @@
|
||||
<redaction-edit-dossier-general-info
|
||||
(updateDossier)="updatedDossier()"
|
||||
*ngIf="activeNav === 'dossierInfo'"
|
||||
[dossierWrapper]="dossierWrapper"
|
||||
[dossier]="dossier"
|
||||
></redaction-edit-dossier-general-info>
|
||||
|
||||
<redaction-edit-dossier-download-package
|
||||
(updateDossier)="updatedDossier()"
|
||||
*ngIf="activeNav === 'downloadPackage'"
|
||||
[dossierWrapper]="dossierWrapper"
|
||||
[dossier]="dossier"
|
||||
></redaction-edit-dossier-download-package>
|
||||
|
||||
<redaction-edit-dossier-dictionary
|
||||
(updateDossier)="updatedDossier()"
|
||||
*ngIf="activeNav === 'dossierDictionary'"
|
||||
[dossierWrapper]="dossierWrapper"
|
||||
[dossier]="dossier"
|
||||
></redaction-edit-dossier-dictionary>
|
||||
|
||||
<redaction-edit-dossier-team-members
|
||||
(updateDossier)="updatedDossier()"
|
||||
*ngIf="activeNav === 'members'"
|
||||
[dossierWrapper]="dossierWrapper"
|
||||
[dossier]="dossier"
|
||||
></redaction-edit-dossier-team-members>
|
||||
|
||||
<redaction-edit-dossier-attributes
|
||||
(updateDossier)="updatedDossier()"
|
||||
*ngIf="activeNav === 'dossierAttributes'"
|
||||
[dossierWrapper]="dossierWrapper"
|
||||
[dossier]="dossier"
|
||||
></redaction-edit-dossier-attributes>
|
||||
|
||||
<redaction-edit-dossier-deleted-documents
|
||||
(updateDossier)="updatedDossier()"
|
||||
*ngIf="activeNav === 'deletedDocuments'"
|
||||
[dossierWrapper]="dossierWrapper"
|
||||
[dossier]="dossier"
|
||||
></redaction-edit-dossier-deleted-documents>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectorRef, Component, Inject, ViewChild } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { EditDossierGeneralInfoComponent } from './general-info/edit-dossier-general-info.component';
|
||||
import { EditDossierDownloadPackageComponent } from './download-package/edit-dossier-download-package.component';
|
||||
import { EditDossierSectionInterface } from './edit-dossier-section.interface';
|
||||
@ -22,7 +22,7 @@ type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'member
|
||||
export class EditDossierDialogComponent {
|
||||
readonly navItems: { key: Section; title?: string; sideNavTitle?: string }[];
|
||||
activeNav: Section;
|
||||
dossierWrapper: DossierWrapper;
|
||||
dossier: Dossier;
|
||||
|
||||
@ViewChild(EditDossierGeneralInfoComponent) generalInfoComponent: EditDossierGeneralInfoComponent;
|
||||
@ViewChild(EditDossierDownloadPackageComponent) downloadPackageComponent: EditDossierDownloadPackageComponent;
|
||||
@ -37,7 +37,7 @@ export class EditDossierDialogComponent {
|
||||
private readonly _changeRef: ChangeDetectorRef,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
private readonly _data: {
|
||||
dossierWrapper: DossierWrapper;
|
||||
dossier: Dossier;
|
||||
afterSave: Function;
|
||||
section?: Section;
|
||||
}
|
||||
@ -73,7 +73,7 @@ export class EditDossierDialogComponent {
|
||||
}
|
||||
];
|
||||
|
||||
this.dossierWrapper = _data.dossierWrapper;
|
||||
this.dossier = _data.dossier;
|
||||
this.activeNav = _data.section || 'dossierInfo';
|
||||
}
|
||||
|
||||
@ -105,8 +105,8 @@ export class EditDossierDialogComponent {
|
||||
}
|
||||
|
||||
updatedDossier() {
|
||||
this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this.dossierWrapper.dossierName } });
|
||||
this.dossierWrapper = this._appStateService.getDossierById(this.dossierWrapper.dossierId);
|
||||
this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this.dossier.dossierName } });
|
||||
this.dossier = this._appStateService.getDossierById(this.dossier.id);
|
||||
this._changeRef.detectChanges();
|
||||
this.afterSave();
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
<div class="dialog-actions">
|
||||
<iqser-icon-button
|
||||
(action)="deleteDossier()"
|
||||
*ngIf="permissionsService.canDeleteDossier(dossierWrapper)"
|
||||
*ngIf="permissionsService.canDeleteDossier(dossier)"
|
||||
[label]="'dossier-listing.delete.action' | translate"
|
||||
[type]="iconButtonTypes.dark"
|
||||
icon="red:trash"
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { DossierTemplateModel } from '@redaction/red-ui-http';
|
||||
import { IDossierTemplate } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import * as moment from 'moment';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { EditDossierDialogComponent } from '../edit-dossier-dialog.component';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { IconButtonTypes, 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';
|
||||
import { ConfirmationDialogInput, TitleColors } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-edit-dossier-general-info',
|
||||
@ -25,10 +24,10 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
|
||||
dossierForm: FormGroup;
|
||||
hasDueDate: boolean;
|
||||
dossierTemplates: DossierTemplateModel[];
|
||||
dossierTemplates: IDossierTemplate[];
|
||||
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Output() updateDossier = new EventEmitter<any>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly updateDossier = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
@ -43,13 +42,13 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
get changed() {
|
||||
for (const key of Object.keys(this.dossierForm.getRawValue())) {
|
||||
if (key === 'dueDate') {
|
||||
if (this.hasDueDate !== !!this.dossierWrapper.dueDate) {
|
||||
if (this.hasDueDate !== !!this.dossier.dueDate) {
|
||||
return true;
|
||||
}
|
||||
if (this.hasDueDate && !moment(this.dossierWrapper.dueDate).isSame(moment(this.dossierForm.get(key).value))) {
|
||||
if (this.hasDueDate && !moment(this.dossier.dueDate).isSame(moment(this.dossierForm.get(key).value))) {
|
||||
return true;
|
||||
}
|
||||
} else if (this.dossierWrapper[key] !== this.dossierForm.get(key).value) {
|
||||
} else if (this.dossier[key] !== this.dossierForm.get(key).value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -68,34 +67,34 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
ngOnInit() {
|
||||
this._filterInvalidDossierTemplates();
|
||||
this.dossierForm = this._formBuilder.group({
|
||||
dossierName: [this.dossierWrapper.dossierName, Validators.required],
|
||||
dossierName: [this.dossier.dossierName, Validators.required],
|
||||
dossierTemplateId: [
|
||||
{
|
||||
value: this.dossierWrapper.dossierTemplateId,
|
||||
disabled: this.dossierWrapper.hasFiles
|
||||
value: this.dossier.dossierTemplateId,
|
||||
disabled: this.dossier.hasFiles
|
||||
},
|
||||
Validators.required
|
||||
],
|
||||
description: [this.dossierWrapper.description],
|
||||
dueDate: [this.dossierWrapper.dueDate],
|
||||
watermarkEnabled: [this.dossierWrapper.watermarkEnabled]
|
||||
description: [this.dossier.description],
|
||||
dueDate: [this.dossier.dueDate],
|
||||
watermarkEnabled: [this.dossier.watermarkEnabled]
|
||||
});
|
||||
this.hasDueDate = !!this.dossierWrapper.dueDate;
|
||||
this.hasDueDate = !!this.dossier.dueDate;
|
||||
}
|
||||
|
||||
revert() {
|
||||
this.dossierForm.reset({
|
||||
dossierName: this.dossierWrapper.dossierName,
|
||||
dossierTemplateId: this.dossierWrapper.dossierTemplateId,
|
||||
description: this.dossierWrapper.description,
|
||||
watermarkEnabled: this.dossierWrapper.watermarkEnabled,
|
||||
dueDate: this.dossierWrapper.dueDate
|
||||
dossierName: this.dossier.dossierName,
|
||||
dossierTemplateId: this.dossier.dossierTemplateId,
|
||||
description: this.dossier.description,
|
||||
watermarkEnabled: this.dossier.watermarkEnabled,
|
||||
dueDate: this.dossier.dueDate
|
||||
});
|
||||
}
|
||||
|
||||
async save() {
|
||||
const dossier = {
|
||||
...this.dossierWrapper,
|
||||
...this.dossier,
|
||||
dossierName: this.dossierForm.get('dossierName').value,
|
||||
description: this.dossierForm.get('description').value,
|
||||
watermarkEnabled: this.dossierForm.get('watermarkEnabled').value,
|
||||
@ -116,12 +115,12 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
requireInput: true,
|
||||
denyText: _('confirmation-dialog.delete-dossier.deny-text'),
|
||||
translateParams: {
|
||||
dossierName: this.dossierWrapper.dossierName,
|
||||
dossierName: this.dossier.dossierName,
|
||||
dossiersCount: 1
|
||||
}
|
||||
});
|
||||
this._dialogService.openDialog('confirm', null, data, async () => {
|
||||
await this._appStateService.deleteDossier(this.dossierWrapper);
|
||||
await this._appStateService.deleteDossier(this.dossier);
|
||||
this._editDossierDialogRef.componentInstance.afterSave();
|
||||
this._editDossierDialogRef.close();
|
||||
this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted());
|
||||
@ -129,12 +128,12 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
}
|
||||
|
||||
private _notifyDossierDeleted() {
|
||||
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: { dossierName: this.dossierWrapper.dossierName } });
|
||||
this._toaster.success(_('edit-dossier-dialog.delete-successful'), { params: { dossierName: this.dossier.dossierName } });
|
||||
}
|
||||
|
||||
private _filterInvalidDossierTemplates() {
|
||||
this.dossierTemplates = this._appStateService.dossierTemplates.filter(r => {
|
||||
if (this.dossierWrapper?.dossierTemplateId === r.dossierTemplateId) {
|
||||
if (this.dossier?.dossierTemplateId === r.dossierTemplateId) {
|
||||
return true;
|
||||
}
|
||||
const notYetValid = !!r.validFrom && moment(r.validFrom).isAfter(moment());
|
||||
|
||||
@ -1 +1 @@
|
||||
<redaction-team-members-manager (save)="updateDossier.emit()" [dossierWrapper]="dossierWrapper"></redaction-team-members-manager>
|
||||
<redaction-team-members-manager (save)="updateDossier.emit()" [dossier]="dossier"></redaction-team-members-manager>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { TeamMembersManagerComponent } from '../../../components/team-members-manager/team-members-manager.component';
|
||||
import { UserService } from '@services/user.service';
|
||||
@ -13,12 +12,12 @@ import { UserService } from '@services/user.service';
|
||||
export class EditDossierTeamMembersComponent implements EditDossierSectionInterface {
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@Input() dossierWrapper: DossierWrapper;
|
||||
@Output() updateDossier = new EventEmitter<any>();
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly updateDossier = new EventEmitter();
|
||||
|
||||
@ViewChild(TeamMembersManagerComponent) managerComponent: TeamMembersManagerComponent;
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService, private readonly _userService: UserService) {}
|
||||
constructor(private readonly _userService: UserService) {}
|
||||
|
||||
get changed() {
|
||||
return this.managerComponent.changed;
|
||||
|
||||
@ -10,7 +10,7 @@ import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry
|
||||
import { ManualAnnotationService } from '../../services/manual-annotation.service';
|
||||
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { TypeValue } from '@models/file/type-value';
|
||||
|
||||
export interface LegalBasisOption {
|
||||
label?: string;
|
||||
@ -30,7 +30,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
|
||||
isDictionaryRequest: boolean;
|
||||
isFalsePositiveRequest: boolean;
|
||||
|
||||
redactionDictionaries: TypeValueWrapper[] = [];
|
||||
redactionDictionaries: TypeValue[] = [];
|
||||
legalOptions: LegalBasisOption[] = [];
|
||||
|
||||
constructor(
|
||||
|
||||
@ -33,13 +33,13 @@
|
||||
|
||||
<ng-template #nameTemplate let-dossier="entity">
|
||||
<div class="cell">
|
||||
<div [matTooltip]="dossier.dossierName" class="table-item-title heading" matTooltipPosition="above">
|
||||
<div [matTooltip]="dossier.dossierName" class="table-item-title heading mb-6" matTooltipPosition="above">
|
||||
{{ dossier.dossierName }}
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div class="small-label stats-subtitle mb-6">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
{{ dossier.dossierTemplateName }}
|
||||
{{ getDossierTemplateNameFor(dossier.dossierTemplateId) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
@ -53,7 +53,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:user"></mat-icon>
|
||||
{{ dossier.memberCount }}
|
||||
{{ dossier.memberIds.length }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { AfterViewInit, Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { Dossier } from '@redaction/red-ui-http';
|
||||
import { DossierStatuses } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { groupBy } from '@utils/functions';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { timer } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { TranslateChartService } from '@services/translate-chart.service';
|
||||
@ -29,7 +29,7 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierListingScreenComponent) }]
|
||||
})
|
||||
export class DossierListingScreenComponent
|
||||
extends ListingComponent<DossierWrapper>
|
||||
extends ListingComponent<Dossier>
|
||||
implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach
|
||||
{
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
@ -43,21 +43,20 @@ export class DossierListingScreenComponent
|
||||
type: 'primary'
|
||||
}
|
||||
];
|
||||
tableColumnConfigs: TableColumnConfig<DossierWrapper>[];
|
||||
tableColumnConfigs: TableColumnConfig<Dossier>[];
|
||||
dossiersChartData: DoughnutChartConfig[] = [];
|
||||
documentsChartData: DoughnutChartConfig[] = [];
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<never>;
|
||||
@ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<never>;
|
||||
@ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef<never>;
|
||||
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'dossierName';
|
||||
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<unknown>;
|
||||
private _lastScrolledIndex: number;
|
||||
@ViewChild('needsWorkFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
static: true
|
||||
})
|
||||
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
|
||||
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<DossierWrapper>;
|
||||
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
|
||||
|
||||
constructor(
|
||||
private readonly _router: Router,
|
||||
@ -76,14 +75,16 @@ export class DossierListingScreenComponent
|
||||
}
|
||||
|
||||
private get _activeDossiersCount(): number {
|
||||
return this.entitiesService.all.filter(p => p.status === Dossier.StatusEnum.ACTIVE).length;
|
||||
return this.entitiesService.all.filter(p => p.status === DossierStatuses.ACTIVE).length;
|
||||
}
|
||||
|
||||
private get _inactiveDossiersCount(): number {
|
||||
return this.entitiesService.all.length - this._activeDossiersCount;
|
||||
}
|
||||
|
||||
routerLinkFn = (dossier: DossierWrapper) => ['/main/dossiers/' + dossier.dossierId];
|
||||
getDossierTemplateNameFor(dossierTemplateId: string): string {
|
||||
return this._appStateService.getDossierTemplateById(dossierTemplateId).name;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._configureTableColumns();
|
||||
@ -120,10 +121,10 @@ export class DossierListingScreenComponent
|
||||
|
||||
openAddDossierDialog(): void {
|
||||
this._dialogService.openDialog('addDossier', null, null, async addResponse => {
|
||||
await this._router.navigate([`/main/dossiers/${addResponse.dossier.dossierId}`]);
|
||||
await this._router.navigate([`/main/dossiers/${addResponse.dossier.id}`]);
|
||||
if (addResponse.addMembers) {
|
||||
this._dialogService.openDialog('editDossier', null, {
|
||||
dossierWrapper: addResponse.dossier,
|
||||
dossier: addResponse.dossier,
|
||||
section: 'members'
|
||||
});
|
||||
}
|
||||
@ -140,12 +141,12 @@ export class DossierListingScreenComponent
|
||||
const groups = groupBy(this._appStateService.aggregatedFiles, 'status');
|
||||
this.documentsChartData = [];
|
||||
|
||||
for (const key of Object.keys(groups)) {
|
||||
for (const status of Object.keys(groups)) {
|
||||
this.documentsChartData.push({
|
||||
value: groups[key].length,
|
||||
color: key,
|
||||
label: fileStatusTranslations[key],
|
||||
key: key
|
||||
value: groups[status].length,
|
||||
color: status,
|
||||
label: fileStatusTranslations[status],
|
||||
key: status
|
||||
});
|
||||
}
|
||||
this.documentsChartData.sort(StatusSorter.byStatus);
|
||||
@ -156,7 +157,7 @@ export class DossierListingScreenComponent
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('dossier-listing.table-col-names.name'),
|
||||
sortByKey: 'dossierName',
|
||||
sortByKey: 'searchKey',
|
||||
template: this.nameTemplate,
|
||||
width: '2fr'
|
||||
},
|
||||
@ -214,23 +215,29 @@ export class DossierListingScreenComponent
|
||||
allDistinctDossierTemplates.add(entry.dossierTemplateId);
|
||||
});
|
||||
|
||||
const statusFilters = [...allDistinctFileStatus].map<NestedFilter>(status => ({
|
||||
key: status,
|
||||
label: this._translateService.instant(fileStatusTranslations[status])
|
||||
}));
|
||||
const statusFilters = [...allDistinctFileStatus].map(
|
||||
status =>
|
||||
new NestedFilter({
|
||||
id: status,
|
||||
label: this._translateService.instant(fileStatusTranslations[status])
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'statusFilters',
|
||||
label: this._translateService.instant('filters.status'),
|
||||
icon: 'red:status',
|
||||
filters: statusFilters.sort(StatusSorter.byStatus),
|
||||
filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
|
||||
checker: dossierStatusChecker
|
||||
});
|
||||
|
||||
const peopleFilters = [...allDistinctPeople].map<NestedFilter>(userId => ({
|
||||
key: userId,
|
||||
label: this._userService.getNameForId(userId)
|
||||
}));
|
||||
const peopleFilters = [...allDistinctPeople].map(
|
||||
userId =>
|
||||
new NestedFilter({
|
||||
id: userId,
|
||||
label: this._userService.getNameForId(userId)
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'peopleFilters',
|
||||
@ -240,25 +247,31 @@ export class DossierListingScreenComponent
|
||||
checker: dossierMemberChecker
|
||||
});
|
||||
|
||||
const needsWorkFilters = [...allDistinctNeedsWork].map<NestedFilter>(type => ({
|
||||
key: type,
|
||||
label: workloadTranslations[type]
|
||||
}));
|
||||
const needsWorkFilters = [...allDistinctNeedsWork].map(
|
||||
type =>
|
||||
new NestedFilter({
|
||||
id: type,
|
||||
label: workloadTranslations[type]
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'needsWorkFilters',
|
||||
label: this._translateService.instant('filters.needs-work'),
|
||||
icon: 'red:needs-work',
|
||||
filterTemplate: this._needsWorkFilterTemplate,
|
||||
filters: needsWorkFilters.sort(RedactionFilterSorter.byKey),
|
||||
filters: needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id]),
|
||||
checker: annotationFilterChecker,
|
||||
matchAll: true
|
||||
});
|
||||
|
||||
const dossierTemplateFilters = [...allDistinctDossierTemplates].map<NestedFilter>(id => ({
|
||||
key: id,
|
||||
label: this._appStateService.getDossierTemplateById(id).name
|
||||
}));
|
||||
const dossierTemplateFilters = [...allDistinctDossierTemplates].map(
|
||||
id =>
|
||||
new NestedFilter({
|
||||
id: id,
|
||||
label: this._appStateService.getDossierTemplateById(id).name
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'dossierTemplateFilters',
|
||||
@ -273,13 +286,16 @@ export class DossierListingScreenComponent
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'quickFilters',
|
||||
filters: quickFilters,
|
||||
checker: (dw: DossierWrapper) => quickFilters.reduce((acc, f) => acc || (f.checked && f.checker(dw)), false)
|
||||
checker: (dw: Dossier) => 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
|
||||
}));
|
||||
const dossierFilters = this.entitiesService.all.map(
|
||||
dossier =>
|
||||
new NestedFilter({
|
||||
id: dossier.dossierName,
|
||||
label: dossier.dossierName
|
||||
})
|
||||
);
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'dossierNameFilter',
|
||||
label: this._translateService.instant('dossier-listing.filters.label'),
|
||||
@ -290,30 +306,30 @@ export class DossierListingScreenComponent
|
||||
});
|
||||
}
|
||||
|
||||
private _createQuickFilters() {
|
||||
private _createQuickFilters(): NestedFilter[] {
|
||||
const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers');
|
||||
const filters: NestedFilter[] = [
|
||||
{
|
||||
key: 'my-dossiers',
|
||||
id: 'my-dossiers',
|
||||
label: myDossiersLabel,
|
||||
checker: (dw: DossierWrapper) => dw.ownerId === this.currentUser.id
|
||||
checker: (dw: Dossier) => dw.ownerId === this.currentUser.id
|
||||
},
|
||||
{
|
||||
key: 'to-approve',
|
||||
id: 'to-approve',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.to-approve'),
|
||||
checker: (dw: DossierWrapper) => dw.approverIds.includes(this.currentUser.id)
|
||||
checker: (dw: Dossier) => dw.approverIds.includes(this.currentUser.id)
|
||||
},
|
||||
{
|
||||
key: 'to-review',
|
||||
id: 'to-review',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.to-review'),
|
||||
checker: (dw: DossierWrapper) => dw.memberIds.includes(this.currentUser.id)
|
||||
checker: (dw: Dossier) => dw.memberIds.includes(this.currentUser.id)
|
||||
},
|
||||
{
|
||||
key: 'other',
|
||||
id: 'other',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.other'),
|
||||
checker: (dw: DossierWrapper) => !dw.memberIds.includes(this.currentUser.id)
|
||||
checker: (dw: Dossier) => !dw.memberIds.includes(this.currentUser.id)
|
||||
}
|
||||
];
|
||||
].map(filter => new NestedFilter(filter));
|
||||
|
||||
return filters.filter(f => f.label === myDossiersLabel || this._userPreferenceService.areDevFeaturesEnabled);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
[actionConfigs]="actionConfigs"
|
||||
[fileAttributeConfigs]="fileAttributeConfigs"
|
||||
[showCloseButton]="true"
|
||||
[viewModeSelection]="viewModeSelection"
|
||||
>
|
||||
<redaction-file-download-btn
|
||||
[dossier]="currentDossier"
|
||||
@ -37,6 +38,7 @@
|
||||
<div [class.extended]="collapsedDetails" class="content-container">
|
||||
<iqser-table
|
||||
(noDataAction)="fileInput.click()"
|
||||
*ngIf="(listingMode$ | async) === listingModes.table"
|
||||
[bulkActions]="bulkActions"
|
||||
[hasScrollButton]="true"
|
||||
[itemSize]="80"
|
||||
@ -49,6 +51,23 @@
|
||||
noDataButtonIcon="red:upload"
|
||||
noDataIcon="red:document"
|
||||
></iqser-table>
|
||||
|
||||
<iqser-workflow
|
||||
(addElement)="fileInput.click()"
|
||||
(noDataAction)="fileInput.click()"
|
||||
*ngIf="(listingMode$ | async) === listingModes.workflow"
|
||||
[config]="workflowConfig"
|
||||
[itemClasses]="{ disabled: disabledFn }"
|
||||
[itemTemplate]="workflowItemTemplate"
|
||||
[noDataButtonLabel]="'dossier-overview.no-data.action' | translate"
|
||||
[noDataText]="'dossier-overview.no-data.title' | translate"
|
||||
[showNoDataButton]="true"
|
||||
addElementColumn="UNASSIGNED"
|
||||
addElementIcon="red:upload"
|
||||
itemHeight="56px"
|
||||
noDataButtonIcon="red:upload"
|
||||
noDataIcon="red:document"
|
||||
></iqser-workflow>
|
||||
</div>
|
||||
|
||||
<div [class.collapsed]="collapsedDetails" class="right-container" iqserHasScrollbar>
|
||||
@ -72,95 +91,80 @@
|
||||
<redaction-dossier-overview-bulk-actions (reload)="bulkActionPerformed()"></redaction-dossier-overview-bulk-actions>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #filenameTemplate let-fileStatus="entity">
|
||||
<ng-template #filenameTemplate let-file="entity">
|
||||
<div class="cell">
|
||||
<div class="filename-wrapper">
|
||||
<div [class.error]="fileStatus.isError" class="table-item-title text-overflow">
|
||||
<span [matTooltip]="fileStatus.filename" matTooltipPosition="above">
|
||||
{{ fileStatus.filename }}
|
||||
<div>
|
||||
<div [class.error]="file.isError" [matTooltip]="file.filename" class="table-item-title" matTooltipPosition="above">
|
||||
{{ file.filename }}
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="file.primaryAttribute" class="small-label">
|
||||
<div class="primary-attribute">
|
||||
<span [matTooltip]="file.primaryAttribute" matTooltipPosition="above">
|
||||
{{ file.primaryAttribute }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="fileStatus.primaryAttribute" class="small-label">
|
||||
<div class="primary-attribute text-overflow">
|
||||
<span [matTooltip]="fileStatus.primaryAttribute" matTooltipPosition="above">
|
||||
{{ fileStatus.primaryAttribute }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||
{{ fileStatus.numberOfPages }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:exclude-pages"></mat-icon>
|
||||
{{ fileStatus.excludedPagesCount }}
|
||||
</div>
|
||||
<div *ngIf="fileStatus.lastOCRTime" [matTooltipPosition]="'above'" [matTooltip]="'dossier-overview.ocr-performed' | translate">
|
||||
<mat-icon svgIcon="red:ocr"></mat-icon>
|
||||
{{ fileStatus.lastOCRTime | date: 'mediumDate' }}
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="statsTemplate; context: { entity: file }"></ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #addedOnTemplate let-fileStatus="entity">
|
||||
<ng-template #addedOnTemplate let-file="entity">
|
||||
<div class="cell">
|
||||
<div [class.error]="fileStatus.isError" class="small-label">
|
||||
{{ fileStatus.added | date: 'd MMM. yyyy, hh:mm a' }}
|
||||
<div [class.error]="file.isError" class="small-label">
|
||||
{{ file.added | date: 'd MMM. yyyy, hh:mm a' }}
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #attributeTemplate let-config="extra" let-fileStatus="entity">
|
||||
<ng-template #attributeTemplate let-config="extra" let-file="entity">
|
||||
<div class="cell">
|
||||
{{ fileStatus.fileAttributes.attributeIdToValue[config.id] }}
|
||||
{{ file.fileAttributes.attributeIdToValue[config.id] }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #needsWorkTemplate let-fileStatus="entity">
|
||||
<ng-template #needsWorkTemplate let-file="entity">
|
||||
<!-- always show A for error-->
|
||||
<div *ngIf="fileStatus.isError" class="cell">
|
||||
<div *ngIf="file.isError" class="cell">
|
||||
<redaction-annotation-icon color="#dd4d50" label="A" type="square"></redaction-annotation-icon>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!fileStatus.isError" class="cell">
|
||||
<redaction-needs-work-badge [needsWorkInput]="fileStatus"></redaction-needs-work-badge>
|
||||
<div *ngIf="!file.isError" class="cell">
|
||||
<redaction-needs-work-badge [needsWorkInput]="file"></redaction-needs-work-badge>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #reviewerTemplate let-fileStatus="entity">
|
||||
<div *ngIf="!fileStatus.isError" class="user-column cell">
|
||||
<redaction-initials-avatar [userId]="fileStatus.currentReviewer" [withName]="true"></redaction-initials-avatar>
|
||||
<ng-template #reviewerTemplate let-file="entity">
|
||||
<div *ngIf="!file.isError" class="user-column cell">
|
||||
<redaction-initials-avatar [userId]="file.currentReviewer" [withName]="true"></redaction-initials-avatar>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #pagesTemplate let-fileStatus="entity">
|
||||
<div *ngIf="!fileStatus.isError" class="cell">
|
||||
<ng-template #pagesTemplate let-file="entity">
|
||||
<div *ngIf="!file.isError" class="cell">
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||
{{ fileStatus.numberOfPages }}
|
||||
{{ file.numberOfPages }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #statusTemplate let-fileStatus="entity">
|
||||
<div [class.extend-cols]="fileStatus.isError" class="status-container cell">
|
||||
<div *ngIf="fileStatus.isError" class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div>
|
||||
<div *ngIf="fileStatus.isPending" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
|
||||
<ng-template #statusTemplate let-file="entity">
|
||||
<div [class.extend-cols]="file.isError" class="status-container cell">
|
||||
<div *ngIf="file.isError" class="small-label error" translate="dossier-overview.file-listing.file-entry.file-error"></div>
|
||||
<div *ngIf="file.isPending" class="small-label" translate="dossier-overview.file-listing.file-entry.file-pending"></div>
|
||||
<div
|
||||
*ngIf="fileStatus.isProcessing"
|
||||
*ngIf="file.isProcessing"
|
||||
class="small-label loading"
|
||||
translate="dossier-overview.file-listing.file-entry.file-processing"
|
||||
></div>
|
||||
<iqser-status-bar
|
||||
*ngIf="fileStatus.isWorkable"
|
||||
*ngIf="file.isWorkable"
|
||||
[configs]="[
|
||||
{
|
||||
color: fileStatus.status,
|
||||
color: file.status,
|
||||
length: 1
|
||||
}
|
||||
]"
|
||||
@ -168,9 +172,68 @@
|
||||
|
||||
<redaction-file-actions
|
||||
(actionPerformed)="calculateData()"
|
||||
*ngIf="!fileStatus.isProcessing"
|
||||
[fileStatus]="fileStatus"
|
||||
*ngIf="!file.isProcessing"
|
||||
[file]="file"
|
||||
class="mr-4"
|
||||
type="dossier-overview-list"
|
||||
></redaction-file-actions>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #viewModeSelection>
|
||||
<div class="view-mode-selection">
|
||||
<div class="all-caps-label" translate="view-mode.view-as"></div>
|
||||
<iqser-circle-button
|
||||
(action)="listingMode = listingModes.workflow"
|
||||
[attr.aria-expanded]="(listingMode$ | async) === listingModes.workflow"
|
||||
[tooltip]="'view-mode.workflow' | translate"
|
||||
icon="iqser:lanes"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="listingMode = listingModes.table"
|
||||
[attr.aria-expanded]="(listingMode$ | async) === listingModes.table"
|
||||
[tooltip]="'view-mode.list' | translate"
|
||||
icon="iqser:list"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #workflowItemTemplate let-file="entity">
|
||||
<div class="workflow-item">
|
||||
<div>
|
||||
<div class="details">
|
||||
<div [matTooltip]="file.filename" class="filename" matTooltipPosition="above">
|
||||
{{ file.filename }}
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="statsTemplate; context: { entity: file }"></ng-container>
|
||||
</div>
|
||||
<div class="user">
|
||||
<redaction-initials-avatar [userId]="file.currentReviewer"></redaction-initials-avatar>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-file-actions
|
||||
(actionPerformed)="actionPerformed($event, file)"
|
||||
*ngIf="!file.isProcessing"
|
||||
[file]="file"
|
||||
type="dossier-overview-workflow"
|
||||
></redaction-file-actions>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #statsTemplate let-file="entity">
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||
{{ file.numberOfPages }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:exclude-pages"></mat-icon>
|
||||
{{ file.excludedPagesCount }}
|
||||
</div>
|
||||
<div *ngIf="file.lastOCRTime" [matTooltipPosition]="'above'" [matTooltip]="'dossier-overview.ocr-performed' | translate">
|
||||
<mat-icon svgIcon="red:ocr"></mat-icon>
|
||||
{{ file.lastOCRTime | date: 'mediumDate' }}
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
@use 'variables';
|
||||
@use 'common-mixins';
|
||||
|
||||
.file-upload-input {
|
||||
display: none;
|
||||
@ -27,6 +28,7 @@
|
||||
|
||||
.primary-attribute {
|
||||
padding-top: 6px;
|
||||
@include common-mixins.line-clamp(1);
|
||||
}
|
||||
|
||||
&.extend-cols {
|
||||
@ -63,3 +65,56 @@
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.view-mode-selection {
|
||||
border-right: 1px solid variables.$separator;
|
||||
padding-right: 16px;
|
||||
margin-right: 16px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> iqser-circle-button:not(:last-child) {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
> div {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-item {
|
||||
padding: 10px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.details {
|
||||
max-width: calc(100% - 28px);
|
||||
|
||||
.filename {
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
@include common-mixins.line-clamp(1);
|
||||
}
|
||||
}
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
redaction-file-actions {
|
||||
margin-top: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover redaction-file-actions {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-subtitle {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
TemplateRef,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { FileStatus, FileStatuses, IFileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
||||
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
||||
@ -18,13 +19,13 @@ import { StatusOverlayService } from '@upload-download/services/status-overlay.s
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import * as moment from 'moment';
|
||||
import { DossierDetailsComponent } from '../../components/dossier-details/dossier-details.component';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { timer } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
import { convertFiles, handleFileDrop } from '@utils/file-drop-utils';
|
||||
import { convertFiles, Files, handleFileDrop } from '@utils/file-drop-utils';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
@ -32,32 +33,36 @@ import { ActionConfig } from '@shared/components/page-header/models/action-confi
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
DefaultListingServices,
|
||||
INestedFilter,
|
||||
keyChecker,
|
||||
ListingComponent,
|
||||
ListingModes,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
TableColumnConfig,
|
||||
TableComponent,
|
||||
Toaster
|
||||
Toaster,
|
||||
WorkflowConfig
|
||||
} from '@iqser/common-ui';
|
||||
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
|
||||
import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { workloadTranslations } from '../../translations/workload-translations';
|
||||
import { fileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { annotationFilterChecker } from '@utils/filter-utils';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { Router } from '@angular/router';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-overview-screen.component.html',
|
||||
styleUrls: ['./dossier-overview-screen.component.scss'],
|
||||
providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }]
|
||||
})
|
||||
export class DossierOverviewScreenComponent extends ListingComponent<FileStatusWrapper> implements OnInit, OnDestroy, OnDetach, OnAttach {
|
||||
export class DossierOverviewScreenComponent extends ListingComponent<File> implements OnInit, OnDestroy, OnDetach, OnAttach {
|
||||
readonly listingModes = ListingModes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
currentDossier = this._appStateService.activeDossier;
|
||||
@ -70,29 +75,30 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
hide: !this.currentUser.isManager
|
||||
}
|
||||
];
|
||||
tableColumnConfigs: readonly TableColumnConfig<FileStatusWrapper>[] = [];
|
||||
tableColumnConfigs: readonly TableColumnConfig<File>[] = [];
|
||||
collapsedDetails = false;
|
||||
dossierAttributes: DossierAttributeWithValue[] = [];
|
||||
fileAttributeConfigs: FileAttributeConfig[];
|
||||
@ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<never>;
|
||||
@ViewChild('addedOnTemplate', { static: true }) addedOnTemplate: TemplateRef<never>;
|
||||
@ViewChild('attributeTemplate', { static: true }) attributeTemplate: TemplateRef<never>;
|
||||
@ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<never>;
|
||||
@ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef<never>;
|
||||
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
|
||||
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>;
|
||||
protected readonly _primaryKey = 'filename';
|
||||
fileAttributeConfigs: IFileAttributeConfig[];
|
||||
@ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('addedOnTemplate', { static: true }) addedOnTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('attributeTemplate', { static: true }) attributeTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<unknown>;
|
||||
readonly workflowConfig: WorkflowConfig<File, FileStatus>;
|
||||
@ViewChild(DossierDetailsComponent, { static: false })
|
||||
private readonly _dossierDetailsComponent: DossierDetailsComponent;
|
||||
private _lastScrolledIndex: number;
|
||||
@ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true })
|
||||
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
|
||||
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<DossierWrapper>;
|
||||
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
|
||||
|
||||
constructor(
|
||||
private readonly _toaster: Toaster,
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _router: Router,
|
||||
private readonly _userService: UserService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
@ -104,15 +110,53 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _fileUploadService: FileUploadService,
|
||||
private readonly _statusOverlayService: StatusOverlayService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _fileDropOverlayService: FileDropOverlayService,
|
||||
private readonly _dossierAttributesService: DossierAttributesService
|
||||
private readonly _dossierAttributesService: DossierAttributesService,
|
||||
private readonly _fileActionService: FileActionService
|
||||
) {
|
||||
super(_injector);
|
||||
this._loadEntitiesFromState();
|
||||
this.fileAttributeConfigs = this._appStateService.getFileAttributeConfig(
|
||||
this.currentDossier.dossierTemplateId
|
||||
).fileAttributeConfigs;
|
||||
this.workflowConfig = {
|
||||
columnIdentifierFn: entity => entity.status,
|
||||
itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
|
||||
columns: [
|
||||
{
|
||||
label: fileStatusTranslations[FileStatuses.UNASSIGNED],
|
||||
key: FileStatuses.UNASSIGNED,
|
||||
enterFn: this.unassignFn,
|
||||
enterPredicate: (entity: File) => false,
|
||||
color: '#D3D5DA'
|
||||
},
|
||||
{
|
||||
label: fileStatusTranslations[FileStatuses.UNDER_REVIEW],
|
||||
enterFn: this.underReviewFn,
|
||||
enterPredicate: (file: File) =>
|
||||
this.permissionsService.canSetUnderReview(file) ||
|
||||
this.permissionsService.canAssignToSelf(file) ||
|
||||
this.permissionsService.canAssignUser(file),
|
||||
key: FileStatuses.UNDER_REVIEW,
|
||||
color: '#FDBD00'
|
||||
},
|
||||
{
|
||||
label: fileStatusTranslations[FileStatuses.UNDER_APPROVAL],
|
||||
enterFn: this.underApprovalFn,
|
||||
enterPredicate: (file: File) =>
|
||||
this.permissionsService.canSetUnderApproval(file) || this.permissionsService.canUndoApproval(file),
|
||||
key: FileStatuses.UNDER_APPROVAL,
|
||||
color: '#374C81'
|
||||
},
|
||||
{
|
||||
label: fileStatusTranslations[FileStatuses.APPROVED],
|
||||
enterFn: this.approveFn,
|
||||
enterPredicate: (file: File) => this.permissionsService.isReadyForApproval(file),
|
||||
key: FileStatuses.APPROVED,
|
||||
color: '#48C9F7'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
get checkedRequiredFilters() {
|
||||
@ -127,11 +171,44 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
return this.fileAttributeConfigs.filter(config => config.displayedInFileList);
|
||||
}
|
||||
|
||||
routerLinkFn = (fileStatus: FileStatusWrapper) =>
|
||||
fileStatus.canBeOpened ? [`/main/dossiers/${this.currentDossier.dossierId}/file/${fileStatus.fileId}`] : [];
|
||||
unassignFn = async (file: File) => {
|
||||
// TODO
|
||||
console.log('unassign', file);
|
||||
};
|
||||
|
||||
disabledFn = (fileStatus: FileStatusWrapper) => fileStatus.excluded;
|
||||
lastOpenedFn = (fileStatus: FileStatusWrapper) => fileStatus.lastOpened;
|
||||
underReviewFn = (file: File) => {
|
||||
this._fileActionService.assignFile('reviewer', null, file, () => this._loadingService.loadWhile(this.reloadDossiers()), true);
|
||||
};
|
||||
|
||||
underApprovalFn = async (file: File) => {
|
||||
if (this._appStateService.activeDossier.approverIds.length > 1) {
|
||||
this._fileActionService.assignFile('approver', null, file, () => this._loadingService.loadWhile(this.reloadDossiers()), true);
|
||||
} else {
|
||||
this._loadingService.start();
|
||||
await this._fileActionService.setFilesUnderApproval([file]).toPromise();
|
||||
await this.reloadDossiers();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
};
|
||||
|
||||
approveFn = async (file: File) => {
|
||||
this._loadingService.start();
|
||||
await this._fileActionService.setFilesApproved([file]).toPromise();
|
||||
await this.reloadDossiers();
|
||||
this._loadingService.stop();
|
||||
};
|
||||
|
||||
actionPerformed(action?: string, file?: File) {
|
||||
this.calculateData();
|
||||
|
||||
if (action === 'navigate') {
|
||||
this._router.navigate([file.routerLink]);
|
||||
}
|
||||
}
|
||||
|
||||
disabledFn = (fileStatus: File) => fileStatus.excluded;
|
||||
|
||||
lastOpenedFn = (fileStatus: File) => fileStatus.lastOpened;
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this._configureTableColumns();
|
||||
@ -160,8 +237,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
.pipe(tap(index => (this._lastScrolledIndex = index)))
|
||||
.subscribe();
|
||||
|
||||
this.searchService.setSearchKey('filename');
|
||||
|
||||
this.dossierAttributes = await this._dossierAttributesService.getValues(this.currentDossier);
|
||||
} catch (e) {
|
||||
} finally {
|
||||
@ -213,39 +288,39 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
}
|
||||
|
||||
@HostListener('drop', ['$event'])
|
||||
onDrop(event: DragEvent) {
|
||||
onDrop(event: DragEvent): void {
|
||||
handleFileDrop(event, this.currentDossier, this._uploadFiles.bind(this));
|
||||
}
|
||||
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver(event) {
|
||||
onDragOver(event): void {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
async uploadFiles(files: File[] | FileList) {
|
||||
async uploadFiles(files: Files): Promise<void> {
|
||||
await this._uploadFiles(convertFiles(files, this.currentDossier));
|
||||
this._fileInput.nativeElement.value = null;
|
||||
}
|
||||
|
||||
async bulkActionPerformed() {
|
||||
async bulkActionPerformed(): Promise<void> {
|
||||
this.entitiesService.setSelected([]);
|
||||
await this.reloadDossiers();
|
||||
}
|
||||
|
||||
openEditDossierDialog($event: MouseEvent) {
|
||||
this._dialogService.openDialog('editDossier', $event, {
|
||||
dossierWrapper: this.currentDossier
|
||||
dossier: this.currentDossier
|
||||
});
|
||||
}
|
||||
|
||||
openAssignDossierMembersDialog(): void {
|
||||
const data = { dossierWrapper: this.currentDossier, section: 'members' };
|
||||
const data = { dossier: this.currentDossier, section: 'members' };
|
||||
this._dialogService.openDialog('editDossier', null, data, async () => await this.reloadDossiers());
|
||||
}
|
||||
|
||||
openDossierDictionaryDialog() {
|
||||
const data = { dossierWrapper: this.currentDossier, section: 'dossierDictionary' };
|
||||
const data = { dossier: this.currentDossier, section: 'dossierDictionary' };
|
||||
this._dialogService.openDialog('editDossier', null, data, async () => {
|
||||
await this.reloadDossiers();
|
||||
});
|
||||
@ -255,11 +330,11 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
this.collapsedDetails = !this.collapsedDetails;
|
||||
}
|
||||
|
||||
recentlyModifiedChecker = (file: FileStatusWrapper) =>
|
||||
recentlyModifiedChecker = (file: File) =>
|
||||
moment(file.lastUpdated).add(this._configService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
|
||||
|
||||
private _configureTableColumns() {
|
||||
const dynamicColumns: TableColumnConfig<FileStatusWrapper>[] = [];
|
||||
const dynamicColumns: TableColumnConfig<File>[] = [];
|
||||
for (const config of this.displayedInFileListAttributes) {
|
||||
if (config.displayedInFileList) {
|
||||
dynamicColumns.push({ label: config.label, notTranslatable: true, template: this.attributeTemplate, extra: config });
|
||||
@ -268,7 +343,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
this.tableColumnConfigs = [
|
||||
{
|
||||
label: _('dossier-overview.table-col-names.name'),
|
||||
sortByKey: 'filename',
|
||||
sortByKey: 'searchKey',
|
||||
template: this.filenameTemplate,
|
||||
width: '3fr'
|
||||
},
|
||||
@ -292,7 +367,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
},
|
||||
{
|
||||
label: _('dossier-overview.table-col-names.pages'),
|
||||
sortByKey: 'pages',
|
||||
sortByKey: 'numberOfPages',
|
||||
template: this.pagesTemplate
|
||||
},
|
||||
{
|
||||
@ -323,7 +398,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
return;
|
||||
}
|
||||
|
||||
const allDistinctFileStatusWrapper = new Set<string>();
|
||||
const allDistinctFileStatuses = new Set<string>();
|
||||
const allDistinctPeople = new Set<string>();
|
||||
const allDistinctAddedDates = new Set<string>();
|
||||
const allDistinctNeedsWork = new Set<string>();
|
||||
@ -332,7 +407,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
|
||||
this.entitiesService.all.forEach(file => {
|
||||
allDistinctPeople.add(file.currentReviewer);
|
||||
allDistinctFileStatusWrapper.add(file.status);
|
||||
allDistinctFileStatuses.add(file.status);
|
||||
allDistinctAddedDates.add(moment(file.added).format('DD/MM/YYYY'));
|
||||
|
||||
if (file.analysisRequired) {
|
||||
@ -379,33 +454,40 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
});
|
||||
});
|
||||
|
||||
const statusFilters = [...allDistinctFileStatusWrapper].map<NestedFilter>(item => ({
|
||||
key: item,
|
||||
label: this._translateService.instant(fileStatusTranslations[item])
|
||||
}));
|
||||
const statusFilters = [...allDistinctFileStatuses].map(
|
||||
status =>
|
||||
new NestedFilter({
|
||||
id: status,
|
||||
label: this._translateService.instant(fileStatusTranslations[status])
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'statusFilters',
|
||||
label: this._translateService.instant('filters.status'),
|
||||
icon: 'red:status',
|
||||
filters: statusFilters.sort(StatusSorter.byStatus),
|
||||
filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
|
||||
checker: keyChecker('status')
|
||||
});
|
||||
|
||||
const peopleFilters = [];
|
||||
const peopleFilters: NestedFilter[] = [];
|
||||
if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) {
|
||||
allDistinctPeople.delete(undefined);
|
||||
allDistinctPeople.delete(null);
|
||||
peopleFilters.push({
|
||||
key: null,
|
||||
label: this._translateService.instant('initials-avatar.unassigned')
|
||||
});
|
||||
peopleFilters.push(
|
||||
new NestedFilter({
|
||||
id: null,
|
||||
label: this._translateService.instant('initials-avatar.unassigned')
|
||||
})
|
||||
);
|
||||
}
|
||||
allDistinctPeople.forEach(userId => {
|
||||
peopleFilters.push({
|
||||
key: userId,
|
||||
label: this._userService.getNameForId(userId)
|
||||
});
|
||||
peopleFilters.push(
|
||||
new NestedFilter({
|
||||
id: userId,
|
||||
label: this._userService.getNameForId(userId)
|
||||
})
|
||||
);
|
||||
});
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'peopleFilters',
|
||||
@ -415,10 +497,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
checker: keyChecker('currentReviewer')
|
||||
});
|
||||
|
||||
const needsWorkFilters = [...allDistinctNeedsWork].map<NestedFilter>(item => ({
|
||||
key: item,
|
||||
label: workloadTranslations[item]
|
||||
}));
|
||||
const needsWorkFilters = [...allDistinctNeedsWork].map(
|
||||
item =>
|
||||
new NestedFilter({
|
||||
id: item,
|
||||
label: workloadTranslations[item]
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'needsWorkFilters',
|
||||
@ -437,27 +522,33 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
slug: key,
|
||||
label: key,
|
||||
icon: 'red:template',
|
||||
filters: [...filterValue].map<NestedFilter>((value: string) => ({
|
||||
key: value,
|
||||
label: value === '-' ? this._translateService.instant('filters.empty') : value
|
||||
})),
|
||||
checker: (input: FileStatusWrapper, filter: NestedFilter) => filter.key === input.fileAttributes.attributeIdToValue[id]
|
||||
filters: [...filterValue].map(
|
||||
(value: string) =>
|
||||
new NestedFilter({
|
||||
id: value,
|
||||
label: value === '-' ? this._translateService.instant('filters.empty') : value
|
||||
})
|
||||
),
|
||||
checker: (input: File, filter: INestedFilter) => filter.id === input.fileAttributes.attributeIdToValue[id]
|
||||
});
|
||||
});
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'quickFilters',
|
||||
filters: this._createQuickFilters(),
|
||||
checker: (file: FileStatusWrapper) =>
|
||||
checker: (file: File) =>
|
||||
this.checkedRequiredFilters.reduce((acc, f) => acc && f.checker(file), true) &&
|
||||
(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
|
||||
}));
|
||||
const filesNamesFilters = this.entitiesService.all.map(
|
||||
file =>
|
||||
new NestedFilter({
|
||||
id: file.filename,
|
||||
label: file.filename
|
||||
})
|
||||
);
|
||||
|
||||
this.filterService.addFilterGroup({
|
||||
slug: 'filesNamesFilter',
|
||||
@ -469,13 +560,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
});
|
||||
}
|
||||
|
||||
private _createQuickFilters() {
|
||||
let quickFilters = [];
|
||||
private _createQuickFilters(): NestedFilter[] {
|
||||
let quickFilters: INestedFilter[] = [];
|
||||
if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) {
|
||||
const recentPeriod = this._configService.values.RECENT_PERIOD_IN_HOURS;
|
||||
quickFilters = [
|
||||
{
|
||||
key: 'recent',
|
||||
id: 'recent',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.recent', {
|
||||
hours: recentPeriod
|
||||
}),
|
||||
@ -488,20 +579,20 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
|
||||
return [
|
||||
...quickFilters,
|
||||
{
|
||||
key: 'assigned-to-me',
|
||||
id: 'assigned-to-me',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'),
|
||||
checker: (file: FileStatusWrapper) => file.currentReviewer === this.currentUser.id
|
||||
checker: (file: File) => file.currentReviewer === this.currentUser.id
|
||||
},
|
||||
{
|
||||
key: 'unassigned',
|
||||
id: 'unassigned',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.unassigned'),
|
||||
checker: (file: FileStatusWrapper) => !file.currentReviewer
|
||||
checker: (file: File) => !file.currentReviewer
|
||||
},
|
||||
{
|
||||
key: 'assigned-to-others',
|
||||
id: 'assigned-to-others',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'),
|
||||
checker: (file: FileStatusWrapper) => !!file.currentReviewer && file.currentReviewer !== this.currentUser.id
|
||||
checker: (file: File) => !!file.currentReviewer && file.currentReviewer !== this.currentUser.id
|
||||
}
|
||||
];
|
||||
].map(filter => new NestedFilter(filter));
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +92,7 @@
|
||||
(actionPerformed)="fileActionPerformed($event)"
|
||||
[activeDocumentInfo]="viewDocumentInfo"
|
||||
[activeExcludePages]="excludePages"
|
||||
type="file-preview"
|
||||
></redaction-file-actions>
|
||||
|
||||
<iqser-circle-button
|
||||
@ -107,10 +108,10 @@
|
||||
(action)="downloadOriginalFile()"
|
||||
*ngIf="userPreferenceService.areDevFeaturesEnabled"
|
||||
[tooltip]="'file-preview.download-original-file' | translate"
|
||||
[type]="circleButtonTypes.primary"
|
||||
class="ml-8"
|
||||
icon="red:download"
|
||||
tooltipPosition="below"
|
||||
[type]="circleButtonTypes.primary"
|
||||
></iqser-circle-button>
|
||||
<!-- End Dev Mode Features-->
|
||||
|
||||
@ -141,7 +142,7 @@
|
||||
[annotations]="annotations"
|
||||
[canPerformActions]="canPerformAnnotationActions"
|
||||
[fileData]="displayData"
|
||||
[fileStatus]="appStateService.activeFile"
|
||||
[file]="appStateService.activeFile"
|
||||
[multiSelectActive]="multiSelectActive"
|
||||
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
|
||||
></redaction-pdf-viewer>
|
||||
@ -158,7 +159,7 @@
|
||||
<redaction-document-info
|
||||
(closeDocumentInfoView)="viewDocumentInfo = false"
|
||||
*ngIf="viewDocumentInfo"
|
||||
[file]="fileData.fileStatus"
|
||||
[file]="fileData.file"
|
||||
></redaction-document-info>
|
||||
|
||||
<redaction-file-workload
|
||||
|
||||
@ -8,8 +8,8 @@ import {
|
||||
CircleButtonTypes,
|
||||
Debounce,
|
||||
FilterService,
|
||||
INestedFilter,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
processFilters,
|
||||
Toaster
|
||||
} from '@iqser/common-ui';
|
||||
@ -21,17 +21,12 @@ 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 { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { timer } from 'rxjs';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import {
|
||||
FileManagementControllerService,
|
||||
FileStatus,
|
||||
StatusControllerService,
|
||||
UserPreferenceControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FileManagementControllerService, FileStatus, List, UserPreferenceControllerService } from '@redaction/red-ui-http';
|
||||
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
|
||||
import { download } from '@utils/file-download-utils';
|
||||
import { ViewMode } from '@models/file/view-mode';
|
||||
@ -44,6 +39,8 @@ import { fileStatusTranslations } from '../../translations/file-status-translati
|
||||
import { handleFilterDelta } from '@utils/filter-utils';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileActionsComponent } from '../../components/file-actions/file-actions.component';
|
||||
import { User } from '@models/user';
|
||||
import { FilesService } from '../../services/files.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
|
||||
@ -71,6 +68,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
viewDocumentInfo = false;
|
||||
excludePages = false;
|
||||
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
|
||||
@ViewChild('fileActions') fileActions: FileActionsComponent;
|
||||
private _instance: WebViewerInstance;
|
||||
private _lastPage: string;
|
||||
private _reloadFileOnReanalysis = false;
|
||||
@ -79,8 +77,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
read: TemplateRef,
|
||||
static: true
|
||||
})
|
||||
private readonly _filterTemplate: TemplateRef<NestedFilter>;
|
||||
@ViewChild('fileActions') fileActions: FileActionsComponent;
|
||||
private readonly _filterTemplate: TemplateRef<INestedFilter>;
|
||||
|
||||
constructor(
|
||||
readonly appStateService: AppStateService,
|
||||
@ -97,7 +94,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _fileActionService: FileActionService,
|
||||
private readonly _fileDownloadService: PdfViewerDataService,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _fileManagementControllerService: FileManagementControllerService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
@ -113,7 +110,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
});
|
||||
}
|
||||
|
||||
get singleUsersSelectOptions(): string[] {
|
||||
get singleUsersSelectOptions(): List {
|
||||
return this.appStateService.activeFile?.isUnderApproval
|
||||
? this.appStateService.activeDossier.approverIds
|
||||
: this.appStateService.activeDossier.memberIds;
|
||||
@ -146,11 +143,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
get canSwitchToRedactedView(): boolean {
|
||||
return this.fileData && !this.fileData.fileStatus.analysisRequired && !this.fileData.fileStatus.excluded;
|
||||
return this.fileData && !this.fileData.file.analysisRequired && !this.fileData.file.excluded;
|
||||
}
|
||||
|
||||
get canSwitchToDeltaView(): boolean {
|
||||
return this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.fileStatus.excluded;
|
||||
return this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.file.excluded;
|
||||
}
|
||||
|
||||
get canAssign(): boolean {
|
||||
@ -174,7 +171,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
get lastReviewer(): string | undefined {
|
||||
return this.appStateService.activeFile.fileStatus.lastReviewer;
|
||||
return this.appStateService.activeFile.lastReviewer;
|
||||
}
|
||||
|
||||
get assignOrChangeReviewerTooltip(): string {
|
||||
@ -187,11 +184,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
return this.appStateService.activeFile.currentReviewer;
|
||||
}
|
||||
|
||||
get status(): FileStatus.StatusEnum {
|
||||
get status(): FileStatus {
|
||||
return this.appStateService.activeFile.status;
|
||||
}
|
||||
|
||||
get statusBarConfig(): [{ length: number; color: FileStatus.StatusEnum }] {
|
||||
get statusBarConfig(): [{ length: number; color: FileStatus }] {
|
||||
return [{ length: 1, color: this.status }];
|
||||
}
|
||||
|
||||
@ -200,9 +197,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
get canAssignReviewer(): boolean {
|
||||
return (
|
||||
!this.currentReviewer && this.permissionsService.canAssignUser() && this.appStateService.activeDossier.hasMoreThanOneReviewer
|
||||
);
|
||||
return !this.currentReviewer && this.permissionsService.canAssignUser() && this.appStateService.activeDossier.hasReviewers;
|
||||
}
|
||||
|
||||
updateViewMode(): void {
|
||||
@ -261,7 +256,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._updateCanPerformActions();
|
||||
this._subscribeToFileUpdates();
|
||||
|
||||
if (this.fileData?.fileStatus?.analysisRequired) {
|
||||
if (this.fileData?.file?.analysisRequired) {
|
||||
this.fileActions.reanalyseFile();
|
||||
}
|
||||
}
|
||||
@ -355,7 +350,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
response.manualRedactionEntryWrapper.rectId
|
||||
);
|
||||
this._instance.Core.annotationManager.deleteAnnotation(annotation);
|
||||
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
||||
this.fileData.file = await this.appStateService.reloadActiveFile();
|
||||
const distinctPages = entryWrapper.manualRedactionEntry.positions
|
||||
.map(p => p.page)
|
||||
.filter((item, pos, self) => self.indexOf(item) === pos);
|
||||
@ -490,18 +485,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
async assignToMe() {
|
||||
await this._fileActionService.assignToMe(this.fileData.fileStatus, async () => {
|
||||
await this._fileActionService.assignToMe([this.fileData.file], async () => {
|
||||
await this.appStateService.reloadActiveFile();
|
||||
this._updateCanPerformActions();
|
||||
});
|
||||
}
|
||||
|
||||
async assignReviewer(user: UserWrapper | string) {
|
||||
async assignReviewer(user: User | string) {
|
||||
const reviewerId = typeof user === 'string' ? user : user.id;
|
||||
const reviewerName = this.userService.getNameForId(reviewerId);
|
||||
|
||||
const { dossierId, fileId, filename } = this.fileData.fileStatus;
|
||||
await this._statusControllerService.setFileReviewer(dossierId, fileId, reviewerId).toPromise();
|
||||
const { dossierId, fileId, filename } = this.fileData.file;
|
||||
await this._filesService.setReviewerFor([fileId], dossierId, reviewerId).toPromise();
|
||||
|
||||
this._toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } });
|
||||
await this.appStateService.reloadActiveFile();
|
||||
@ -523,9 +518,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
downloadOriginalFile() {
|
||||
this.addSubscription = this._fileManagementControllerService
|
||||
.downloadOriginalFile(this.dossierId, this.fileId, true, this.fileData.fileStatus.cacheIdentifier, 'response')
|
||||
.downloadOriginalFile(this.dossierId, this.fileId, true, this.fileData.file.cacheIdentifier, 'response')
|
||||
.subscribe(data => {
|
||||
download(data, this.fileData.fileStatus.filename);
|
||||
download(data, this.fileData.file.filename);
|
||||
});
|
||||
}
|
||||
|
||||
@ -561,7 +556,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
if (excludedPages && excludedPages.length > 0) {
|
||||
const pdfNet = this._instance.Core.PDFNet;
|
||||
const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc();
|
||||
await clearStamps(document, pdfNet, [...Array(this.fileData.fileStatus.numberOfPages).keys()]);
|
||||
await clearStamps(document, pdfNet, [...Array(this.fileData.file.numberOfPages).keys()]);
|
||||
await stampPDFPage(
|
||||
document,
|
||||
pdfNet,
|
||||
@ -577,7 +572,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private async _stampExcludedPages() {
|
||||
await this._doStampExcludedPages(this.fileData.fileStatus.excludedPages);
|
||||
await this._doStampExcludedPages(this.fileData.file.excludedPages);
|
||||
this._instance.Core.documentViewer.refreshAll();
|
||||
this._instance.Core.documentViewer.updateView([this.activeViewerPage], this.activeViewerPage);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
@ -585,8 +580,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
private _subscribeToFileUpdates(): void {
|
||||
this.addSubscription = timer(0, 5000).subscribe(async () => await this.appStateService.reloadActiveFile());
|
||||
this.addSubscription = this.appStateService.fileReanalysed$.subscribe(async (fileStatus: FileStatusWrapper) => {
|
||||
if (fileStatus.fileId === this.fileId) {
|
||||
this.addSubscription = this.appStateService.fileReanalysed$.subscribe(async (file: File) => {
|
||||
if (file.fileId === this.fileId) {
|
||||
await this._loadFileData(!this._reloadFileOnReanalysis);
|
||||
this._reloadFileOnReanalysis = false;
|
||||
this._loadingService.stop();
|
||||
@ -606,11 +601,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private async _loadFileData(performUpdate = false): Promise<void> {
|
||||
const fileData = await this._fileDownloadService.loadActiveFileData().toPromise();
|
||||
|
||||
if (!fileData.fileStatus?.isPending && !fileData.fileStatus?.isError) {
|
||||
if (!fileData.file?.isPending && !fileData.file?.isError) {
|
||||
if (performUpdate) {
|
||||
this.fileData.redactionLog = fileData.redactionLog;
|
||||
this.fileData.redactionChangeLog = fileData.redactionChangeLog;
|
||||
this.fileData.fileStatus = fileData.fileStatus;
|
||||
this.fileData.file = fileData.file;
|
||||
this.rebuildFilters(true);
|
||||
} else {
|
||||
this.fileData = fileData;
|
||||
@ -620,7 +615,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileData.fileStatus.isError) {
|
||||
if (fileData.file.isError) {
|
||||
await this._router.navigate(['/main/dossiers/' + this.dossierId]);
|
||||
}
|
||||
}
|
||||
@ -648,7 +643,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) {
|
||||
const currentPageAnnotations = this.annotations.filter(a => a.pageNumber === page);
|
||||
const currentPageAnnotationIds = currentPageAnnotations.map(a => a.id);
|
||||
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
||||
this.fileData.file = await this.appStateService.reloadActiveFile();
|
||||
|
||||
this._fileDownloadService.loadActiveFileRedactionLog().subscribe(redactionLogPreview => {
|
||||
this.fileData.redactionLog = redactionLogPreview;
|
||||
|
||||
@ -1,23 +1,29 @@
|
||||
import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { DefaultListingServices, keyChecker, Listable, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { MatchedDocument, SearchControllerService, SearchResult } from '@redaction/red-ui-http';
|
||||
import {
|
||||
DefaultListingServices,
|
||||
IListable,
|
||||
keyChecker,
|
||||
ListingComponent,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
TableColumnConfig
|
||||
} from '@iqser/common-ui';
|
||||
import { List, MatchedDocument, SearchControllerService, SearchResult } from '@redaction/red-ui-http';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { debounceTime, map, skip, switchMap, tap } from 'rxjs/operators';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { fileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { SearchPositions } from '@shared/components/page-header/models/search-positions.type';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
|
||||
interface ListItem extends Listable {
|
||||
interface ListItem extends IListable {
|
||||
readonly dossierId: string;
|
||||
readonly filename: string;
|
||||
readonly unmatched: readonly string[] | null;
|
||||
readonly highlights: Record<string, readonly string[]>;
|
||||
readonly unmatched: List | null;
|
||||
readonly highlights: Record<string, List>;
|
||||
readonly routerLink: string;
|
||||
readonly status: string;
|
||||
readonly dossierName: string;
|
||||
@ -26,7 +32,7 @@ interface ListItem extends Listable {
|
||||
|
||||
interface SearchInput {
|
||||
readonly query: string;
|
||||
readonly dossierIds?: readonly string[];
|
||||
readonly dossierIds?: List;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -53,7 +59,6 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
|
||||
tap(result => this.entitiesService.setEntities(result)),
|
||||
tap(() => this._loadingService.stop())
|
||||
);
|
||||
protected readonly _primaryKey = 'filename';
|
||||
|
||||
constructor(
|
||||
private readonly _router: Router,
|
||||
@ -72,11 +77,14 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
|
||||
label: this._translateService.instant('search-screen.filters.by-dossier'),
|
||||
filterceptionPlaceholder: this._translateService.instant('search-screen.filters.search-placeholder'),
|
||||
icon: 'red:folder',
|
||||
filters: this._appStateService.allDossiers.map(dossier => ({
|
||||
key: dossier.dossierId,
|
||||
label: dossier.dossierName
|
||||
})),
|
||||
checker: keyChecker('dossierId')
|
||||
filters: this._appStateService.allDossiers.map(
|
||||
dossier =>
|
||||
new NestedFilter({
|
||||
id: dossier.id,
|
||||
label: dossier.dossierName
|
||||
})
|
||||
),
|
||||
checker: keyChecker('id')
|
||||
});
|
||||
|
||||
this.addSubscription = _activatedRoute.queryParamMap
|
||||
@ -89,17 +97,11 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
|
||||
this.addSubscription = this.searchService.valueChanges$.pipe(debounceTime(300)).subscribe(value => this.updateNavigation(value));
|
||||
|
||||
this.addSubscription = this.filterService.filterGroups$.pipe(skip(1)).subscribe(group => {
|
||||
const dossierIds = group[0].filters.filter(v => v.checked).map(v => v.key);
|
||||
const dossierIds = group[0].filters.filter(v => v.checked).map(v => v.id);
|
||||
this.search$.next({ query: this.searchService.searchValue, dossierIds: dossierIds });
|
||||
});
|
||||
}
|
||||
|
||||
routerLinkFn = (entity: ListItem) => [entity.routerLink];
|
||||
|
||||
setInitialConfig(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
updateNavigation(query: string, mustContain?: string): void {
|
||||
const newQuery = query?.replace(mustContain, `"${mustContain}"`);
|
||||
const queryParams = newQuery && newQuery !== '' ? { query: newQuery } : {};
|
||||
@ -137,14 +139,6 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
|
||||
this.search$.next({ query, dossierIds: dossierId ? [dossierId] : [] });
|
||||
}
|
||||
|
||||
private _getFileWrapper(dossierId: string, fileId: string): FileStatusWrapper {
|
||||
return this._appStateService.getFileById(dossierId, fileId);
|
||||
}
|
||||
|
||||
private _getDossierWrapper(dossierId: string): DossierWrapper {
|
||||
return this._appStateService.getDossierById(dossierId);
|
||||
}
|
||||
|
||||
private _toMatchedDocuments({ matchedDocuments }: SearchResult): MatchedDocument[] {
|
||||
return matchedDocuments.filter(doc => doc.score > 0 && doc.matchedTerms.length > 0);
|
||||
}
|
||||
@ -154,8 +148,8 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
|
||||
}
|
||||
|
||||
private _toListItem({ dossierId, fileId, unmatchedTerms, highlights }: MatchedDocument): ListItem {
|
||||
const fileWrapper = this._getFileWrapper(dossierId, fileId);
|
||||
if (!fileWrapper) {
|
||||
const file = this._appStateService.getFileById(dossierId, fileId);
|
||||
if (!file) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -164,10 +158,11 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
|
||||
dossierId,
|
||||
unmatched: unmatchedTerms || null,
|
||||
highlights,
|
||||
status: fileWrapper.status,
|
||||
numberOfPages: fileWrapper.numberOfPages,
|
||||
dossierName: this._getDossierWrapper(dossierId).dossierName,
|
||||
filename: fileWrapper.filename,
|
||||
status: file.status,
|
||||
numberOfPages: file.numberOfPages,
|
||||
dossierName: this._appStateService.getDossierById(dossierId).dossierName,
|
||||
filename: file.filename,
|
||||
searchKey: file.filename,
|
||||
routerLink: `/main/dossiers/${dossierId}/file/${fileId}`
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,38 +1,36 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { SuperTypeSorter } from '@utils/sorters/super-type-sorter';
|
||||
import { handleCheckedValue, NestedFilter } from '@iqser/common-ui';
|
||||
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui';
|
||||
import { annotationTypesTranslations } from '../../../translations/annotation-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@Injectable()
|
||||
export class AnnotationProcessingService {
|
||||
static get secondaryAnnotationFilters(): NestedFilter[] {
|
||||
static get secondaryAnnotationFilters(): INestedFilter[] {
|
||||
return [
|
||||
{
|
||||
key: 'with-comments',
|
||||
id: 'with-comments',
|
||||
icon: 'red:comment',
|
||||
label: _('filter-menu.with-comments'),
|
||||
checked: false,
|
||||
topLevelFilter: true,
|
||||
children: [],
|
||||
checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0
|
||||
},
|
||||
{
|
||||
key: 'with-reason-changes',
|
||||
id: 'with-reason-changes',
|
||||
icon: 'red:reason',
|
||||
label: _('filter-menu.with-reason-changes'),
|
||||
checked: false,
|
||||
topLevelFilter: true,
|
||||
children: [],
|
||||
checker: (annotation: AnnotationWrapper) => annotation?.legalBasisChangeValue?.length > 0
|
||||
}
|
||||
];
|
||||
].map(item => new NestedFilter(item));
|
||||
}
|
||||
|
||||
getAnnotationFilter(annotations: AnnotationWrapper[]): NestedFilter[] {
|
||||
const filterMap = new Map<string, NestedFilter>();
|
||||
const filters: NestedFilter[] = [];
|
||||
getAnnotationFilter(annotations: AnnotationWrapper[]): INestedFilter[] {
|
||||
const filterMap = new Map<string, INestedFilter>();
|
||||
const filters: INestedFilter[] = [];
|
||||
|
||||
annotations?.forEach(a => {
|
||||
const topLevelFilter = a.superType !== 'hint' && a.superType !== 'redaction' && a.superType !== 'recommendation';
|
||||
@ -49,21 +47,20 @@ export class AnnotationProcessingService {
|
||||
if (!parentFilter) {
|
||||
parentFilter = this._createParentFilter(a.superType, filterMap, filters);
|
||||
}
|
||||
const childFilter = {
|
||||
key: a.type,
|
||||
const childFilter: IFilter = {
|
||||
id: a.type,
|
||||
label: a.type,
|
||||
checked: false,
|
||||
filters: [],
|
||||
matches: 1
|
||||
};
|
||||
filterMap.set(key, childFilter);
|
||||
parentFilter.children.push(childFilter);
|
||||
parentFilter.children.push(new Filter(childFilter));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const filter of filters) {
|
||||
filter.children.sort((a, b) => a.key.localeCompare(b.key));
|
||||
filter.children.sort((a, b) => a.id.localeCompare(b.id));
|
||||
handleCheckedValue(filter);
|
||||
if (filter.checked || filter.indeterminate) {
|
||||
filter.expanded = true;
|
||||
@ -73,13 +70,13 @@ export class AnnotationProcessingService {
|
||||
}
|
||||
}
|
||||
|
||||
return filters.sort((a, b) => SuperTypeSorter[a.key] - SuperTypeSorter[b.key]);
|
||||
return filters.sort((a, b) => SuperTypeSorter[a.id] - SuperTypeSorter[b.id]);
|
||||
}
|
||||
|
||||
filterAndGroupAnnotations(
|
||||
annotations: AnnotationWrapper[],
|
||||
primaryFilters: NestedFilter[],
|
||||
secondaryFilters?: NestedFilter[]
|
||||
primaryFilters: INestedFilter[],
|
||||
secondaryFilters?: INestedFilter[]
|
||||
): Map<number, AnnotationWrapper[]> {
|
||||
const obj = new Map<number, AnnotationWrapper[]>();
|
||||
|
||||
@ -116,21 +113,20 @@ export class AnnotationProcessingService {
|
||||
return obj;
|
||||
}
|
||||
|
||||
private _createParentFilter(key: string, filterMap: Map<string, NestedFilter>, filters: NestedFilter[]) {
|
||||
const filter: NestedFilter = {
|
||||
key: key,
|
||||
private _createParentFilter(key: string, filterMap: Map<string, INestedFilter>, filters: INestedFilter[]) {
|
||||
const filter: INestedFilter = new NestedFilter({
|
||||
id: key,
|
||||
topLevelFilter: true,
|
||||
matches: 1,
|
||||
label: annotationTypesTranslations[key],
|
||||
children: []
|
||||
};
|
||||
label: annotationTypesTranslations[key]
|
||||
});
|
||||
filterMap.set(key, filter);
|
||||
filters.push(filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
private _getFlatFilters(filters: NestedFilter[], filterBy?: (f: NestedFilter) => boolean) {
|
||||
const flatFilters: NestedFilter[] = [];
|
||||
private _getFlatFilters(filters: INestedFilter[], filterBy?: (f: INestedFilter) => boolean) {
|
||||
const flatFilters: INestedFilter[] = [];
|
||||
|
||||
filters.forEach(filter => {
|
||||
flatFilters.push(filter);
|
||||
@ -140,7 +136,7 @@ export class AnnotationProcessingService {
|
||||
return filterBy ? flatFilters.filter(f => filterBy(f)) : flatFilters;
|
||||
}
|
||||
|
||||
private _matchesOne = (filters: NestedFilter[], condition: (filter: NestedFilter) => boolean): boolean => {
|
||||
private _matchesOne = (filters: INestedFilter[], condition: (filter: INestedFilter) => boolean): boolean => {
|
||||
if (filters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
@ -154,7 +150,7 @@ export class AnnotationProcessingService {
|
||||
return false;
|
||||
};
|
||||
|
||||
private _matchesAll = (filters: NestedFilter[], condition: (filter: NestedFilter) => boolean): boolean => {
|
||||
private _matchesAll = (filters: INestedFilter[], condition: (filter: INestedFilter) => boolean): boolean => {
|
||||
if (filters.length === 0) {
|
||||
return true;
|
||||
}
|
||||
@ -168,11 +164,11 @@ export class AnnotationProcessingService {
|
||||
return true;
|
||||
};
|
||||
|
||||
private _checkByFilterKey = (filter: NestedFilter, annotation: AnnotationWrapper) => {
|
||||
private _checkByFilterKey = (filter: INestedFilter, annotation: AnnotationWrapper) => {
|
||||
const superType = annotation.superType;
|
||||
const isNotTopLevelFilter = superType === 'hint' || superType === 'redaction' || superType === 'recommendation';
|
||||
|
||||
return filter.key === superType || (filter.key === annotation.type && isNotTopLevelFilter);
|
||||
return filter.id === superType || (filter.id === annotation.type && isNotTopLevelFilter);
|
||||
};
|
||||
|
||||
private _sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {
|
||||
|
||||
@ -1,33 +1,90 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Dossier, DossierControllerService } from '@redaction/red-ui-http';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { IDossier } from '@redaction/red-ui-http';
|
||||
import { EntitiesService, List, QueryParam } from '@iqser/common-ui';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { TEMPORARY_INJECTOR } from './injector';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { ActivationEnd, Router } from '@angular/router';
|
||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||
|
||||
export interface IDossiersStats {
|
||||
totalPeople: number;
|
||||
totalAnalyzedPages: number;
|
||||
}
|
||||
|
||||
const getRelatedEvents = filter(event => event instanceof ActivationEnd && event.snapshot.component !== BaseScreenComponent);
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DossiersService {
|
||||
constructor(private readonly _dossierControllerService: DossierControllerService) {}
|
||||
export class DossiersService extends EntitiesService<Dossier, IDossier> {
|
||||
readonly stats$ = this.all$.pipe(map(entities => this._computeStats(entities)));
|
||||
readonly activeDossierId$: Observable<string | null>;
|
||||
readonly activeDossier$: Observable<Dossier | null>;
|
||||
private readonly _activeDossierId$ = new BehaviorSubject<string | null>(null);
|
||||
|
||||
createOrUpdate(dossier: Dossier): Promise<Dossier> {
|
||||
return this._dossierControllerService.createOrUpdateDossier(dossier).toPromise();
|
||||
constructor(protected readonly _injector: Injector, private readonly _router: Router) {
|
||||
super(TEMPORARY_INJECTOR(_injector), 'dossier');
|
||||
this.activeDossierId$ = this._activeDossierId$.asObservable();
|
||||
this.activeDossier$ = this.activeDossierId$.pipe(map(id => this.all.find(dossier => dossier.id === id)));
|
||||
|
||||
_router.events.pipe(getRelatedEvents).subscribe((event: ActivationEnd) => {
|
||||
const dossierId = event.snapshot.paramMap.get('dossierId');
|
||||
const sameIdAsCurrentActive = dossierId === this._activeDossierId$.getValue();
|
||||
|
||||
if (sameIdAsCurrentActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dossierId === null || dossierId === undefined) {
|
||||
return this._activeDossierId$.next(null);
|
||||
}
|
||||
|
||||
// const notFound = !this.all.some(dossier => dossier.id === dossierId);
|
||||
// if (notFound) {
|
||||
// return this._router.navigate(['/main/dossiers']).then();
|
||||
// }
|
||||
|
||||
this._activeDossierId$.next(dossierId);
|
||||
});
|
||||
}
|
||||
|
||||
delete(dossierId: string): Promise<unknown> {
|
||||
return this._dossierControllerService.deleteDossier(dossierId).toPromise();
|
||||
get(): Observable<IDossier[]>;
|
||||
get(dossierId: string): Observable<IDossier>;
|
||||
get(dossierId?: string): Observable<IDossier | IDossier[]> {
|
||||
return dossierId ? super._getOne([dossierId]) : super.getAll();
|
||||
}
|
||||
|
||||
getAll(): Promise<Dossier[]> {
|
||||
return this._dossierControllerService.getDossiers().toPromise();
|
||||
createOrUpdate(dossier: IDossier): Promise<IDossier> {
|
||||
return this._post(dossier).toPromise();
|
||||
}
|
||||
|
||||
getDeleted(): Promise<Dossier[]> {
|
||||
return this._dossierControllerService.getDeletedDossiers().toPromise();
|
||||
getDeleted(): Promise<IDossier[]> {
|
||||
return this.getAll('deleted-dossiers').toPromise();
|
||||
}
|
||||
|
||||
restore(dossierIds: Array<string>): Promise<unknown> {
|
||||
return this._dossierControllerService.restoreDossiers(dossierIds).toPromise();
|
||||
restore(dossierIds: List): Promise<unknown> {
|
||||
return this._post(dossierIds, 'deleted-dossiers/restore').toPromise();
|
||||
}
|
||||
|
||||
hardDelete(dossierIds: Array<string>): Promise<unknown> {
|
||||
return this._dossierControllerService.hardDeleteDossiers(dossierIds).toPromise();
|
||||
hardDelete(dossierIds: List): Promise<unknown> {
|
||||
const body = dossierIds.map<QueryParam>(id => ({ key: 'dossierId', value: id }));
|
||||
return this.delete(body, 'deleted-dossiers/hard-delete', body).toPromise();
|
||||
}
|
||||
|
||||
private _computeStats(entities: List<Dossier>): IDossiersStats {
|
||||
let totalAnalyzedPages = 0;
|
||||
const totalPeople = new Set<string>();
|
||||
|
||||
entities.forEach(dossier => {
|
||||
dossier.memberIds?.forEach(m => totalPeople.add(m));
|
||||
totalAnalyzedPages += dossier.totalNumberOfPages;
|
||||
});
|
||||
|
||||
return {
|
||||
totalPeople: totalPeople.size,
|
||||
totalAnalyzedPages
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { ReanalysisControllerService, StatusControllerService } from '@redaction/red-ui-http';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { File } from '@models/file/file';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { isArray } from 'rxjs/internal-compatibility';
|
||||
import { DossiersDialogService } from './dossiers-dialog.service';
|
||||
import { ConfirmationDialogInput } from '../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FilesService } from './files.service';
|
||||
|
||||
@Injectable()
|
||||
export class FileActionService {
|
||||
@ -15,107 +15,81 @@ export class FileActionService {
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _fileService: FilesService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
private readonly _appStateService: AppStateService
|
||||
) {}
|
||||
|
||||
reanalyseFile(fileStatusWrapper?: FileStatusWrapper) {
|
||||
if (!fileStatusWrapper) {
|
||||
fileStatusWrapper = this._appStateService.activeFile;
|
||||
}
|
||||
return this._reanalysisControllerService.reanalyzeFile(
|
||||
this._appStateService.activeDossier.dossierId,
|
||||
fileStatusWrapper.fileId,
|
||||
true
|
||||
);
|
||||
reanalyseFile(file = this._appStateService.activeFile) {
|
||||
return this._reanalysisControllerService.reanalyzeFile(this._appStateService.activeDossier.id, file.fileId, true);
|
||||
}
|
||||
|
||||
toggleAnalysis(fileStatusWrapper?: FileStatusWrapper) {
|
||||
if (!fileStatusWrapper) {
|
||||
fileStatusWrapper = this._appStateService.activeFile;
|
||||
}
|
||||
return this._reanalysisControllerService.toggleAnalysis(
|
||||
fileStatusWrapper.dossierId,
|
||||
fileStatusWrapper.fileId,
|
||||
!fileStatusWrapper.excluded
|
||||
);
|
||||
toggleAnalysis(file = this._appStateService.activeFile) {
|
||||
return this._reanalysisControllerService.toggleAnalysis(file.dossierId, file.fileId, !file.excluded);
|
||||
}
|
||||
|
||||
async assignToMe(fileStatus?: FileStatusWrapper | FileStatusWrapper[], callback?: Function) {
|
||||
async assignToMe(files?: File[], callback?: Function) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!isArray(fileStatus)) {
|
||||
fileStatus = [fileStatus];
|
||||
}
|
||||
|
||||
const atLeastOneFileHasReviewer = fileStatus.reduce((acc, fs) => acc || !!fs.currentReviewer, false);
|
||||
const atLeastOneFileHasReviewer = files.reduce((acc, fs) => acc || !!fs.currentReviewer, false);
|
||||
if (atLeastOneFileHasReviewer) {
|
||||
const data = new ConfirmationDialogInput({
|
||||
title: _('confirmation-dialog.assign-file-to-me.title'),
|
||||
question: _('confirmation-dialog.assign-file-to-me.question')
|
||||
});
|
||||
this._dialogService.openDialog('confirm', null, data, () => {
|
||||
this._assignReviewerToCurrentUser(fileStatus, callback)
|
||||
this._assignReviewerToCurrentUser(files, callback)
|
||||
.then(() => resolve())
|
||||
.catch(() => reject());
|
||||
});
|
||||
} else {
|
||||
this._assignReviewerToCurrentUser(fileStatus, callback)
|
||||
this._assignReviewerToCurrentUser(files, callback)
|
||||
.then(() => resolve())
|
||||
.catch(() => reject());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setFileUnderApproval(fileStatus: FileStatusWrapper | FileStatusWrapper[], approverId?: string) {
|
||||
if (!isArray(fileStatus)) {
|
||||
fileStatus = [fileStatus];
|
||||
}
|
||||
|
||||
setFilesUnderApproval(files: File[], approverId?: string) {
|
||||
if (!approverId) {
|
||||
approverId = this._appStateService.activeDossier.approverIds[0];
|
||||
}
|
||||
|
||||
return this._statusControllerService.setStatusUnderApprovalForList(
|
||||
fileStatus.map(f => f.fileId),
|
||||
return this._fileService.setUnderApprovalFor(
|
||||
files.map(f => f.fileId),
|
||||
approverId,
|
||||
this._appStateService.activeDossierId
|
||||
);
|
||||
}
|
||||
|
||||
setFileApproved(fileStatus: FileStatusWrapper | FileStatusWrapper[]) {
|
||||
if (!isArray(fileStatus)) {
|
||||
fileStatus = [fileStatus];
|
||||
}
|
||||
return this._statusControllerService.setStatusApprovedForList(
|
||||
fileStatus.map(f => f.fileId),
|
||||
setFilesApproved(files: File[]) {
|
||||
return this._fileService.setApprovedFor(
|
||||
files.map(f => f.fileId),
|
||||
this._appStateService.activeDossierId
|
||||
);
|
||||
}
|
||||
|
||||
setFileUnderReview(fileStatus: FileStatusWrapper | FileStatusWrapper[]) {
|
||||
if (!isArray(fileStatus)) {
|
||||
fileStatus = [fileStatus];
|
||||
}
|
||||
return this._statusControllerService.setStatusUnderReviewForList(
|
||||
fileStatus.map(f => f.fileId),
|
||||
setFilesUnderReview(files: File[]) {
|
||||
return this._fileService.setUnderReviewFor(
|
||||
files.map(f => f.fileId),
|
||||
this._appStateService.activeDossierId
|
||||
);
|
||||
}
|
||||
|
||||
ocrFile(fileStatus: FileStatusWrapper | FileStatusWrapper[]) {
|
||||
if (!isArray(fileStatus)) {
|
||||
fileStatus = [fileStatus];
|
||||
}
|
||||
ocrFiles(files: File[]) {
|
||||
return this._reanalysisControllerService.ocrFiles(
|
||||
fileStatus.map(f => f.fileId),
|
||||
files.map(f => f.fileId),
|
||||
this._appStateService.activeDossierId
|
||||
);
|
||||
}
|
||||
|
||||
assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file?: FileStatusWrapper, callback?: Function, ignoreChanged = false) {
|
||||
const files = file ? [file] : [this._appStateService.activeFile];
|
||||
const data = { mode, files, ignoreChanged };
|
||||
assignFile(
|
||||
mode: 'reviewer' | 'approver',
|
||||
$event: MouseEvent,
|
||||
file = this._appStateService.activeFile,
|
||||
callback?: Function,
|
||||
ignoreChanged = false
|
||||
) {
|
||||
const data = { mode, files: [file], ignoreChanged };
|
||||
this._dialogService.openDialog('assignFile', $event, data, async () => {
|
||||
if (callback) {
|
||||
callback();
|
||||
@ -123,13 +97,10 @@ export class FileActionService {
|
||||
});
|
||||
}
|
||||
|
||||
private async _assignReviewerToCurrentUser(fileStatus: FileStatusWrapper | FileStatusWrapper[], callback?: Function) {
|
||||
if (!isArray(fileStatus)) {
|
||||
fileStatus = [fileStatus];
|
||||
}
|
||||
await this._statusControllerService
|
||||
.setFileReviewerForList(
|
||||
fileStatus.map(f => f.fileId),
|
||||
private async _assignReviewerToCurrentUser(files: File[], callback?: Function) {
|
||||
await this._fileService
|
||||
.setReviewerFor(
|
||||
files.map(f => f.fileId),
|
||||
this._appStateService.activeDossierId,
|
||||
this._userService.currentUser.id
|
||||
)
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { EntitiesService, List, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { IFile } from '@redaction/red-ui-http';
|
||||
import { File } from '@models/file/file';
|
||||
import { TEMPORARY_INJECTOR } from './injector';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FilesService extends EntitiesService<File, IFile> {
|
||||
constructor(protected readonly _injector: Injector) {
|
||||
super(TEMPORARY_INJECTOR(_injector), 'status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status for all files.
|
||||
*/
|
||||
get(): Observable<IFile[]>;
|
||||
/**
|
||||
* Gets the status for a file from a dossier.
|
||||
*/
|
||||
get(dossierId: string, fileId: string): Observable<IFile>;
|
||||
get(dossierId?: string, fileId?: string) {
|
||||
if (dossierId && fileId) {
|
||||
return super._getOne([dossierId, fileId]);
|
||||
}
|
||||
|
||||
return super.getAll();
|
||||
}
|
||||
|
||||
getFor(dossierId: string): Observable<IFile[]>;
|
||||
getFor(dossierIds: List): Observable<Record<string, IFile[]>>;
|
||||
getFor(args: string | List) {
|
||||
if (typeof args === 'string') {
|
||||
return super.getAll(`${this._defaultModelPath}/${args}`);
|
||||
}
|
||||
|
||||
return this._post<Record<string, IFile[]>>(args);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
setUnderApprovalFor(@RequiredParam() body: List, @RequiredParam() approverId: string, @RequiredParam() dossierId: string) {
|
||||
const url = `${this._defaultModelPath}/underapproval/${dossierId}/bulk`;
|
||||
return this._post<unknown>(body, url, [{ key: 'approverId', value: approverId }]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a reviewer for a list of files.
|
||||
*/
|
||||
@Validate()
|
||||
setReviewerFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string, @RequiredParam() reviewerId: string) {
|
||||
return this._post<unknown>(filesIds, `${this._defaultModelPath}/${dossierId}/bulk/${reviewerId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status APPROVED for a list of files.
|
||||
*/
|
||||
@Validate()
|
||||
setApprovedFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string) {
|
||||
return this._post<unknown>(filesIds, `${this._defaultModelPath}/approved/${dossierId}/bulk`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status UNDER_REVIEW for a list of files.
|
||||
*/
|
||||
@Validate()
|
||||
setUnderReviewFor(@RequiredParam() filesIds: List, @RequiredParam() dossierId: string) {
|
||||
return this._post<unknown>(filesIds, `${this._defaultModelPath}/underreview/${dossierId}/bulk`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the deleted files for a dossier.
|
||||
*/
|
||||
@Validate()
|
||||
getDeletedFilesFor(@RequiredParam() dossierId: string): Observable<IFile[]> {
|
||||
return this.getAll(`${this._defaultModelPath}/softdeleted/${dossierId}`);
|
||||
}
|
||||
}
|
||||
16
apps/red-ui/src/app/modules/dossier/services/injector.ts
Normal file
16
apps/red-ui/src/app/modules/dossier/services/injector.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Injector } from "@angular/core";
|
||||
import { FilterService, SearchService } from "@iqser/common-ui";
|
||||
|
||||
/**
|
||||
* This should be removed when refactoring is done
|
||||
* @param injector
|
||||
* @constructor
|
||||
*/
|
||||
export const TEMPORARY_INJECTOR = injector =>
|
||||
Injector.create({
|
||||
providers: [
|
||||
{ provide: FilterService, useClass: FilterService },
|
||||
{ provide: SearchService, useClass: SearchService }
|
||||
],
|
||||
parent: injector
|
||||
});
|
||||
@ -1,11 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import {
|
||||
AddRedactionRequest,
|
||||
DictionaryControllerService,
|
||||
ForceRedactionRequest,
|
||||
ManualRedactionControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { AddRedactionRequest, ForceRedactionRequest, ManualRedactionControllerService } from '@redaction/red-ui-http';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -28,7 +23,6 @@ export class ManualAnnotationService {
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _permissionsService: PermissionsService
|
||||
) {
|
||||
this.CONFIG = {
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { File } from '@models/file/file';
|
||||
|
||||
@Injectable()
|
||||
export class PdfViewerDataService {
|
||||
@ -59,13 +59,7 @@ export class PdfViewerDataService {
|
||||
return of({ pages: [] });
|
||||
}
|
||||
|
||||
downloadOriginalFile(fileStatus: FileStatusWrapper): Observable<any> {
|
||||
return this._fileManagementControllerService.downloadOriginalFile(
|
||||
fileStatus.dossierId,
|
||||
fileStatus.fileId,
|
||||
true,
|
||||
fileStatus.cacheIdentifier,
|
||||
'body'
|
||||
);
|
||||
downloadOriginalFile(file: File): Observable<any> {
|
||||
return this._fileManagementControllerService.downloadOriginalFile(file.dossierId, file.fileId, true, file.cacheIdentifier, 'body');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FileStatus } from '@redaction/red-ui-http';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
export const fileStatusTranslations: { [key in FileStatus.StatusEnum]: string } = {
|
||||
export const fileStatusTranslations: { [key in FileStatus]: string } = {
|
||||
APPROVED: _('file-status.approved'),
|
||||
DELETED: _('file-status.deleted'),
|
||||
ERROR: _('file-status.error'),
|
||||
|
||||
@ -22,7 +22,7 @@ export const loadCompareDocumentWrapper = async (
|
||||
compareDocument,
|
||||
mergedDocument,
|
||||
instance,
|
||||
fileStatus,
|
||||
file,
|
||||
setCompareViewMode: () => void,
|
||||
navigateToPage: () => void,
|
||||
pdfNet: any
|
||||
@ -44,7 +44,7 @@ export const loadCompareDocumentWrapper = async (
|
||||
setCompareViewMode();
|
||||
|
||||
instance.loadDocument(mergedDocumentBuffer, {
|
||||
filename: fileStatus ? fileStatus.filename : 'document.pdf'
|
||||
filename: file?.filename ?? 'document.pdf'
|
||||
});
|
||||
instance.disableElements(['compareButton']);
|
||||
instance.enableElements(['closeCompareButton']);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { TypeValue } from '@models/file/type-value';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-icon',
|
||||
@ -10,7 +10,7 @@ export class AnnotationIconComponent implements OnChanges {
|
||||
@Input() color: string;
|
||||
@Input() type: 'square' | 'rhombus' | 'circle' | 'hexagon' | 'none';
|
||||
@Input() label: string;
|
||||
@Input() dictType: TypeValueWrapper;
|
||||
@Input() dictType: TypeValue;
|
||||
|
||||
@ViewChild('icon', { static: true }) icon: ElementRef;
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { List } from '@redaction/red-ui-http';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-assign-user-dropdown',
|
||||
@ -8,27 +10,27 @@ import { UserService, UserWrapper } from '@services/user.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AssignUserDropdownComponent {
|
||||
oldUser: UserWrapper | string;
|
||||
@Input() options: (UserWrapper | string)[];
|
||||
@Output() save = new EventEmitter<UserWrapper | string>();
|
||||
@Output() cancel = new EventEmitter<never>();
|
||||
private _currentUser: UserWrapper | string;
|
||||
oldUser: User | string;
|
||||
@Input() options: List<User | string>;
|
||||
@Output() readonly save = new EventEmitter<User | string>();
|
||||
@Output() readonly cancel = new EventEmitter();
|
||||
private _currentUser: User | string;
|
||||
|
||||
constructor(private readonly _userService: UserService) {}
|
||||
|
||||
get value(): UserWrapper | string {
|
||||
get value(): User | string {
|
||||
return this._currentUser;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set value(value: UserWrapper | string) {
|
||||
set value(value: User | string) {
|
||||
if (this.oldUser === undefined) {
|
||||
this.oldUser = value;
|
||||
}
|
||||
this._currentUser = value;
|
||||
}
|
||||
|
||||
getContext(user: UserWrapper | string) {
|
||||
getContext(user: User | string) {
|
||||
return { userId: typeof user === 'string' ? user : user?.id };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { Dossier } from '../../../../../state/model/dossier';
|
||||
import { File } from '@models/file/file';
|
||||
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { AutoUnsubscribe, CircleButtonType, CircleButtonTypes } from '@iqser/common-ui';
|
||||
@ -17,8 +17,8 @@ export type MenuState = 'OPEN' | 'CLOSED';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FileDownloadBtnComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
@Input() dossier: DossierWrapper;
|
||||
@Input() file: FileStatusWrapper | FileStatusWrapper[];
|
||||
@Input() dossier: Dossier;
|
||||
@Input() file: File | File[];
|
||||
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
|
||||
@Input() type: CircleButtonType = CircleButtonTypes.default;
|
||||
@Input() tooltipClass: string;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { TypeValueWrapper } from '@models/file/type-value.wrapper';
|
||||
import { TypeValue } from '@models/file/type-value';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dictionary-annotation-icon',
|
||||
@ -19,7 +19,7 @@ export class DictionaryAnnotationIconComponent implements OnChanges {
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.dictionaryKey) {
|
||||
const typeValue: TypeValueWrapper = this._appStateService.getDictionaryTypeValue(this.dictionaryKey, this.dossierTemplateId);
|
||||
const typeValue: TypeValue = this._appStateService.getDictionaryTypeValue(this.dictionaryKey, this.dossierTemplateId);
|
||||
this.color = this._appStateService.getDictionaryColor(this.dictionaryKey, this.dossierTemplateId);
|
||||
this.type = typeValue.hint ? 'circle' : 'square';
|
||||
this.label = this.dictionaryKey[0].toUpperCase();
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
|
||||
import { DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Debounce, IconButtonTypes } from '@iqser/common-ui';
|
||||
import { Debounce, IconButtonTypes, List } from '@iqser/common-ui';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { Dossier } from '@state/model/dossier';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
import ICodeEditor = monaco.editor.ICodeEditor;
|
||||
import IDiffEditor = monaco.editor.IDiffEditor;
|
||||
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
||||
@ -23,14 +23,10 @@ const SMOOTH_SCROLL = 0;
|
||||
export class DictionaryManagerComponent implements OnChanges, OnInit {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
|
||||
@Input()
|
||||
withFloatingActions = true;
|
||||
@Input()
|
||||
initialEntries: string[];
|
||||
@Input()
|
||||
canEdit = false;
|
||||
@Output()
|
||||
saveDictionary = new EventEmitter<string[]>();
|
||||
@Input() withFloatingActions = true;
|
||||
@Input() initialEntries: List;
|
||||
@Input() canEdit = false;
|
||||
@Output() readonly saveDictionary = new EventEmitter<string[]>();
|
||||
|
||||
currentMatch = 0;
|
||||
findMatches: FindMatch[] = [];
|
||||
@ -48,20 +44,15 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
|
||||
private _decorations: string[] = [];
|
||||
private _searchDecorations: string[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _appStateService: AppStateService
|
||||
) {
|
||||
this.currentEntries = this.initialEntries;
|
||||
}
|
||||
constructor(private readonly _dictionaryService: DictionaryService, private readonly _appStateService: AppStateService) {}
|
||||
|
||||
private _dossier: DossierWrapper = this.selectDossier as DossierWrapper;
|
||||
private _dossier: Dossier = this.selectDossier as Dossier;
|
||||
|
||||
get dossier() {
|
||||
return this._dossier;
|
||||
}
|
||||
|
||||
set dossier(dossier: DossierWrapper) {
|
||||
set dossier(dossier: Dossier) {
|
||||
this._dossier = dossier;
|
||||
|
||||
if (dossier === this.selectDossier) {
|
||||
@ -99,6 +90,8 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.currentEntries = [...this.initialEntries];
|
||||
|
||||
this.editorOptions = {
|
||||
theme: 'vs',
|
||||
language: 'text/plain',
|
||||
@ -125,7 +118,7 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
|
||||
}
|
||||
|
||||
revert() {
|
||||
this.currentEntries = this.initialEntries;
|
||||
this.currentEntries = [...this.initialEntries];
|
||||
this.searchChanged('');
|
||||
}
|
||||
|
||||
@ -204,10 +197,10 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
|
||||
this._codeEditor.revealLineInCenter(range.startLineNumber, SMOOTH_SCROLL);
|
||||
}
|
||||
|
||||
private _onDossierChanged({ dossierId, dossierTemplateId }: DossierWrapper): Observable<string> {
|
||||
const dictionary$ = this._dictionaryControllerService.getDictionaryForType(dossierTemplateId, 'dossier_redaction', dossierId);
|
||||
private _onDossierChanged({ id, dossierTemplateId }: Dossier): Observable<string> {
|
||||
const dictionary$ = this._dictionaryService.getFor(dossierTemplateId, 'dossier_redaction', id);
|
||||
|
||||
return dictionary$.pipe(map(data => this._toString(data.entries)));
|
||||
return dictionary$.pipe(map(data => this._toString([...data.entries])));
|
||||
}
|
||||
|
||||
private _toString(entries: string[]) {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy } from '@angular/core';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AutoUnsubscribe } from '@iqser/common-ui';
|
||||
import { User } from '@models/user';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-initials-avatar',
|
||||
@ -20,7 +21,7 @@ export class InitialsAvatarComponent extends AutoUnsubscribe implements OnChange
|
||||
displayName: string;
|
||||
initials: string;
|
||||
colorClass: string;
|
||||
user: UserWrapper;
|
||||
user: User;
|
||||
|
||||
constructor(
|
||||
private readonly _userService: UserService,
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
<div (click)="resetFilters()" *ngIf="showResetFilters$ | async" class="reset-filters" translate="reset-filters"></div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="showCloseButton || actionConfigs || buttonConfigs" class="actions">
|
||||
<div *ngIf="showCloseButton || actionConfigs || buttonConfigs || viewModeSelection" class="actions">
|
||||
<ng-container [ngTemplateOutlet]="viewModeSelection"></ng-container>
|
||||
|
||||
<ng-container *ngFor="let config of buttonConfigs; trackBy: trackByLabel">
|
||||
<iqser-icon-button
|
||||
(action)="config.action($event)"
|
||||
@ -53,10 +55,10 @@
|
||||
|
||||
<ng-template #searchBar>
|
||||
<iqser-input-with-action
|
||||
[(value)]="searchService.searchValue"
|
||||
*ngIf="searchPlaceholder && searchService"
|
||||
[(value)]="searchService.searchValue"
|
||||
[class.mr-8]="searchPosition === searchPositions.beforeFilters"
|
||||
[placeholder]="searchPlaceholder"
|
||||
[width]="searchWidth"
|
||||
[class.mr-8]="searchPosition === searchPositions.beforeFilters"
|
||||
></iqser-input-with-action>
|
||||
</ng-template>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user