Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
cea0b23b86
@ -5,8 +5,8 @@ import { BaseScreenComponent } from '@components/base-screen/base-screen.compone
|
|||||||
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
||||||
import { DossiersGuard } from '@guards/dossiers.guard';
|
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
|
||||||
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
|
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
|
||||||
import { FeaturesGuard } from '@guards/features-guard.service';
|
import { FeaturesGuard } from '@guards/features-guard.service';
|
||||||
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
||||||
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
|
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
|
||||||
@ -14,7 +14,7 @@ import { DashboardGuard } from '@guards/dashboard-guard.service';
|
|||||||
import { TrashGuard } from '@guards/trash.guard';
|
import { TrashGuard } from '@guards/trash.guard';
|
||||||
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
|
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
|
||||||
import { DossierFilesGuard } from '@guards/dossier-files-guard';
|
import { DossierFilesGuard } from '@guards/dossier-files-guard';
|
||||||
import { WebViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
|
import { webViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { mainResolver } from '@utils/main.resolver';
|
import { mainResolver } from '@utils/main.resolver';
|
||||||
import { hasAnyRoleGuard, IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
import { hasAnyRoleGuard, IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
@ -26,10 +26,8 @@ import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
|
|||||||
const dossierTemplateIdRoutes: IqserRoutes = [
|
const dossierTemplateIdRoutes: IqserRoutes = [
|
||||||
{
|
{
|
||||||
path: `${DOSSIERS_ROUTE}`,
|
path: `${DOSSIERS_ROUTE}`,
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [loadActiveDossiersGuard(), IqserPermissionsGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [DossiersGuard],
|
|
||||||
dossiersService: ACTIVE_DOSSIERS_SERVICE,
|
|
||||||
permissions: {
|
permissions: {
|
||||||
allow: [Roles.files.readStatus],
|
allow: [Roles.files.readStatus],
|
||||||
redirectTo: '/auth-error',
|
redirectTo: '/auth-error',
|
||||||
@ -53,9 +51,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, webViewerLoadedGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [DossierFilesGuard, WebViewerLoadedGuard],
|
routeGuards: [DossierFilesGuard],
|
||||||
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
|
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
|
||||||
dossiersService: ACTIVE_DOSSIERS_SERVICE,
|
dossiersService: ACTIVE_DOSSIERS_SERVICE,
|
||||||
permissions: {
|
permissions: {
|
||||||
@ -78,10 +76,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
{
|
{
|
||||||
path: `${ARCHIVE_ROUTE}`,
|
path: `${ARCHIVE_ROUTE}`,
|
||||||
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
|
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
|
||||||
canActivate: [CompositeRouteGuard, WebViewerLoadedGuard],
|
canActivate: [CompositeRouteGuard, webViewerLoadedGuard(), loadArchivedDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [FeaturesGuard, DossiersGuard],
|
routeGuards: [FeaturesGuard],
|
||||||
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
|
|
||||||
features: [DOSSIERS_ARCHIVE],
|
features: [DOSSIERS_ARCHIVE],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -150,9 +147,9 @@ const mainRoutes: IqserRoutes = [
|
|||||||
{
|
{
|
||||||
path: 'search',
|
path: 'search',
|
||||||
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
|
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
permissions: {
|
permissions: {
|
||||||
allow: [Roles.search],
|
allow: [Roles.search],
|
||||||
redirectTo: '/auth-error',
|
redirectTo: '/auth-error',
|
||||||
@ -162,10 +159,9 @@ const mainRoutes: IqserRoutes = [
|
|||||||
{
|
{
|
||||||
path: 'trash',
|
path: 'trash',
|
||||||
loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule),
|
loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule),
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadActiveDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard, TrashGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
|
||||||
dossiersService: ACTIVE_DOSSIERS_SERVICE,
|
|
||||||
permissions: {
|
permissions: {
|
||||||
allow: [Roles.dossiers.read, Roles.files.readStatus],
|
allow: [Roles.dossiers.read, Roles.files.readStatus],
|
||||||
redirectTo: '/auth-error',
|
redirectTo: '/auth-error',
|
||||||
|
|||||||
@ -1,45 +1,57 @@
|
|||||||
import { Injectable, Injector, ProviderToken } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||||
import { firstValueFrom, forkJoin } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { take } from 'rxjs/operators';
|
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||||
import { ARCHIVE_ROUTE, DOSSIER_TEMPLATE_ID } from '@red/domain';
|
|
||||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from '../tokens';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
export function loadAllDossiersGuard(): CanActivateFn {
|
||||||
export class DossiersGuard implements CanActivate {
|
return async () => {
|
||||||
constructor(
|
const logger = inject(NGXLogger);
|
||||||
private readonly _injector: Injector,
|
logger.info('[GUARDS] loadAllDossiersGuard start');
|
||||||
private readonly _router: Router,
|
|
||||||
private readonly _tenantsService: TenantsService,
|
|
||||||
private readonly _dashboardStatsService: DashboardStatsService,
|
|
||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
|
||||||
private readonly _archivedDossiersService: ArchivedDossiersService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
|
const services = [inject(ArchivedDossiersService), inject(ActiveDossiersService)];
|
||||||
const token: ProviderToken<DossiersService> = route.data.dossiersService;
|
const requests = services.map(service => firstValueFrom(service.loadAll()));
|
||||||
if (!token) {
|
await Promise.all(requests);
|
||||||
const services = [this._archivedDossiersService, this._activeDossiersService];
|
|
||||||
const loading$ = forkJoin(services.map(service => service.loadAll().pipe(take(1))));
|
|
||||||
await firstValueFrom(loading$);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
|
logger.info('[GUARDS] loadAllDossiersGuard end');
|
||||||
const isArchive = dossiersService.routerPath === ARCHIVE_ROUTE;
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadActiveDossiersGuard(): CanActivateFn {
|
||||||
|
return async () => {
|
||||||
|
const logger = inject(NGXLogger);
|
||||||
|
logger.info('[GUARDS] loadDossiersGuard start');
|
||||||
|
|
||||||
|
await firstValueFrom(inject<ActiveDossiersService>(ACTIVE_DOSSIERS_SERVICE).loadAll());
|
||||||
|
|
||||||
|
logger.info('[GUARDS] loadDossiersGuard end');
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadArchivedDossiersGuard(): CanActivateFn {
|
||||||
|
return async (route: ActivatedRouteSnapshot) => {
|
||||||
|
const logger = inject(NGXLogger);
|
||||||
|
logger.info('[GUARDS] loadArchivedDossiersGuard start');
|
||||||
|
|
||||||
|
const dossiersService = inject<ArchivedDossiersService>(ARCHIVED_DOSSIERS_SERVICE);
|
||||||
const dossierTemplateId = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
const dossierTemplateId = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||||
const dossierTemplateStats = this._dashboardStatsService.find(dossierTemplateId);
|
const dossierTemplateStats = inject(DashboardStatsService).find(dossierTemplateId);
|
||||||
|
|
||||||
if (isArchive && dossierTemplateStats?.numberOfArchivedDossiers === 0) {
|
if (dossierTemplateStats?.numberOfArchivedDossiers === 0) {
|
||||||
await this._router.navigate([this._tenantsService.activeTenantId, 'main', dossierTemplateId, 'dossiers']);
|
logger.info('[GUARDS] loadArchivedDossiersGuard no archived dossiers, redirect to active dossiers page');
|
||||||
|
await inject(Router).navigate([inject(TenantsService).activeTenantId, 'main', dossierTemplateId, 'dossiers']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await firstValueFrom(dossiersService.loadAll());
|
await firstValueFrom(dossiersService.loadAll());
|
||||||
|
logger.info('[GUARDS] loadArchivedDossiersGuard end');
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { LicenseService } from '@services/license.service';
|
|||||||
export function ifLoggedIn(): CanActivateFn {
|
export function ifLoggedIn(): CanActivateFn {
|
||||||
return async (route: ActivatedRouteSnapshot) => {
|
return async (route: ActivatedRouteSnapshot) => {
|
||||||
const logger = inject(NGXLogger);
|
const logger = inject(NGXLogger);
|
||||||
logger.info('[ROUTES] Check if can activate main');
|
logger.info('[ROUTES] Check if can activate route', route);
|
||||||
|
|
||||||
const tenantsService = inject(TenantsService);
|
const tenantsService = inject(TenantsService);
|
||||||
const keycloakService = inject(KeycloakService);
|
const keycloakService = inject(KeycloakService);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div #viewer class="viewer" id="viewer"></div>
|
<div #viewer class="viewer"></div>
|
||||||
|
|
||||||
<redaction-paginator (changePage)="navigateTo($event)" *ngIf="loaded$ | async"></redaction-paginator>
|
<redaction-paginator (changePage)="navigateTo($event)" *ngIf="loaded$ | async"></redaction-paginator>
|
||||||
|
|
||||||
@ -54,30 +54,30 @@
|
|||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
|
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="alignment-buttons" [class.disabled]="form.controls.horizontalTextAlignment.disabled">
|
<div [class.disabled]="form.controls.horizontalTextAlignment.disabled" class="alignment-buttons">
|
||||||
<div
|
<div
|
||||||
|
(click)="alignHorizontally(alignment)"
|
||||||
*ngFor="let alignment of watermarkHorizontalAlignments"
|
*ngFor="let alignment of watermarkHorizontalAlignments"
|
||||||
[class.active]="currentAlignment.horizontal === alignment"
|
[class.active]="currentAlignment.horizontal === alignment"
|
||||||
|
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
|
||||||
[matTooltipPosition]="'above'"
|
[matTooltipPosition]="'above'"
|
||||||
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
|
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
|
||||||
[ngClass]="'horizontal-' + alignment.toLowerCase()"
|
[ngClass]="'horizontal-' + alignment.toLowerCase()"
|
||||||
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
|
|
||||||
class="alignment"
|
class="alignment"
|
||||||
(click)="alignHorizontally(alignment)"
|
|
||||||
>
|
>
|
||||||
<mat-icon [svgIcon]="'red:align-horizontal-' + alignment.toLowerCase()"></mat-icon>
|
<mat-icon [svgIcon]="'red:align-horizontal-' + alignment.toLowerCase()"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alignment-buttons" [class.disabled]="form.controls.verticalTextAlignment.disabled">
|
<div [class.disabled]="form.controls.verticalTextAlignment.disabled" class="alignment-buttons">
|
||||||
<div
|
<div
|
||||||
|
(click)="alignVertically(alignment)"
|
||||||
*ngFor="let alignment of watermarkVerticalAlignments"
|
*ngFor="let alignment of watermarkVerticalAlignments"
|
||||||
[class.active]="currentAlignment.vertical === alignment"
|
[class.active]="currentAlignment.vertical === alignment"
|
||||||
|
[class.disabled]="form.controls.verticalTextAlignment.disabled"
|
||||||
[matTooltipPosition]="'above'"
|
[matTooltipPosition]="'above'"
|
||||||
[matTooltip]="translations.VERTICAL[alignment] | translate"
|
[matTooltip]="translations.VERTICAL[alignment] | translate"
|
||||||
[ngClass]="'vertical-' + alignment.toLowerCase()"
|
[ngClass]="'vertical-' + alignment.toLowerCase()"
|
||||||
[class.disabled]="form.controls.verticalTextAlignment.disabled"
|
|
||||||
class="alignment"
|
class="alignment"
|
||||||
(click)="alignVertically(alignment)"
|
|
||||||
>
|
>
|
||||||
<mat-icon [svgIcon]="'red:align-vertical-' + alignment.toLowerCase()"></mat-icon>
|
<mat-icon [svgIcon]="'red:align-vertical-' + alignment.toLowerCase()"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
TemplateRef,
|
TemplateRef,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||||
import {
|
import {
|
||||||
CircleButtonTypes,
|
CircleButtonTypes,
|
||||||
ConfirmOption,
|
ConfirmOption,
|
||||||
@ -20,6 +20,7 @@ import {
|
|||||||
ErrorService,
|
ErrorService,
|
||||||
HelpModeService,
|
HelpModeService,
|
||||||
IConfirmationDialogData,
|
IConfirmationDialogData,
|
||||||
|
IqserDialog,
|
||||||
LoadingService,
|
LoadingService,
|
||||||
Toaster,
|
Toaster,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
@ -63,7 +64,6 @@ import { ConfigService } from '@services/config.service';
|
|||||||
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
|
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { SuggestionsService } from './services/suggestions.service';
|
import { SuggestionsService } from './services/suggestions.service';
|
||||||
import { IqserDialog } from '../../../../../../libs/common-ui/src/lib/dialog/iqser-dialog.service';
|
|
||||||
import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component';
|
import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
|
import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
|
||||||
@ -108,7 +108,6 @@ export class FilePreviewScreenComponent
|
|||||||
private readonly _annotationManager: REDAnnotationManager,
|
private readonly _annotationManager: REDAnnotationManager,
|
||||||
private readonly _errorService: ErrorService,
|
private readonly _errorService: ErrorService,
|
||||||
private readonly _filterService: FilterService,
|
private readonly _filterService: FilterService,
|
||||||
private readonly _activatedRoute: ActivatedRoute,
|
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _filesMapService: FilesMapService,
|
private readonly _filesMapService: FilesMapService,
|
||||||
private readonly _dossiersService: DossiersService,
|
private readonly _dossiersService: DossiersService,
|
||||||
@ -161,6 +160,7 @@ export class FilePreviewScreenComponent
|
|||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const selectedText = this._documentViewer.selectedText();
|
const selectedText = this._documentViewer.selectedText();
|
||||||
|
console.log('selectedText', selectedText);
|
||||||
const canPerformActions = this.pdfProxyService.canPerformActions();
|
const canPerformActions = this.pdfProxyService.canPerformActions();
|
||||||
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
|
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
<div id="viewer"></div>
|
|
||||||
|
|
||||||
<redaction-compare-file-input></redaction-compare-file-input>
|
<redaction-compare-file-input></redaction-compare-file-input>
|
||||||
|
|
||||||
<redaction-paginator></redaction-paginator>
|
<redaction-paginator></redaction-paginator>
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
:host {
|
|
||||||
--workload-width: 350px;
|
|
||||||
--header-height: calc(var(--iqser-top-bar-height) + 50px);
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
width: calc(100% - var(--workload-width));
|
|
||||||
height: calc(100% - var(--header-height));
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
@ -11,7 +11,6 @@ import { PaginatorComponent } from './components/paginator/paginator.component';
|
|||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||||
import { REDDocumentViewer } from './services/document-viewer.service';
|
import { REDDocumentViewer } from './services/document-viewer.service';
|
||||||
import { WebViewerLoadedGuard } from './services/webviewer-loaded.guard';
|
|
||||||
import { ReadableRedactionsService } from './services/readable-redactions.service';
|
import { ReadableRedactionsService } from './services/readable-redactions.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -27,7 +26,6 @@ import { ReadableRedactionsService } from './services/readable-redactions.servic
|
|||||||
ReadableRedactionsService,
|
ReadableRedactionsService,
|
||||||
ViewerHeaderService,
|
ViewerHeaderService,
|
||||||
AnnotationDrawService,
|
AnnotationDrawService,
|
||||||
WebViewerLoadedGuard,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PdfViewerModule {}
|
export class PdfViewerModule {}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { inject, Injectable, NgZone, Signal, signal } from '@angular/core';
|
import { effect, inject, Injectable, Signal, signal } from '@angular/core';
|
||||||
import { Core } from '@pdftron/webviewer';
|
import { Core } from '@pdftron/webviewer';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { fromEvent, Observable } from 'rxjs';
|
import { fromEvent, Observable } from 'rxjs';
|
||||||
@ -25,7 +25,6 @@ export class REDDocumentViewer {
|
|||||||
readonly #userPreferenceService = inject(UserPreferenceService);
|
readonly #userPreferenceService = inject(UserPreferenceService);
|
||||||
readonly #pdf = inject(PdfViewer);
|
readonly #pdf = inject(PdfViewer);
|
||||||
readonly #activatedRoute = inject(ActivatedRoute);
|
readonly #activatedRoute = inject(ActivatedRoute);
|
||||||
readonly #ngZone = inject(NgZone);
|
|
||||||
readonly loaded$: Observable<boolean>;
|
readonly loaded$: Observable<boolean>;
|
||||||
keyUp$: Observable<KeyboardEvent>;
|
keyUp$: Observable<KeyboardEvent>;
|
||||||
readonly selectedText: Signal<string>;
|
readonly selectedText: Signal<string>;
|
||||||
@ -37,6 +36,21 @@ export class REDDocumentViewer {
|
|||||||
this.loaded$ = toObservable(this.#loaded);
|
this.loaded$ = toObservable(this.#loaded);
|
||||||
this.pageComplete = this.#pageComplete.asReadonly();
|
this.pageComplete = this.#pageComplete.asReadonly();
|
||||||
this.selectedText = this.#selectedText.asReadonly();
|
this.selectedText = this.#selectedText.asReadonly();
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
const viewerElement = document.getElementById('viewer');
|
||||||
|
if (!viewerElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.loaded()) {
|
||||||
|
this.#logger.info('[PDF] Show viewer');
|
||||||
|
viewerElement.style.visibility = 'visible';
|
||||||
|
} else {
|
||||||
|
this.#logger.info('[PDF] Hide viewer');
|
||||||
|
viewerElement.style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get PDFDoc() {
|
get PDFDoc() {
|
||||||
|
|||||||
@ -1,41 +1,42 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { CanActivate, Router } from '@angular/router';
|
import { CanActivateFn, ResolveFn, Router } from '@angular/router';
|
||||||
import { REDDocumentViewer } from './document-viewer.service';
|
import { REDDocumentViewer } from './document-viewer.service';
|
||||||
import { PdfViewer } from './pdf-viewer.service';
|
import { PdfViewer } from './pdf-viewer.service';
|
||||||
import { REDAnnotationManager } from './annotation-manager.service';
|
import { REDAnnotationManager } from './annotation-manager.service';
|
||||||
import { ViewerHeaderService } from './viewer-header.service';
|
import { ViewerHeaderService } from './viewer-header.service';
|
||||||
import { LoadingService } from '@iqser/common-ui';
|
import { LoadingService } from '@iqser/common-ui';
|
||||||
import { WebViewerInstance } from '@pdftron/webviewer';
|
import { WebViewerInstance } from '@pdftron/webviewer';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
|
||||||
@Injectable()
|
export function webViewerLoadedGuard(): CanActivateFn | ResolveFn<boolean> {
|
||||||
export class WebViewerLoadedGuard implements CanActivate {
|
return async (_, state) => {
|
||||||
constructor(
|
const pdf = inject(PdfViewer);
|
||||||
private readonly _documentViewer: REDDocumentViewer,
|
const logger = inject(NGXLogger);
|
||||||
private readonly _pdf: PdfViewer,
|
|
||||||
private readonly _router: Router,
|
|
||||||
private readonly _annotationManager: REDAnnotationManager,
|
|
||||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(_, state) {
|
if (pdf.instance) {
|
||||||
if (this._pdf.instance) {
|
logger.info('[PDF] WebViewerGuard already loaded.');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadingService.start();
|
inject(LoadingService).start();
|
||||||
|
const router = inject(Router);
|
||||||
|
const annotationManager = inject(REDAnnotationManager);
|
||||||
|
const documentViewer = inject(REDDocumentViewer);
|
||||||
|
const viewerHeaderService = inject(ViewerHeaderService);
|
||||||
|
|
||||||
let instance: WebViewerInstance | undefined;
|
let instance: WebViewerInstance | undefined;
|
||||||
try {
|
try {
|
||||||
instance = await this._pdf.init(document.getElementById('viewer'));
|
instance = await pdf.init(inject(DOCUMENT).getElementById('viewer'));
|
||||||
} catch {
|
} catch (e) {
|
||||||
return this._router.navigateByUrl(state.url);
|
logger.warn('[PDF] WebViewerGuard error: ', e, 'redirecting to', state.url);
|
||||||
|
return router.navigateByUrl(state.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._annotationManager.init(instance.Core.annotationManager);
|
annotationManager.init(instance.Core.annotationManager);
|
||||||
this._documentViewer.init(instance.Core.documentViewer);
|
documentViewer.init(instance.Core.documentViewer);
|
||||||
this._viewerHeaderService.init();
|
viewerHeaderService.init();
|
||||||
|
|
||||||
return !!this._pdf.instance;
|
return !!pdf.instance;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,15 +144,11 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
|||||||
|
|
||||||
this._dialogService.openDialog('confirm', data, async () => {
|
this._dialogService.openDialog('confirm', data, async () => {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await firstValueFrom(this._trashService.deleteDossier(this.dossier));
|
const successful = await this._trashService.deleteDossier(this.dossier);
|
||||||
this._editDossierDialogRef.close();
|
if (successful) {
|
||||||
await this._router.navigate([`/${this._tenantsService.activeTenantId}${this.dossier.dossiersListRouterLink}`]);
|
await this.#closeDialogAndRedirectToDossier();
|
||||||
|
}
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
this._toaster.success(_('edit-dossier-dialog.delete-successful'), {
|
|
||||||
params: {
|
|
||||||
dossierName: this.dossier.dossierName,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +189,16 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
|||||||
return this._dossierStatesMapService.get(this.dossier.dossierTemplateId, stateId).color;
|
return this._dossierStatesMapService.get(this.dossier.dossierTemplateId, stateId).color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async #closeDialogAndRedirectToDossier() {
|
||||||
|
this._editDossierDialogRef.close();
|
||||||
|
await this._router.navigate([`/${this._tenantsService.activeTenantId}${this.dossier.dossiersListRouterLink}`]);
|
||||||
|
this._toaster.success(_('edit-dossier-dialog.delete-successful'), {
|
||||||
|
params: {
|
||||||
|
dossierName: this.dossier.dossierName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#getForm(): UntypedFormGroup {
|
#getForm(): UntypedFormGroup {
|
||||||
const formFieldWithArchivedCheck = value => ({ value, disabled: !this.dossier.isActive });
|
const formFieldWithArchivedCheck = value => ({ value, disabled: !this.dossier.isActive });
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { EntitiesService, QueryParam, Toaster } from '@iqser/common-ui';
|
import { EntitiesService, QueryParam, Toaster } from '@iqser/common-ui';
|
||||||
import { Dossier, File, IDossier, IFile, TrashDossier, TrashFile, TrashItem } from '@red/domain';
|
import { Dossier, File, IDossier, IFile, TrashDossier, TrashFile, TrashItem } from '@red/domain';
|
||||||
import { catchError, switchMap, take, tap } from 'rxjs/operators';
|
import { catchError, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { forkJoin, map, Observable, of } from 'rxjs';
|
import { firstValueFrom, forkJoin, map, Observable, of } from 'rxjs';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { PermissionsService } from '../permissions.service';
|
import { PermissionsService } from '../permissions.service';
|
||||||
import { ActiveDossiersService } from '../dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '../dossiers/active-dossiers.service';
|
||||||
@ -31,18 +31,20 @@ export class TrashService extends EntitiesService<TrashItem, TrashItem> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteDossier(dossier: Dossier): Observable<unknown> {
|
deleteDossier(dossier: Dossier) {
|
||||||
const showToast = () => {
|
const showToast = () => {
|
||||||
this._toaster.error(_('dossier-listing.delete.delete-failed'), { params: { dossierName: dossier.dossierName } });
|
this._toaster.error(_('dossier-listing.delete.delete-failed'), { params: { dossierName: dossier.dossierName } });
|
||||||
return of({});
|
return of(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const reloadDossiers$ = dossier.isActive ? this._activeDossiersService.loadAll() : this._archivedDossiersService.loadAll();
|
const reloadDossiers$ = dossier.isActive ? this._activeDossiersService.loadAll() : this._archivedDossiersService.loadAll();
|
||||||
|
|
||||||
return this.delete(dossier.id, 'dossier').pipe(
|
const request$ = this.delete(dossier.id, 'dossier').pipe(
|
||||||
switchMap(() => reloadDossiers$),
|
switchMap(() => reloadDossiers$),
|
||||||
catchError(showToast),
|
catchError(showToast),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return firstValueFrom(request$);
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(items: TrashItem[]): Observable<unknown> {
|
restore(items: TrashItem[]): Observable<unknown> {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { GenericService } from '@iqser/common-ui';
|
import { GenericService } from '@iqser/common-ui';
|
||||||
import { ILicense, ILicenseReport, ILicenseReportRequest, ILicenses } from '@red/domain';
|
import { ILicense, ILicenseReport, ILicenseReportRequest, ILicenses } from '@red/domain';
|
||||||
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
|
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
|
||||||
@ -39,6 +39,10 @@ const defaultOnError: ILicenses = {
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class LicenseService extends GenericService<ILicenseReport> {
|
export class LicenseService extends GenericService<ILicenseReport> {
|
||||||
|
readonly #licenseData$ = new BehaviorSubject<ILicenses | undefined>(undefined);
|
||||||
|
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
|
||||||
|
readonly #logger = inject(NGXLogger);
|
||||||
|
protected readonly _defaultModelPath = 'report';
|
||||||
readonly licenseData$: Observable<ILicenses>;
|
readonly licenseData$: Observable<ILicenses>;
|
||||||
readonly selectedLicense$: Observable<ILicense>;
|
readonly selectedLicense$: Observable<ILicense>;
|
||||||
activeLicenseId: string;
|
activeLicenseId: string;
|
||||||
@ -48,9 +52,19 @@ export class LicenseService extends GenericService<ILicenseReport> {
|
|||||||
unlicensedPages = 0;
|
unlicensedPages = 0;
|
||||||
analyzedPagesInCurrentLicensingPeriod = 0;
|
analyzedPagesInCurrentLicensingPeriod = 0;
|
||||||
uploadedBytesCapacity = 0;
|
uploadedBytesCapacity = 0;
|
||||||
protected readonly _defaultModelPath = 'report';
|
|
||||||
readonly #licenseData$ = new BehaviorSubject<ILicenses | undefined>(undefined);
|
constructor() {
|
||||||
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
|
super();
|
||||||
|
this.selectedLicense$ = this.#selectedLicense$.pipe(filter(license => !!license));
|
||||||
|
this.licenseData$ = this.#licenseData$.pipe(
|
||||||
|
filter(licenses => !!licenses),
|
||||||
|
tap(data => (this.activeLicenseId = data.activeLicense)),
|
||||||
|
tap(() => {
|
||||||
|
const uploadedBytesCapacity = this.activeLicense.features.find(f => f.name === 'uploadedBytesCapacity')?.value;
|
||||||
|
this.uploadedBytesCapacity = uploadedBytesCapacity ? parseInt(uploadedBytesCapacity, 10) : 0;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
get selectedLicense() {
|
get selectedLicense() {
|
||||||
return this.#selectedLicense$.value;
|
return this.#selectedLicense$.value;
|
||||||
@ -66,20 +80,13 @@ export class LicenseService extends GenericService<ILicenseReport> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get activeLicenseKey(): string {
|
get activeLicenseKey(): string {
|
||||||
return this.activeLicense.features.find(f => f.name === 'pdftron').value;
|
const activeLicense = this.activeLicense;
|
||||||
}
|
if (!activeLicense) {
|
||||||
|
this.#logger.error('[LICENSE] No active license found!');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private readonly _logger: NGXLogger) {
|
return activeLicense.features.find(f => f.name === 'pdftron').value;
|
||||||
super();
|
|
||||||
this.selectedLicense$ = this.#selectedLicense$.pipe(filter(license => !!license));
|
|
||||||
this.licenseData$ = this.#licenseData$.pipe(
|
|
||||||
filter(licenses => !!licenses),
|
|
||||||
tap(data => (this.activeLicenseId = data.activeLicense)),
|
|
||||||
tap(() => {
|
|
||||||
const uploadedBytesCapacity = this.activeLicense.features.find(f => f.name === 'uploadedBytesCapacity')?.value;
|
|
||||||
this.uploadedBytesCapacity = uploadedBytesCapacity ? parseInt(uploadedBytesCapacity, 10) : 0;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadLicenseData(license: ILicense = this.selectedLicense) {
|
async loadLicenseData(license: ILicense = this.selectedLicense) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ADMIN_CONTACT_NAME": null,
|
"ADMIN_CONTACT_NAME": null,
|
||||||
"ADMIN_CONTACT_URL": null,
|
"ADMIN_CONTACT_URL": null,
|
||||||
"API_URL": "https://dev-02.iqser.cloud",
|
"API_URL": "https://dev-04.iqser.cloud",
|
||||||
"APP_NAME": "RedactManager",
|
"APP_NAME": "RedactManager",
|
||||||
"AUTO_READ_TIME": 3,
|
"AUTO_READ_TIME": 3,
|
||||||
"BACKEND_APP_VERSION": "4.4.40",
|
"BACKEND_APP_VERSION": "4.4.40",
|
||||||
@ -11,7 +11,7 @@
|
|||||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||||
"OAUTH_CLIENT_ID": "redaction",
|
"OAUTH_CLIENT_ID": "redaction",
|
||||||
"OAUTH_IDP_HINT": null,
|
"OAUTH_IDP_HINT": null,
|
||||||
"OAUTH_URL": "https://dev-02.iqser.cloud/auth",
|
"OAUTH_URL": "https://dev-04.iqser.cloud/auth",
|
||||||
"RECENT_PERIOD_IN_HOURS": 24,
|
"RECENT_PERIOD_IN_HOURS": 24,
|
||||||
"SELECTION_MODE": "structural",
|
"SELECTION_MODE": "structural",
|
||||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<redaction-root></redaction-root>
|
<redaction-root></redaction-root>
|
||||||
|
<div id="viewer"></div>
|
||||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -143,3 +143,11 @@ $dark-accent-10: darken(vars.$accent, 10%);
|
|||||||
$iqser-app-name-font-size: 13px,
|
$iqser-app-name-font-size: 13px,
|
||||||
$iqser-app-name-color: vars.$white
|
$iqser-app-name-color: vars.$white
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#viewer {
|
||||||
|
width: calc(100% - 350px);
|
||||||
|
height: calc(100% - calc(var(--iqser-top-bar-height) + 50px));
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit f631395f613e6cbeaf71826dac4dc8446be33520
|
Subproject commit da18c68a861a8d343dd3f63298e825543714f743
|
||||||
Loading…
x
Reference in New Issue
Block a user