Merge remote-tracking branch 'origin/master'

This commit is contained in:
Valentin Mihai 2023-07-17 20:22:19 +03:00
commit e6108be4b8
12 changed files with 91 additions and 55 deletions

View File

@ -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,

View File

@ -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()"

View File

@ -183,3 +183,9 @@
.hide {
visibility: hidden;
}
@media screen and (max-width: 1395px) {
.file-attribute .edit-input form .workflow-input {
width: 63%;
}
}

View File

@ -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 {

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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]);
}

View File

@ -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
View File

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}