Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
e6108be4b8
@ -131,11 +131,17 @@ export const appModuleFactory = (config: AppConfig) => {
|
||||
features: {
|
||||
ANNOTATIONS: {
|
||||
color: 'aqua',
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
level: NgxLoggerLevel.DEBUG,
|
||||
},
|
||||
FILTERS: {
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
},
|
||||
TENANTS: {
|
||||
enabled: false,
|
||||
},
|
||||
ROUTES: {
|
||||
enabled: false,
|
||||
},
|
||||
PDF: {
|
||||
enabled: true,
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
<ng-template #input>
|
||||
<div [ngClass]="{ 'workflow-edit-input': mode === 'workflow' }" class="edit-input" iqserStopPropagation>
|
||||
<form [formGroup]="form">
|
||||
<form (ngSubmit)="form.valid && save()" [formGroup]="form">
|
||||
<iqser-dynamic-input
|
||||
(closedDatepicker)="closedDatepicker = $event"
|
||||
(keydown.escape)="close()"
|
||||
|
||||
@ -183,3 +183,9 @@
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1395px) {
|
||||
.file-attribute .edit-input form .workflow-input {
|
||||
width: 63%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,16 +145,20 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
const fileAttributes = this.file.fileAttributes.attributeIdToValue;
|
||||
Object.keys(fileAttributes).forEach(key => {
|
||||
const attrValue = fileAttributes[key];
|
||||
config[key] = [
|
||||
dayjs(attrValue, 'YYYY-MM-DD', true).isValid() ? dayjs(attrValue).toDate() : attrValue,
|
||||
Validators.pattern(/^(\s+\S+\s*)*(?!\s).*$/),
|
||||
];
|
||||
config[key] = [dayjs(attrValue, 'YYYY-MM-DD', true).isValid() ? dayjs(attrValue).toDate() : attrValue];
|
||||
});
|
||||
return this._formBuilder.group(config, {
|
||||
validators: control =>
|
||||
control.get(this.fileAttribute.id).value &&
|
||||
!control.get(this.fileAttribute.id).value.trim().length &&
|
||||
!this.fileAttributeValue
|
||||
? { emptyString: true }
|
||||
: null,
|
||||
});
|
||||
return this._formBuilder.group(config);
|
||||
}
|
||||
|
||||
#formatAttributeValue(attrValue) {
|
||||
return this.isDate ? attrValue && dayjs(attrValue).format('YYYY-MM-DD') : attrValue.replaceAll(/\s\s+/g, ' ');
|
||||
return this.isDate ? attrValue && dayjs(attrValue).format('YYYY-MM-DD') : attrValue.trim().replaceAll(/\s\s+/g, ' ');
|
||||
}
|
||||
|
||||
#toggleEdit(): void {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div
|
||||
(click)="pageSelected.emit(number)"
|
||||
(dblclick)="toggleReadState()"
|
||||
*ngIf="componentContext$ | async"
|
||||
*ngIf="assigneeChanged$ | async"
|
||||
[class.active]="isActive"
|
||||
[class.read]="read"
|
||||
[id]="'quick-nav-page-' + number"
|
||||
|
||||
@ -1,34 +1,31 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
|
||||
import { Component, effect, EventEmitter, inject, Input, OnChanges, Output } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ViewedPagesService } from '@services/files/viewed-pages.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
||||
import { getConfig } from '@iqser/common-ui';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { map, startWith, tap } from 'rxjs/operators';
|
||||
import { AppConfig, ViewedPage } from '@red/domain';
|
||||
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||
import { pairwise } from 'rxjs';
|
||||
import { Observable, pairwise } from 'rxjs';
|
||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { ContextComponent } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface PageIndicatorContext {
|
||||
isRotated: boolean;
|
||||
assigneeChanged: boolean;
|
||||
}
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-page-indicator',
|
||||
templateUrl: './page-indicator.component.html',
|
||||
styleUrls: ['./page-indicator.component.scss'],
|
||||
})
|
||||
export class PageIndicatorComponent extends ContextComponent<PageIndicatorContext> implements OnChanges, OnInit {
|
||||
export class PageIndicatorComponent implements OnChanges {
|
||||
readonly #config = getConfig<AppConfig>();
|
||||
readonly #logger = inject(NGXLogger);
|
||||
@Input({ required: true }) number: number;
|
||||
@Input() showDottedIcon = false;
|
||||
@Input() activeSelection = false;
|
||||
@Input() read = false;
|
||||
@Output() readonly pageSelected = new EventEmitter<number>();
|
||||
pageReadTimeout: number = null;
|
||||
readonly assigneeChanged$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
private readonly _viewedPagesService: ViewedPagesService,
|
||||
@ -38,38 +35,45 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
|
||||
private readonly _pdf: PdfViewer,
|
||||
readonly pageRotationService: PageRotationService,
|
||||
) {
|
||||
super();
|
||||
this.assigneeChanged$ = this._state.file$.pipe(
|
||||
pairwise(),
|
||||
map(([prevFile, currFile]) => prevFile.assignee !== currFile.assignee),
|
||||
tap(assigneeChanged => assigneeChanged && this.handlePageRead()),
|
||||
startWith(true),
|
||||
map(() => true),
|
||||
);
|
||||
|
||||
effect(() => {
|
||||
if (this.isActive) {
|
||||
this.handlePageRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
return this.number === this._pdf.currentPage();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const assigneeChanged$ = this._state.file$.pipe(
|
||||
pairwise(),
|
||||
map(([prevFile, currFile]) => prevFile.assignee !== currFile.assignee),
|
||||
tap(assigneeChanged => assigneeChanged && this.handlePageRead()),
|
||||
);
|
||||
super._initContext({ assigneeChanged: assigneeChanged$ });
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.handlePageRead();
|
||||
}
|
||||
|
||||
async toggleReadState() {
|
||||
if (this._permissionService.canMarkPagesAsViewed(this._state.file())) {
|
||||
if (this.read) {
|
||||
await this.#markPageUnread();
|
||||
} else {
|
||||
await this.#markPageRead();
|
||||
}
|
||||
if (!this._permissionService.canMarkPagesAsViewed(this._state.file())) {
|
||||
this.#logger.info('[PAGES] Cannot toggle read state');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.read) {
|
||||
await this.#markPageUnread();
|
||||
} else {
|
||||
await this.#markPageRead();
|
||||
}
|
||||
}
|
||||
|
||||
handlePageRead(): void {
|
||||
if (!this._permissionService.canMarkPagesAsViewed(this._state.file())) {
|
||||
this.#logger.info('[PAGES] Cannot mark pages as read');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -87,6 +91,8 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
|
||||
}
|
||||
|
||||
async #markPageRead() {
|
||||
this.#logger.info('[PAGES] Mark page read', this.number);
|
||||
|
||||
const fileId = this._state.fileId;
|
||||
await this._viewedPagesService.add({ page: this.number }, this._state.dossierId, fileId);
|
||||
const viewedPage = new ViewedPage({ page: this.number, fileId });
|
||||
@ -94,6 +100,8 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
|
||||
}
|
||||
|
||||
async #markPageUnread() {
|
||||
this.#logger.info('[PAGES] Mark page unread', this.number);
|
||||
|
||||
const fileId = this._state.fileId;
|
||||
await this._viewedPagesService.remove(this._state.dossierId, fileId, this.number);
|
||||
this._viewedPagesMapService.delete(fileId, this.number);
|
||||
|
||||
@ -136,7 +136,7 @@ export class REDAnnotationManager {
|
||||
|
||||
#autoSelectRectangleAfterCreation() {
|
||||
this.#manager.addEventListener('annotationChanged', (annotations: Annotation[], action: string, options) => {
|
||||
this.#logger.info('[PDF] Annotations changed: ', annotations, action, options);
|
||||
this.#logger.info('[ANNOTATIONS] Annotations changed: ', annotations, action, options);
|
||||
// when a rectangle is drawn,
|
||||
// it returns one annotation with tool name 'AnnotationCreateRectangle;
|
||||
// this will auto select rectangle after drawing
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Dossier, DownloadFileType, DownloadFileTypes, File, IReportTemplate, WorkflowFileStatuses } from '@red/domain';
|
||||
import { downloadTypesForDownloadTranslations } from '@translations/download-types-translations';
|
||||
import { ReportTemplateService } from '@services/report-template.service';
|
||||
@ -7,6 +7,7 @@ import { DefaultColorsService } from '@services/entity-services/default-colors.s
|
||||
import { IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { Roles } from '@users/roles';
|
||||
import { List } from '@iqser/common-ui/lib/utils';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
export interface DownloadDialogData {
|
||||
readonly dossier: Dossier;
|
||||
@ -24,10 +25,13 @@ export interface DownloadDialogResult {
|
||||
styleUrls: ['./download-dialog.component.scss'],
|
||||
})
|
||||
export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialogComponent, DownloadDialogData, DownloadDialogResult> {
|
||||
readonly #logger = inject(NGXLogger);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly downloadTypes: { key: DownloadFileType; label: string }[] = this.#formDownloadTypes;
|
||||
readonly availableReportTypes = this.#availableReportTypes;
|
||||
readonly form = this.#getForm();
|
||||
readonly hasApprovedFiles = this.data.files.some(file => file.workflowStatus === WorkflowFileStatuses.APPROVED);
|
||||
|
||||
initialFormValue = this.form.getRawValue();
|
||||
readonly roles = Roles;
|
||||
|
||||
@ -52,12 +56,12 @@ export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialog
|
||||
}
|
||||
|
||||
get invalidDownload() {
|
||||
return (
|
||||
!this.form.controls.reportTemplateIds.value.length &&
|
||||
!!this.form.controls.downloadFileTypes.value.length &&
|
||||
this.form.controls.downloadFileTypes.value.every(type => type === DownloadFileTypes.REDACTED) &&
|
||||
!this.data.files.some(file => file.workflowStatus === WorkflowFileStatuses.APPROVED)
|
||||
);
|
||||
const hasReportTypes = this.form.controls.reportTemplateIds.value.length;
|
||||
const downloadFileTypes = this.form.controls.downloadFileTypes.value;
|
||||
const onlyRedactedVersionSelected =
|
||||
downloadFileTypes.length && downloadFileTypes.every(type => type === DownloadFileTypes.REDACTED);
|
||||
|
||||
return !hasReportTypes || (onlyRedactedVersionSelected && !this.hasApprovedFiles);
|
||||
}
|
||||
|
||||
override get valid() {
|
||||
@ -77,7 +81,7 @@ export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialog
|
||||
label: downloadTypesForDownloadTranslations[type],
|
||||
}))
|
||||
.filter(type => {
|
||||
if (!this.data.files.some(file => file.workflowStatus === WorkflowFileStatuses.APPROVED)) {
|
||||
if (!this.hasApprovedFiles) {
|
||||
return type.key !== DownloadFileTypes.REDACTED;
|
||||
}
|
||||
return true;
|
||||
@ -86,7 +90,7 @@ export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialog
|
||||
|
||||
get #selectedDownloadTypes() {
|
||||
return this.data.dossier.downloadFileTypes.filter(type => {
|
||||
if (!this.data.files.some(file => file.workflowStatus === WorkflowFileStatuses.APPROVED)) {
|
||||
if (!this.hasApprovedFiles) {
|
||||
return type !== DownloadFileTypes.REDACTED;
|
||||
}
|
||||
return true;
|
||||
@ -99,18 +103,20 @@ export class DownloadDialogComponent extends IqserDialogComponent<DownloadDialog
|
||||
|
||||
reportTemplateValueMapper = (reportTemplate: IReportTemplate) => reportTemplate.templateId;
|
||||
|
||||
close() {
|
||||
override close() {
|
||||
const result: DownloadDialogResult = {
|
||||
reportTemplateIds: this.form.controls.reportTemplateIds.value,
|
||||
downloadFileTypes: this.form.controls.downloadFileTypes.value,
|
||||
redactionPreviewColor: this.form.controls.redactionPreviewColor.value,
|
||||
};
|
||||
|
||||
this.#logger.info('[DOWNLOAD] Closing with result', result);
|
||||
|
||||
if (result.reportTemplateIds.length === 0) {
|
||||
return this.dialogRef.close();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
this.dialogRef.close(result);
|
||||
super.close(result);
|
||||
}
|
||||
|
||||
#hasReportTemplateOrDownloadType(control: AbstractControl) {
|
||||
|
||||
@ -31,7 +31,7 @@ export class FilesService extends EntitiesService<IFile, File> {
|
||||
loadAll(dossierId: string) {
|
||||
const files$ = this.getFor(dossierId).pipe(
|
||||
mapEach(file => new File(file, this._userService.getName(file.assignee))),
|
||||
tap(() => this._logger.info('[FILE] Loaded')),
|
||||
tap(file => this._logger.info('[FILE] Loaded', file)),
|
||||
);
|
||||
const loadStats$ = files$.pipe(switchMap(files => this._dossierStatsService.getFor([dossierId]).pipe(map(() => files))));
|
||||
return loadStats$.pipe(tap(files => this._filesMapService.set(dossierId, files)));
|
||||
@ -41,7 +41,7 @@ export class FilesService extends EntitiesService<IFile, File> {
|
||||
const _file = await firstValueFrom(super._getOne([dossierId, file.id]));
|
||||
const reloadedFile = new File(_file, this._userService.getName(_file.assignee));
|
||||
await firstValueFrom(this._dossierStatsService.getFor([dossierId]));
|
||||
this._logger.info('[FILE] Reloaded');
|
||||
this._logger.info('[FILE] Reloaded', reloadedFile);
|
||||
return this._filesMapService.replace(dossierId, [reloadedFile]);
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"ADMIN_CONTACT_NAME": null,
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dev-04.iqser.cloud",
|
||||
"APP_NAME": "RedactManager",
|
||||
"API_URL": "https://dan.iqser.cloud",
|
||||
"APP_NAME": "RedactManager very very very very long",
|
||||
"AUTO_READ_TIME": 3,
|
||||
"BACKEND_APP_VERSION": "4.4.40",
|
||||
"EULA_URL": "EULA_URL",
|
||||
@ -11,7 +11,7 @@
|
||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"OAUTH_IDP_HINT": null,
|
||||
"OAUTH_URL": "https://dev-04.iqser.cloud/auth",
|
||||
"OAUTH_URL": "https://dan.iqser.cloud/auth",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural",
|
||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit a8f5fb2e25cd1f150c1099d387b4f9dece3b922c
|
||||
Subproject commit d3ac14497d784fda844da062f9919d4391a425d9
|
||||
6
renovate.json
Normal file
6
renovate.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user