Merge remote-tracking branch 'origin/master'

This commit is contained in:
Valentin Mihai 2023-07-07 15:31:32 +03:00
commit cea0b23b86
17 changed files with 161 additions and 130 deletions

View File

@ -5,8 +5,8 @@ import { BaseScreenComponent } from '@components/base-screen/base-screen.compone
import { RouteReuseStrategy, RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
import { DossiersGuard } from '@guards/dossiers.guard';
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
import { FeaturesGuard } from '@guards/features-guard.service';
import { DossierTemplatesGuard } from '@guards/dossier-templates.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 { 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 { 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 { mainResolver } from '@utils/main.resolver';
import { hasAnyRoleGuard, IqserAuthGuard } from '@iqser/common-ui/lib/users';
@ -26,10 +26,8 @@ import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
const dossierTemplateIdRoutes: IqserRoutes = [
{
path: `${DOSSIERS_ROUTE}`,
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [loadActiveDossiersGuard(), IqserPermissionsGuard],
data: {
routeGuards: [DossiersGuard],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
allow: [Roles.files.readStatus],
redirectTo: '/auth-error',
@ -53,9 +51,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [
},
{
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, webViewerLoadedGuard()],
data: {
routeGuards: [DossierFilesGuard, WebViewerLoadedGuard],
routeGuards: [DossierFilesGuard],
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
permissions: {
@ -78,10 +76,9 @@ const dossierTemplateIdRoutes: IqserRoutes = [
{
path: `${ARCHIVE_ROUTE}`,
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
canActivate: [CompositeRouteGuard, WebViewerLoadedGuard],
canActivate: [CompositeRouteGuard, webViewerLoadedGuard(), loadArchivedDossiersGuard()],
data: {
routeGuards: [FeaturesGuard, DossiersGuard],
dossiersService: ARCHIVED_DOSSIERS_SERVICE,
routeGuards: [FeaturesGuard],
features: [DOSSIERS_ARCHIVE],
},
},
@ -150,9 +147,9 @@ const mainRoutes: IqserRoutes = [
{
path: 'search',
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard],
routeGuards: [IqserAuthGuard, RedRoleGuard],
permissions: {
allow: [Roles.search],
redirectTo: '/auth-error',
@ -162,10 +159,9 @@ const mainRoutes: IqserRoutes = [
{
path: 'trash',
loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule),
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadActiveDossiersGuard()],
data: {
routeGuards: [IqserAuthGuard, RedRoleGuard, DossiersGuard, TrashGuard],
dossiersService: ACTIVE_DOSSIERS_SERVICE,
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
permissions: {
allow: [Roles.dossiers.read, Roles.files.readStatus],
redirectTo: '/auth-error',

View File

@ -1,45 +1,57 @@
import { Injectable, Injector, ProviderToken } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { firstValueFrom, forkJoin } from 'rxjs';
import { take } from 'rxjs/operators';
import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { ARCHIVE_ROUTE, DOSSIER_TEMPLATE_ID } from '@red/domain';
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
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 class DossiersGuard implements CanActivate {
constructor(
private readonly _injector: Injector,
private readonly _router: Router,
private readonly _tenantsService: TenantsService,
private readonly _dashboardStatsService: DashboardStatsService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
) {}
export function loadAllDossiersGuard(): CanActivateFn {
return async () => {
const logger = inject(NGXLogger);
logger.info('[GUARDS] loadAllDossiersGuard start');
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const token: ProviderToken<DossiersService> = route.data.dossiersService;
if (!token) {
const services = [this._archivedDossiersService, this._activeDossiersService];
const loading$ = forkJoin(services.map(service => service.loadAll().pipe(take(1))));
await firstValueFrom(loading$);
return true;
}
const services = [inject(ArchivedDossiersService), inject(ActiveDossiersService)];
const requests = services.map(service => firstValueFrom(service.loadAll()));
await Promise.all(requests);
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
const isArchive = dossiersService.routerPath === ARCHIVE_ROUTE;
logger.info('[GUARDS] loadAllDossiersGuard end');
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 dossierTemplateStats = this._dashboardStatsService.find(dossierTemplateId);
const dossierTemplateStats = inject(DashboardStatsService).find(dossierTemplateId);
if (isArchive && dossierTemplateStats?.numberOfArchivedDossiers === 0) {
await this._router.navigate([this._tenantsService.activeTenantId, 'main', dossierTemplateId, 'dossiers']);
if (dossierTemplateStats?.numberOfArchivedDossiers === 0) {
logger.info('[GUARDS] loadArchivedDossiersGuard no archived dossiers, redirect to active dossiers page');
await inject(Router).navigate([inject(TenantsService).activeTenantId, 'main', dossierTemplateId, 'dossiers']);
return false;
}
await firstValueFrom(dossiersService.loadAll());
logger.info('[GUARDS] loadArchivedDossiersGuard end');
return true;
}
};
}

View File

@ -9,7 +9,7 @@ import { LicenseService } from '@services/license.service';
export function ifLoggedIn(): CanActivateFn {
return async (route: ActivatedRouteSnapshot) => {
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 keycloakService = inject(KeycloakService);

View File

@ -1,5 +1,5 @@
<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>
@ -54,30 +54,30 @@
<div class="iqser-input-group">
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
<div class="flex">
<div class="alignment-buttons" [class.disabled]="form.controls.horizontalTextAlignment.disabled">
<div [class.disabled]="form.controls.horizontalTextAlignment.disabled" class="alignment-buttons">
<div
(click)="alignHorizontally(alignment)"
*ngFor="let alignment of watermarkHorizontalAlignments"
[class.active]="currentAlignment.horizontal === alignment"
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
[matTooltipPosition]="'above'"
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
[ngClass]="'horizontal-' + alignment.toLowerCase()"
[class.disabled]="form.controls.horizontalTextAlignment.disabled"
class="alignment"
(click)="alignHorizontally(alignment)"
>
<mat-icon [svgIcon]="'red:align-horizontal-' + alignment.toLowerCase()"></mat-icon>
</div>
</div>
<div class="alignment-buttons" [class.disabled]="form.controls.verticalTextAlignment.disabled">
<div [class.disabled]="form.controls.verticalTextAlignment.disabled" class="alignment-buttons">
<div
(click)="alignVertically(alignment)"
*ngFor="let alignment of watermarkVerticalAlignments"
[class.active]="currentAlignment.vertical === alignment"
[class.disabled]="form.controls.verticalTextAlignment.disabled"
[matTooltipPosition]="'above'"
[matTooltip]="translations.VERTICAL[alignment] | translate"
[ngClass]="'vertical-' + alignment.toLowerCase()"
[class.disabled]="form.controls.verticalTextAlignment.disabled"
class="alignment"
(click)="alignVertically(alignment)"
>
<mat-icon [svgIcon]="'red:align-vertical-' + alignment.toLowerCase()"></mat-icon>
</div>

View File

@ -11,7 +11,7 @@ import {
TemplateRef,
ViewChild,
} from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import {
CircleButtonTypes,
ConfirmOption,
@ -20,6 +20,7 @@ import {
ErrorService,
HelpModeService,
IConfirmationDialogData,
IqserDialog,
LoadingService,
Toaster,
} from '@iqser/common-ui';
@ -63,7 +64,6 @@ import { ConfigService } from '@services/config.service';
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
import { Roles } from '@users/roles';
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 { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
@ -108,7 +108,6 @@ export class FilePreviewScreenComponent
private readonly _annotationManager: REDAnnotationManager,
private readonly _errorService: ErrorService,
private readonly _filterService: FilterService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _loadingService: LoadingService,
private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService,
@ -161,6 +160,7 @@ export class FilePreviewScreenComponent
effect(() => {
const selectedText = this._documentViewer.selectedText();
console.log('selectedText', selectedText);
const canPerformActions = this.pdfProxyService.canPerformActions();
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());

View File

@ -1,5 +1,3 @@
<div id="viewer"></div>
<redaction-compare-file-input></redaction-compare-file-input>
<redaction-paginator></redaction-paginator>

View File

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

View File

@ -11,7 +11,6 @@ import { PaginatorComponent } from './components/paginator/paginator.component';
import { MatIconModule } from '@angular/material/icon';
import { AnnotationDrawService } from './services/annotation-draw.service';
import { REDDocumentViewer } from './services/document-viewer.service';
import { WebViewerLoadedGuard } from './services/webviewer-loaded.guard';
import { ReadableRedactionsService } from './services/readable-redactions.service';
@NgModule({
@ -27,7 +26,6 @@ import { ReadableRedactionsService } from './services/readable-redactions.servic
ReadableRedactionsService,
ViewerHeaderService,
AnnotationDrawService,
WebViewerLoadedGuard,
],
})
export class PdfViewerModule {}

View File

@ -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 { NGXLogger } from 'ngx-logger';
import { fromEvent, Observable } from 'rxjs';
@ -25,7 +25,6 @@ export class REDDocumentViewer {
readonly #userPreferenceService = inject(UserPreferenceService);
readonly #pdf = inject(PdfViewer);
readonly #activatedRoute = inject(ActivatedRoute);
readonly #ngZone = inject(NgZone);
readonly loaded$: Observable<boolean>;
keyUp$: Observable<KeyboardEvent>;
readonly selectedText: Signal<string>;
@ -37,6 +36,21 @@ export class REDDocumentViewer {
this.loaded$ = toObservable(this.#loaded);
this.pageComplete = this.#pageComplete.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() {

View File

@ -1,41 +1,42 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { inject } from '@angular/core';
import { CanActivateFn, ResolveFn, Router } from '@angular/router';
import { REDDocumentViewer } from './document-viewer.service';
import { PdfViewer } from './pdf-viewer.service';
import { REDAnnotationManager } from './annotation-manager.service';
import { ViewerHeaderService } from './viewer-header.service';
import { LoadingService } from '@iqser/common-ui';
import { WebViewerInstance } from '@pdftron/webviewer';
import { NGXLogger } from 'ngx-logger';
import { DOCUMENT } from '@angular/common';
@Injectable()
export class WebViewerLoadedGuard implements CanActivate {
constructor(
private readonly _documentViewer: REDDocumentViewer,
private readonly _pdf: PdfViewer,
private readonly _router: Router,
private readonly _annotationManager: REDAnnotationManager,
private readonly _viewerHeaderService: ViewerHeaderService,
private readonly _loadingService: LoadingService,
) {}
export function webViewerLoadedGuard(): CanActivateFn | ResolveFn<boolean> {
return async (_, state) => {
const pdf = inject(PdfViewer);
const logger = inject(NGXLogger);
async canActivate(_, state) {
if (this._pdf.instance) {
if (pdf.instance) {
logger.info('[PDF] WebViewerGuard already loaded.');
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;
try {
instance = await this._pdf.init(document.getElementById('viewer'));
} catch {
return this._router.navigateByUrl(state.url);
instance = await pdf.init(inject(DOCUMENT).getElementById('viewer'));
} catch (e) {
logger.warn('[PDF] WebViewerGuard error: ', e, 'redirecting to', state.url);
return router.navigateByUrl(state.url);
}
this._annotationManager.init(instance.Core.annotationManager);
this._documentViewer.init(instance.Core.documentViewer);
this._viewerHeaderService.init();
annotationManager.init(instance.Core.annotationManager);
documentViewer.init(instance.Core.documentViewer);
viewerHeaderService.init();
return !!this._pdf.instance;
}
return !!pdf.instance;
};
}

View File

@ -144,15 +144,11 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
this._dialogService.openDialog('confirm', data, async () => {
this._loadingService.start();
await firstValueFrom(this._trashService.deleteDossier(this.dossier));
this._editDossierDialogRef.close();
await this._router.navigate([`/${this._tenantsService.activeTenantId}${this.dossier.dossiersListRouterLink}`]);
const successful = await this._trashService.deleteDossier(this.dossier);
if (successful) {
await this.#closeDialogAndRedirectToDossier();
}
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;
}
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 {
const formFieldWithArchivedCheck = value => ({ value, disabled: !this.dossier.isActive });
return this._formBuilder.group({

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { EntitiesService, QueryParam, Toaster } from '@iqser/common-ui';
import { Dossier, File, IDossier, IFile, TrashDossier, TrashFile, TrashItem } from '@red/domain';
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 { PermissionsService } from '../permissions.service';
import { ActiveDossiersService } from '../dossiers/active-dossiers.service';
@ -31,18 +31,20 @@ export class TrashService extends EntitiesService<TrashItem, TrashItem> {
super();
}
deleteDossier(dossier: Dossier): Observable<unknown> {
deleteDossier(dossier: Dossier) {
const showToast = () => {
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();
return this.delete(dossier.id, 'dossier').pipe(
const request$ = this.delete(dossier.id, 'dossier').pipe(
switchMap(() => reloadDossiers$),
catchError(showToast),
);
return firstValueFrom(request$);
}
restore(items: TrashItem[]): Observable<unknown> {

View File

@ -1,4 +1,4 @@
import { Injectable } from '@angular/core';
import { inject, Injectable } from '@angular/core';
import { GenericService } from '@iqser/common-ui';
import { ILicense, ILicenseReport, ILicenseReportRequest, ILicenses } from '@red/domain';
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
@ -39,6 +39,10 @@ const defaultOnError: ILicenses = {
providedIn: 'root',
})
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 selectedLicense$: Observable<ILicense>;
activeLicenseId: string;
@ -48,9 +52,19 @@ export class LicenseService extends GenericService<ILicenseReport> {
unlicensedPages = 0;
analyzedPagesInCurrentLicensingPeriod = 0;
uploadedBytesCapacity = 0;
protected readonly _defaultModelPath = 'report';
readonly #licenseData$ = new BehaviorSubject<ILicenses | undefined>(undefined);
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
constructor() {
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() {
return this.#selectedLicense$.value;
@ -66,20 +80,13 @@ export class LicenseService extends GenericService<ILicenseReport> {
}
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) {
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;
}),
);
return activeLicense.features.find(f => f.name === 'pdftron').value;
}
async loadLicenseData(license: ILicense = this.selectedLicense) {

View File

@ -1,7 +1,7 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dev-02.iqser.cloud",
"API_URL": "https://dev-04.iqser.cloud",
"APP_NAME": "RedactManager",
"AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40",
@ -11,7 +11,7 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"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,
"SELECTION_MODE": "structural",
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",

View File

@ -10,6 +10,7 @@
</head>
<body>
<redaction-root></redaction-root>
<div id="viewer"></div>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>

View File

@ -143,3 +143,11 @@ $dark-accent-10: darken(vars.$accent, 10%);
$iqser-app-name-font-size: 13px,
$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