Merge branch 'master' into VM/RED-6801
This commit is contained in:
commit
cf655c0c5c
@ -1,22 +1,12 @@
|
||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||
import {
|
||||
CompositeRouteGuard,
|
||||
CustomRouteReuseStrategy,
|
||||
DEFAULT_REDIRECT_KEY,
|
||||
ifLoggedIn,
|
||||
ifNotLoggedIn,
|
||||
IqserAuthGuard,
|
||||
IqserPermissionsGuard,
|
||||
IqserRoutes,
|
||||
TenantSelectComponent,
|
||||
} from '@iqser/common-ui';
|
||||
import { CompositeRouteGuard, DEFAULT_REDIRECT_KEY, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||
import { RedRoleGuard } from '@users/red-role.guard';
|
||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||
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';
|
||||
@ -24,17 +14,20 @@ 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';
|
||||
import { CustomRouteReuseStrategy } from '@iqser/common-ui/lib/utils';
|
||||
import { ifLoggedIn } from '@guards/if-logged-in.guard';
|
||||
import { ifNotLoggedIn } from '@guards/if-not-logged-in.guard';
|
||||
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',
|
||||
@ -58,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: {
|
||||
@ -83,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],
|
||||
},
|
||||
},
|
||||
@ -155,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',
|
||||
@ -167,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',
|
||||
@ -182,7 +173,7 @@ const mainRoutes: IqserRoutes = [
|
||||
children: dossierTemplateIdRoutes,
|
||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, templateExistsWhenEnteringDossierList()],
|
||||
data: {
|
||||
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard, DashboardGuard],
|
||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||
permissions: {
|
||||
allow: [
|
||||
Roles.any,
|
||||
@ -210,7 +201,7 @@ const routes: IqserRoutes = [
|
||||
{
|
||||
path: '',
|
||||
pathMatch: 'full',
|
||||
canActivate: [ifNotLoggedIn],
|
||||
canActivate: [ifNotLoggedIn()],
|
||||
component: TenantSelectComponent,
|
||||
},
|
||||
{
|
||||
@ -220,7 +211,7 @@ const routes: IqserRoutes = [
|
||||
},
|
||||
{
|
||||
path: ':tenant/main',
|
||||
canActivate: [ifLoggedIn],
|
||||
canActivate: [ifLoggedIn()],
|
||||
component: BaseScreenComponent,
|
||||
resolve: {
|
||||
whateverThisMainRouteNeeds: mainResolver,
|
||||
@ -230,7 +221,7 @@ const routes: IqserRoutes = [
|
||||
{
|
||||
path: ':tenant/auth-error',
|
||||
component: AuthErrorComponent,
|
||||
canActivate: [IqserAuthGuard],
|
||||
canActivate: [hasAnyRoleGuard()],
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
CachingModule,
|
||||
ChevronButtonComponent,
|
||||
CircleButtonComponent,
|
||||
CommonUiModule,
|
||||
EmptyStateComponent,
|
||||
HelpModeKey,
|
||||
HiddenActionDirective,
|
||||
@ -21,18 +20,12 @@ import {
|
||||
IqserListingModule,
|
||||
IqserLoadingModule,
|
||||
IqserTranslateModule,
|
||||
IqserUsersModule,
|
||||
LanguageService,
|
||||
LogoComponent,
|
||||
MAX_RETRIES_ON_SERVER_ERROR,
|
||||
RoundCheckboxComponent,
|
||||
SERVER_ERROR_SKIP_PATHS,
|
||||
ServerErrorInterceptor,
|
||||
SkeletonComponent,
|
||||
StopPropagationDirective,
|
||||
TenantPipe,
|
||||
TenantsModule,
|
||||
ToastComponent,
|
||||
} from '@iqser/common-ui';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
@ -71,6 +64,10 @@ import { SkeletonStatsComponent } from '@components/skeleton/skeleton-stats/skel
|
||||
import { UserMenuComponent } from '@components/user-menu/user-menu.component';
|
||||
import { TenantsMenuComponent } from '@components/tenants-menu/tenants-menu.component';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { CommonUiModule } from '@iqser/common-ui/lib/common-ui.module';
|
||||
import { LogoComponent, SkeletonComponent, ToastComponent } from '@iqser/common-ui/lib/shared';
|
||||
import { TenantPipe, TenantsModule } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
export const appModuleFactory = (config: AppConfig) => {
|
||||
@NgModule({
|
||||
@ -144,10 +141,10 @@ export const appModuleFactory = (config: AppConfig) => {
|
||||
enabled: true,
|
||||
},
|
||||
FILE: {
|
||||
enabled: false,
|
||||
enabled: true,
|
||||
},
|
||||
CHANGES: {
|
||||
enabled: false,
|
||||
enabled: true,
|
||||
},
|
||||
STATS: {
|
||||
enabled: false,
|
||||
|
||||
@ -6,15 +6,17 @@ import { Title } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
|
||||
import { filter, map, startWith } from 'rxjs/operators';
|
||||
import { IqserPermissionsService, List, shareDistinctLast, TenantsService } from '@iqser/common-ui';
|
||||
import { IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||
import { FeaturesService } from '@services/features.service';
|
||||
import { ARCHIVE_ROUTE, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE } from '@red/domain';
|
||||
import { Roles } from '@users/roles';
|
||||
import { REDDocumentViewer } from '../../modules/pdf-viewer/services/document-viewer.service';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { List, shareDistinctLast } from '@iqser/common-ui/lib/utils';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
const isNavigationStart = event => event instanceof NavigationStart;
|
||||
const isNavigationStart = (event: unknown): event is NavigationStart => event instanceof NavigationStart;
|
||||
const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
|
||||
|
||||
@Component({
|
||||
@ -22,6 +24,12 @@ const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
|
||||
styleUrls: ['./base-screen.component.scss'],
|
||||
})
|
||||
export class BaseScreenComponent {
|
||||
readonly #navigationStart$ = this._router.events.pipe(
|
||||
filter(isNavigationStart),
|
||||
map(event => event.url),
|
||||
startWith(this._router.url),
|
||||
shareDistinctLast(),
|
||||
);
|
||||
readonly roles = Roles;
|
||||
readonly documentViewer = inject(REDDocumentViewer);
|
||||
readonly currentUser = this.userService.currentUser;
|
||||
@ -44,12 +52,6 @@ export class BaseScreenComponent {
|
||||
action: (query): void => this.#search(query, []),
|
||||
},
|
||||
];
|
||||
readonly #navigationStart$ = this._router.events.pipe(
|
||||
filter(isNavigationStart),
|
||||
map((event: NavigationStart) => event.url),
|
||||
startWith(this._router.url),
|
||||
shareDistinctLast(),
|
||||
);
|
||||
readonly isSearchScreen$ = this.#navigationStart$.pipe(map(isSearchScreen));
|
||||
|
||||
constructor(
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<iqser-circle-button
|
||||
[matMenuTriggerFor]="menu"
|
||||
[showDot]="hasUnreadNotifications$ | async"
|
||||
[tooltip]="'notifications.button-text' | translate"
|
||||
[tooltipPosition]="'below'"
|
||||
[tooltip]="'notifications.button-text' | translate"
|
||||
buttonId="notification-button"
|
||||
icon="red:notification"
|
||||
></iqser-circle-button>
|
||||
@ -25,7 +25,7 @@
|
||||
*ngIf="(hasUnreadNotifications$ | async) && first"
|
||||
class="view-all"
|
||||
id="notifications-mark-all-as-read-btn"
|
||||
stopPropagation
|
||||
iqserStopPropagation
|
||||
>
|
||||
{{ 'notifications.mark-all-as-read' | translate }}
|
||||
</div>
|
||||
@ -37,8 +37,8 @@
|
||||
[class.unread]="!notification.readDate"
|
||||
[id]="'notifications-mark-as-read-' + notification.id + '-btn'"
|
||||
class="notification"
|
||||
iqserStopPropagation
|
||||
mat-menu-item
|
||||
stopPropagation
|
||||
>
|
||||
<iqser-initials-avatar [user]="notification.userId"></iqser-initials-avatar>
|
||||
|
||||
@ -51,9 +51,9 @@
|
||||
(click)="markRead([notification], !notification.readDate)"
|
||||
[id]="'notifications-mark-' + notification.id"
|
||||
class="dot"
|
||||
iqserStopPropagation
|
||||
matTooltip="{{ 'notifications.mark-as' | translate : { type: notification.readDate ? 'unread' : 'read' } }}"
|
||||
matTooltipPosition="before"
|
||||
stopPropagation
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,7 +4,7 @@ import { NotificationsService } from '@services/notifications.service';
|
||||
import { Notification } from '@red/domain';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { isToday, shareLast, trackByFactory } from '@iqser/common-ui';
|
||||
import { isToday, shareLast, trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<div id="tenants-menu-items">
|
||||
<ng-container *ngFor="let item of storedTenants | keyvalue; trackBy: trackBy">
|
||||
<ng-container *ngFor="let item of storedTenants; trackBy: trackBy">
|
||||
<div class="label">{{ item.key }}</div>
|
||||
|
||||
<a
|
||||
(click)="selectTenant(stored.tenant.tenantId, stored.email)"
|
||||
(click)="selectTenant(stored.tenantId, stored.email)"
|
||||
*ngFor="let stored of item.value"
|
||||
[id]="stored.tenant.tenantId"
|
||||
[id]="stored.tenantId + stored.email"
|
||||
mat-menu-item
|
||||
>
|
||||
{{ stored.email }}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { BASE_HREF, getConfig, getCurrentUser, IStoredTenant, KeycloakStatusService, TenantsService } from '@iqser/common-ui';
|
||||
import { getConfig } from '@iqser/common-ui';
|
||||
import { User } from '@red/domain';
|
||||
import { KeyValue } from '@angular/common';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { BASE_HREF } from '@iqser/common-ui/lib/utils';
|
||||
import { getKeycloakOptions, IStoredTenantId, KeycloakStatusService, TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tenants-menu',
|
||||
@ -10,48 +12,49 @@ import { KeycloakService } from 'keycloak-angular';
|
||||
styleUrls: ['./tenants-menu.component.scss'],
|
||||
})
|
||||
export class TenantsMenuComponent {
|
||||
readonly tenantsService = inject(TenantsService);
|
||||
readonly storedTenants: Map<string, IStoredTenant[]>;
|
||||
readonly #baseHref = inject(BASE_HREF);
|
||||
readonly #keycloakService = inject(KeycloakService);
|
||||
readonly #keycloakStatusService = inject(KeycloakStatusService);
|
||||
readonly #currentUser = getCurrentUser<User>();
|
||||
readonly #config = getConfig();
|
||||
readonly tenantsService = inject(TenantsService);
|
||||
readonly storedTenants: { key: string; value: IStoredTenantId[] }[];
|
||||
|
||||
constructor() {
|
||||
this.storedTenants = this.#getStoredTenants();
|
||||
}
|
||||
|
||||
trackBy(_index: number, item: KeyValue<string, IStoredTenant[]>) {
|
||||
trackBy(_index: number, item: { key: string; value: IStoredTenantId[] }) {
|
||||
return item.key;
|
||||
}
|
||||
|
||||
// TODO: this should be moved to the keycloak status service
|
||||
async selectTenant(tenantId?: string, email?: string) {
|
||||
let tenantUrl = tenantId ? '/' + tenantId : '/';
|
||||
if (email) {
|
||||
tenantUrl += '?username=' + email;
|
||||
if (tenantId && tenantId !== this.tenantsService.activeTenantId) {
|
||||
await this.#keycloakService.init(getKeycloakOptions(this.#baseHref, this.#config, tenantId));
|
||||
}
|
||||
|
||||
if (tenantId === this.tenantsService.activeTenantId) {
|
||||
if (tenantId) {
|
||||
const url = this.#keycloakService.getKeycloakInstance().createLoginUrl({
|
||||
redirectUri: this.#keycloakStatusService.createLoginUrl(),
|
||||
redirectUri: this.#keycloakStatusService.createLoginUrl(tenantId),
|
||||
idpHint: this.#config.OAUTH_IDP_HINT,
|
||||
loginHint: email ?? undefined,
|
||||
});
|
||||
return this.#keycloakService.logout(url);
|
||||
}
|
||||
|
||||
window.open(window.location.origin + this.#baseHref + tenantUrl, '_blank');
|
||||
return this.#keycloakService.logout(window.location.origin + this.#baseHref);
|
||||
}
|
||||
|
||||
#getStoredTenants(): Map<string, IStoredTenant[]> {
|
||||
#getStoredTenants() {
|
||||
const storedTenants = this.tenantsService.getStoredTenants();
|
||||
|
||||
const otherTenant = storedTenants.filter(t => {
|
||||
const isCurrentTenant = t.tenant.tenantId === this.tenantsService.activeTenantId;
|
||||
const isCurrentTenant = t.tenantId === this.tenantsService.activeTenantId;
|
||||
const isCurrentUser = t.email === this.#currentUser.email || t.email === this.#currentUser.username;
|
||||
return !(isCurrentTenant && isCurrentUser);
|
||||
});
|
||||
return otherTenant.groupBy(t => t.tenant.displayName);
|
||||
const grouped = otherTenant.groupBy(t => t.tenantId);
|
||||
return [...grouped.keys()].sort((a, b) => a.localeCompare(b)).map(key => ({ key, value: grouped.get(key) }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
</a>
|
||||
</ng-container>
|
||||
|
||||
<a *ngIf="tenantsService.hasMultiple()" [id]="'switch-accounts'" [matMenuTriggerFor]="tenantsMenu" mat-menu-item>
|
||||
<a *ngIf="tenantsService.getStoredTenants().length > 1" [id]="'switch-accounts'" [matMenuTriggerFor]="tenantsMenu" mat-menu-item>
|
||||
{{ 'top-bar.navigation-items.my-account.children.select-tenant' | translate }}
|
||||
</a>
|
||||
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser, IqserPermissionsService, List, TenantsService } from '@iqser/common-ui';
|
||||
import { IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { User } from '@red/domain';
|
||||
import { UserService } from '@users/user.service';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { List } from '@iqser/common-ui/lib/utils';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
interface MenuItem {
|
||||
readonly id: string;
|
||||
@ -20,10 +23,10 @@ interface MenuItem {
|
||||
styleUrls: ['./user-menu.component.scss'],
|
||||
})
|
||||
export class UserMenuComponent {
|
||||
readonly #permissionsService = inject(IqserPermissionsService);
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly tenantsService = inject(TenantsService);
|
||||
readonly userService = inject(UserService);
|
||||
readonly #permissionsService = inject(IqserPermissionsService);
|
||||
readonly userMenuItems: List<MenuItem> = [
|
||||
{
|
||||
id: 'account',
|
||||
|
||||
@ -7,7 +7,7 @@ import { DOSSIER_ID, DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
|
||||
import { TenantsService } from '@iqser/common-ui';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DossierFilesGuard implements CanActivate {
|
||||
|
||||
@ -2,7 +2,10 @@ import { inject } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||
import { TenantsService } from '@iqser/common-ui';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
|
||||
export function templateExistsWhenEnteringAdmin(): CanActivateFn {
|
||||
return async function (route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||
@ -20,10 +23,18 @@ export function templateExistsWhenEnteringAdmin(): CanActivateFn {
|
||||
export function templateExistsWhenEnteringDossierList(): CanActivateFn {
|
||||
return async function (route: ActivatedRouteSnapshot) {
|
||||
const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||
const dashboardStatsService = inject(DashboardStatsService);
|
||||
const dossierTemplatesService = inject(DossierTemplatesService);
|
||||
const logger = inject(NGXLogger);
|
||||
const router = inject(Router);
|
||||
const tenantsService = inject(TenantsService);
|
||||
|
||||
const dossierTemplateStats = inject(DashboardStatsService).find(dossierTemplateId);
|
||||
await firstValueFrom(dashboardStatsService.loadAll());
|
||||
await firstValueFrom(dossierTemplatesService.loadAll());
|
||||
const dossierTemplateStats = dashboardStatsService.find(dossierTemplateId);
|
||||
if (!dossierTemplateStats || dossierTemplateStats.isEmpty) {
|
||||
await inject(Router).navigate([inject(TenantsService).activeTenantId, 'main']);
|
||||
logger.warn('[ROUTES] Dossier template not found, redirecting to main');
|
||||
await router.navigate([tenantsService.activeTenantId, 'main']);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -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';
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { TenantsService } from '@iqser/common-ui';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
export function entityExistsGuard(): CanActivateFn {
|
||||
return async function (route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||
|
||||
50
apps/red-ui/src/app/guards/if-logged-in.guard.ts
Normal file
50
apps/red-ui/src/app/guards/if-logged-in.guard.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||
import { inject } from '@angular/core';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { keycloakInitializer, KeycloakStatusService, TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { UserService } from '@users/user.service';
|
||||
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 route', route);
|
||||
|
||||
const tenantsService = inject(TenantsService);
|
||||
const keycloakService = inject(KeycloakService);
|
||||
const usersService = inject(UserService);
|
||||
const licenseService = inject(LicenseService);
|
||||
const keycloakStatusService = inject(KeycloakStatusService);
|
||||
|
||||
const keycloakInstance = keycloakService.getKeycloakInstance();
|
||||
const tenant = route.paramMap.get('tenant');
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const username = queryParams.get('username');
|
||||
|
||||
if (!keycloakInstance) {
|
||||
if (!tenant) {
|
||||
logger.error('[ROUTES] No tenant found, something is wrong...');
|
||||
return inject(Router).navigate(['/']);
|
||||
}
|
||||
|
||||
logger.info('[KEYCLOAK] Keycloak init...');
|
||||
await keycloakInitializer(tenant);
|
||||
logger.info('[KEYCLOAK] Keycloak init done!');
|
||||
await tenantsService.selectTenant(tenant);
|
||||
await usersService.initialize();
|
||||
await licenseService.loadLicenses();
|
||||
}
|
||||
|
||||
const isLoggedIn = await keycloakService.isLoggedIn();
|
||||
|
||||
if (isLoggedIn) {
|
||||
logger.info('[ROUTES] Is logged in, continuing');
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.warn('[ROUTES] Redirect to login');
|
||||
keycloakStatusService.createLoginUrlAndExecute(username);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
29
apps/red-ui/src/app/guards/if-not-logged-in.guard.ts
Normal file
29
apps/red-ui/src/app/guards/if-not-logged-in.guard.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||
import { inject } from '@angular/core';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
|
||||
export function ifNotLoggedIn(): CanActivateFn {
|
||||
return async (route: ActivatedRouteSnapshot) => {
|
||||
const logger = inject(NGXLogger);
|
||||
const router = inject(Router);
|
||||
const keycloakService = inject(KeycloakService);
|
||||
|
||||
const isLoggedIn = await keycloakService.isLoggedIn();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
logger.info('[ROUTES] Not logged in, continuing to selected route');
|
||||
return true;
|
||||
}
|
||||
|
||||
const tenant = route.paramMap.get('tenant') || keycloakService.getKeycloakInstance().realm;
|
||||
if (!tenant) {
|
||||
logger.error('[ROUTES] Tenant not found in route or keycloak realm');
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.warn('[ROUTES] Is logged in for ' + tenant + ', redirecting to /' + tenant);
|
||||
await router.navigate([tenant]);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@ -2,7 +2,7 @@ import { inject } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
|
||||
import { DOSSIER_TEMPLATE_ID, WATERMARK_ID } from '@red/domain';
|
||||
import { TenantsService } from '@iqser/common-ui';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
export function watermarkExistsGuard(): CanActivateFn {
|
||||
return async function (route: ActivatedRouteSnapshot) {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { CompositeRouteGuard, IqserAuthGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||
import { RedRoleGuard } from '@users/red-role.guard';
|
||||
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
||||
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
||||
import { Roles } from '@users/roles';
|
||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{ path: '', redirectTo: 'user-profile', pathMatch: 'full' },
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { getCurrentUser, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { Roles } from '@users/roles';
|
||||
import { User } from '@red/domain';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
interface NavItem {
|
||||
readonly label: string;
|
||||
|
||||
@ -6,8 +6,9 @@ import { AccountSideNavComponent } from './account-side-nav/account-side-nav.com
|
||||
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
||||
import { NotificationPreferencesService } from './services/notification-preferences.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IconButtonComponent, IqserHelpModeModule, SideNavComponent } from '@iqser/common-ui';
|
||||
import { IconButtonComponent, IqserHelpModeModule } from '@iqser/common-ui';
|
||||
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
||||
import { SideNavComponent } from '@iqser/common-ui/lib/shared';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AccountSideNavComponent, BaseAccountScreenComponent, PreferencesComponent],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
||||
import { BaseFormComponent, getCurrentUser, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import {
|
||||
NotificationCategoriesValues,
|
||||
@ -12,6 +12,7 @@ import {
|
||||
} from '@red/domain';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
templateUrl: './notifications-screen.component.html',
|
||||
@ -24,6 +25,7 @@ export class NotificationsScreenComponent extends BaseFormComponent implements O
|
||||
readonly notificationGroupsValues = NotificationGroupsValues;
|
||||
readonly translations = notificationsSettingsTranslations;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
|
||||
constructor(
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _formBuilder: UntypedFormBuilder,
|
||||
|
||||
@ -2,8 +2,9 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/
|
||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AsControl, BaseFormComponent, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { Roles } from '@users/roles';
|
||||
import { AsControl } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface PreferencesForm {
|
||||
// preferences
|
||||
|
||||
@ -2,15 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit }
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
BaseFormComponent,
|
||||
getConfig,
|
||||
IqserPermissionsService,
|
||||
LanguageService,
|
||||
LoadingService,
|
||||
TenantsService,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { BaseFormComponent, getConfig, IqserPermissionsService, LanguageService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { AppConfig, IProfile } from '@red/domain';
|
||||
import { languagesTranslations } from '@translations/languages-translations';
|
||||
import { UserService } from '@users/user.service';
|
||||
@ -19,6 +11,7 @@ import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Component({
|
||||
templateUrl: './user-profile-screen.component.html',
|
||||
@ -26,12 +19,11 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
|
||||
#profileModel: IProfile;
|
||||
readonly translations = languagesTranslations;
|
||||
readonly devMode = this._userPreferenceService.areDevFeaturesEnabled;
|
||||
readonly changePasswordUrl: string;
|
||||
|
||||
#profileModel: IProfile;
|
||||
|
||||
constructor(
|
||||
domSanitizer: DomSanitizer,
|
||||
private readonly _userService: UserService,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CompositeRouteGuard, IqserAuthGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||
import { RedRoleGuard } from '@users/red-role.guard';
|
||||
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||
@ -18,6 +18,7 @@ import { entityExistsGuard } from '@guards/entity-exists-guard.service';
|
||||
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
||||
import { PermissionsGuard } from '@guards/permissions-guard';
|
||||
import { Roles } from '@users/roles';
|
||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||
|
||||
const dossierTemplateIdRoutes: IqserRoutes = [
|
||||
{
|
||||
|
||||
@ -49,13 +49,13 @@ import {
|
||||
IqserHelpModeModule,
|
||||
IqserListingModule,
|
||||
IqserUploadFileModule,
|
||||
IqserUsersModule,
|
||||
RoundCheckboxComponent,
|
||||
TenantPipe,
|
||||
} from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AuditInfoDialogComponent } from './dialogs/audit-info-dialog/audit-info-dialog.component';
|
||||
import { DossierTemplateActionsComponent } from './shared/components/dossier-template-actions/dossier-template-actions.component';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
const dialogs = [
|
||||
AddEditCloneDossierTemplateDialogComponent,
|
||||
|
||||
@ -4,21 +4,23 @@ import { Router } from '@angular/router';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
import { AdminDialogService } from '../services/admin-dialog.service';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
import { getParam, LoadingService, TenantsService } from '@iqser/common-ui';
|
||||
import { LoadingService } from '@iqser/common-ui';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Component({
|
||||
templateUrl: './base-entity-screen.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BaseEntityScreenComponent {
|
||||
readonly disabledItems$: Observable<string[]>;
|
||||
readonly canDeleteEntity$: Observable<boolean>;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly #entityType = getParam(ENTITY_TYPE);
|
||||
readonly disabledItems$: Observable<string[]>;
|
||||
readonly canDeleteEntity$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
private readonly _router: Router,
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { BaseFormComponent, IProfileUpdateRequest, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { rolesTranslations } from '@translations/roles-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { User } from '@red/domain';
|
||||
import { UserService } from '@users/user.service';
|
||||
import { HttpStatusCode } from '@angular/common/http';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-details',
|
||||
@ -15,17 +16,15 @@ import { firstValueFrom } from 'rxjs';
|
||||
styleUrls: ['./user-details.component.scss'],
|
||||
})
|
||||
export class UserDetailsComponent extends BaseFormComponent implements OnChanges {
|
||||
/** e.g. a RED_ADMIN is automatically a RED_USER_ADMIN => can't disable RED_USER_ADMIN as long as RED_ADMIN is checked */
|
||||
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||
@Input() user: User;
|
||||
@Output() readonly toggleResetPassword = new EventEmitter();
|
||||
@Output() readonly closeDialog = new EventEmitter();
|
||||
@Output() readonly cancel = new EventEmitter();
|
||||
|
||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||
readonly translations = rolesTranslations;
|
||||
|
||||
/** e.g. a RED_ADMIN is automatically a RED_USER_ADMIN => can't disable RED_USER_ADMIN as long as RED_ADMIN is checked */
|
||||
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: UntypedFormBuilder,
|
||||
private readonly _toaster: Toaster,
|
||||
|
||||
@ -3,7 +3,6 @@ import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getCurrentUser,
|
||||
IqserPermissionsService,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
@ -19,6 +18,7 @@ import { Dayjs } from 'dayjs';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
@ -28,6 +28,8 @@ const PAGE_SIZE = 50;
|
||||
providers: listingProvidersFactory(AuditScreenComponent),
|
||||
})
|
||||
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
|
||||
private _previousFrom: Dayjs;
|
||||
private _previousTo: Dayjs;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly ALL_CATEGORIES = 'allCategories';
|
||||
readonly ALL_USERS = _('audit-screen.all-users');
|
||||
@ -37,11 +39,9 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
||||
readonly permissionsService = inject(IqserPermissionsService);
|
||||
readonly roles = Roles;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
|
||||
categories: string[] = [];
|
||||
userIds: Set<string>;
|
||||
logs: IAuditResponse;
|
||||
|
||||
readonly tableColumnConfigs: TableColumnConfig<Audit>[] = [
|
||||
{ label: _('audit-screen.table-col-names.message') },
|
||||
{ label: _('audit-screen.table-col-names.date') },
|
||||
@ -49,8 +49,6 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
||||
{ label: _('audit-screen.table-col-names.category') },
|
||||
];
|
||||
readonly tableHeaderLabel = _('audit-screen.table-header.title');
|
||||
private _previousFrom: Dayjs;
|
||||
private _previousTo: Dayjs;
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: UntypedFormBuilder,
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { DefaultColorTypes, DOSSIER_TEMPLATE_ID, User } from '@red/domain';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
IListable,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
TableColumnConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { CircleButtonTypes, IListable, ListingComponent, listingProvidersFactory, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { defaultColorsTranslations } from '@translations/default-colors-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface ListItem extends IListable {
|
||||
readonly key: string;
|
||||
@ -29,6 +23,7 @@ interface ListItem extends IListable {
|
||||
providers: listingProvidersFactory(DefaultColorsScreenComponent),
|
||||
})
|
||||
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly roles = Roles;
|
||||
@ -39,7 +34,6 @@ export class DefaultColorsScreenComponent extends ListingComponent<ListItem> {
|
||||
{ label: _('default-colors-screen.table-col-names.color'), class: 'flex-center' },
|
||||
];
|
||||
readonly context$;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(private readonly _dialogService: AdminDialogService, private readonly _defaultColorsService: DefaultColorsService) {
|
||||
super();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { getCurrentUser, IconButtonTypes, IqserPermissionsService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { IconButtonTypes, IqserPermissionsService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { DigitalSignatureService } from '../../services/digital-signature.service';
|
||||
@ -9,6 +9,7 @@ import { PkcsSignatureConfigurationComponent } from '../../dialogs/configure-dig
|
||||
import { KmsSignatureConfigurationComponent } from '../../dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component';
|
||||
import { DigitalSignatureOptions, IKmsDigitalSignatureRequest, IPkcsDigitalSignatureRequest, User } from '@red/domain';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-digital-signature-screen',
|
||||
|
||||
@ -2,8 +2,6 @@ import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
defaultDialogConfig,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
@ -21,6 +19,8 @@ import {
|
||||
AddEditDossierAttributeDialogComponent,
|
||||
AddEditDossierAttributeDialogData,
|
||||
} from './add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-attributes-listing-screen.component.html',
|
||||
@ -31,6 +31,7 @@ import {
|
||||
}),
|
||||
})
|
||||
export class DossierAttributesListingScreenComponent extends ListingComponent<DossierAttributeConfig> implements OnInit {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
@ -42,7 +43,6 @@ export class DossierAttributesListingScreenComponent extends ListingComponent<Do
|
||||
];
|
||||
@ViewChild('impactedTemplates') impactedTemplatesRef: TemplateRef<unknown>;
|
||||
readonly canEditDossierAttributes = this.permissionsService.canEditGlobalDossierAttributes();
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
|
||||
@ -1,13 +1,5 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import {
|
||||
defaultDialogConfig,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
SortingOrders,
|
||||
TableColumnConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { defaultDialogConfig, IconButtonTypes, ListingComponent, listingProvidersFactory, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { type DonutChartConfig, DOSSIER_TEMPLATE_ID, type DossierState } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
@ -20,6 +12,8 @@ import {
|
||||
AddEditDossierStateDialogComponent,
|
||||
AddEditDossierStateDialogData,
|
||||
} from '../add-edit-dossier-state-dialog/add-edit-dossier-state-dialog.component';
|
||||
import { SortingOrders } from '@iqser/common-ui/lib/sorting';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-states-listing-screen.component.html',
|
||||
@ -27,6 +21,7 @@ import {
|
||||
providers: listingProvidersFactory(DossierStatesListingScreenComponent),
|
||||
})
|
||||
export class DossierStatesListingScreenComponent extends ListingComponent<DossierState> implements OnInit, OnDestroy {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly tableHeaderLabel = _('dossier-states-listing.table-header.title');
|
||||
readonly tableColumnConfigs: TableColumnConfig<DossierState>[] = [
|
||||
@ -35,7 +30,6 @@ export class DossierStatesListingScreenComponent extends ListingComponent<Dossie
|
||||
{ label: _('dossier-states-listing.table-col-names.dossiers-count'), class: 'flex-center' },
|
||||
];
|
||||
readonly chartConfig$: Observable<DonutChartConfig[]>;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
private readonly _dialog: MatDialog,
|
||||
|
||||
@ -4,7 +4,6 @@ import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { DossierTemplate, User } from '@red/domain';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getCurrentUser,
|
||||
IconButtonTypes,
|
||||
IqserPermissionsService,
|
||||
ListingComponent,
|
||||
@ -17,6 +16,7 @@ import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-templates-listing-screen.component.html',
|
||||
|
||||
@ -12,10 +12,10 @@ import {
|
||||
IqserHelpModeModule,
|
||||
IqserListingModule,
|
||||
IqserRoutes,
|
||||
IqserUsersModule,
|
||||
} from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { DossierTemplateActionsComponent } from '../../shared/components/dossier-template-actions/dossier-template-actions.component';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
|
||||
const routes: IqserRoutes = [{ path: '', component: DossierTemplatesListingScreenComponent }];
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
@ -17,6 +16,7 @@ import { DossierTemplateStatsService } from '@services/entity-services/dossier-t
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './entities-listing-screen.component.html',
|
||||
@ -24,6 +24,7 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
providers: listingProvidersFactory(EntitiesListingScreenComponent),
|
||||
})
|
||||
export class EntitiesListingScreenComponent extends ListingComponent<Dictionary> {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly tableHeaderLabel = _('entities-listing.table-header.title');
|
||||
@ -34,7 +35,6 @@ export class EntitiesListingScreenComponent extends ListingComponent<Dictionary>
|
||||
{ label: _('entities-listing.table-col-names.dictionary-entries') },
|
||||
];
|
||||
readonly templateStats$: Observable<DossierTemplateStats>;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
private readonly _loadingService: LoadingService,
|
||||
|
||||
@ -2,12 +2,14 @@ import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
import { getCurrentUser, getParam, IqserPermissionsService, List, LoadingService } from '@iqser/common-ui';
|
||||
import { IqserPermissionsService, LoadingService } from '@iqser/common-ui';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { DICTIONARY_TO_ENTRY_TYPE_MAP, DICTIONARY_TYPE_KEY_MAP, DictionaryType, DOSSIER_TEMPLATE_ID, ENTITY_TYPE, User } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { getParam, List } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dictionary-screen.component.html',
|
||||
@ -15,15 +17,15 @@ import { NGXLogger } from 'ngx-logger';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DictionaryScreenComponent implements OnInit {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
@ViewChild('dictionaryManager', { static: false })
|
||||
private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly roles = Roles;
|
||||
readonly initialEntries$ = new BehaviorSubject<string[]>([]);
|
||||
isLeavingPage = false;
|
||||
readonly type: DictionaryType;
|
||||
readonly entityType = getParam(ENTITY_TYPE);
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
@ViewChild('dictionaryManager', { static: false })
|
||||
private readonly _dictionaryManager: DictionaryManagerComponent;
|
||||
|
||||
constructor(
|
||||
route: ActivatedRoute,
|
||||
|
||||
@ -5,9 +5,10 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { getCurrentUser } from '@users/user.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
|
||||
import { IconButtonTypes, IqserEventTarget } from '@iqser/common-ui';
|
||||
import { IconButtonTypes } from '@iqser/common-ui';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Roles } from '@users/roles';
|
||||
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-entity-info',
|
||||
@ -16,14 +17,13 @@ import { Roles } from '@users/roles';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class EntityInfoComponent {
|
||||
@ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
||||
readonly currentUser = getCurrentUser();
|
||||
readonly entity$: Observable<Dictionary>;
|
||||
readonly dossierTemplateId: string;
|
||||
readonly roles = Roles;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
|
||||
@ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
||||
|
||||
constructor(route: ActivatedRoute, dictionariesMapService: DictionariesMapService, readonly permissionsService: PermissionsService) {
|
||||
this.dossierTemplateId = route.parent.snapshot.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||
const entityType = route.parent.snapshot.paramMap.get(ENTITY_TYPE);
|
||||
|
||||
@ -4,11 +4,12 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import * as Papa from 'papaparse';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { IconButtonTypes, ListingComponent, listingProvidersFactory, TableColumnConfig, Toaster, trackByFactory } from '@iqser/common-ui';
|
||||
import { IconButtonTypes, ListingComponent, listingProvidersFactory, TableColumnConfig, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileAttributeConfig, FileAttributeConfigTypes, FileAttributeEncodingTypes, IField, IFileAttributesConfig } from '@red/domain';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations';
|
||||
import { trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
export interface IFileAttributesCSVImportData {
|
||||
readonly csv: File;
|
||||
|
||||
@ -3,8 +3,6 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
defaultDialogConfig,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
largeDialogConfig,
|
||||
ListingComponent,
|
||||
@ -32,6 +30,8 @@ import {
|
||||
IFileAttributesCSVImportData,
|
||||
} from './file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component';
|
||||
import { FileAttributesConfigurationsDialogComponent } from './file-attributes-configurations-dialog/file-attributes-configurations-dialog.component';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './file-attributes-listing-screen.component.html',
|
||||
@ -39,6 +39,10 @@ import { FileAttributesConfigurationsDialogComponent } from './file-attributes-c
|
||||
providers: listingProvidersFactory(FileAttributesListingScreenComponent),
|
||||
})
|
||||
export class FileAttributesListingScreenComponent extends ListingComponent<FileAttributeConfig> implements OnInit, OnDestroy {
|
||||
@ViewChild('impactedTemplates') private readonly _impactedTemplatesRef: TemplateRef<unknown>;
|
||||
#existingConfiguration: IFileAttributesConfig;
|
||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
@ -59,18 +63,6 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
},
|
||||
];
|
||||
readonly roles = Roles;
|
||||
@ViewChild('impactedTemplates') private readonly _impactedTemplatesRef: TemplateRef<unknown>;
|
||||
#existingConfiguration: IFileAttributesConfig;
|
||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
private get _numberOfDisplayedAttrs(): number {
|
||||
return this.entitiesService.all.filter(attr => attr.displayedInFileList).length;
|
||||
}
|
||||
|
||||
private get _numberOfFilterableAttrs(): number {
|
||||
return this.entitiesService.all.filter(attr => attr.filterable).length;
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
@ -84,6 +76,14 @@ export class FileAttributesListingScreenComponent extends ListingComponent<FileA
|
||||
super();
|
||||
}
|
||||
|
||||
private get _numberOfDisplayedAttrs(): number {
|
||||
return this.entitiesService.all.filter(attr => attr.displayedInFileList).length;
|
||||
}
|
||||
|
||||
private get _numberOfFilterableAttrs(): number {
|
||||
return this.entitiesService.all.filter(attr => attr.filterable).length;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.#loadData();
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { SystemPreferences } from '@red/domain';
|
||||
import { BaseFormComponent, IqserPermissionsService, KeysOf, LoadingService } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, IqserPermissionsService, LoadingService } from '@iqser/common-ui';
|
||||
import { SystemPreferencesService } from '@services/system-preferences.service';
|
||||
import { systemPreferencesTranslations } from '@translations/system-preferences-translations';
|
||||
import { Roles } from '@users/roles';
|
||||
import { KeysOf } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
export type ValueType = 'number' | 'string' | 'boolean';
|
||||
|
||||
@ -13,24 +14,24 @@ export type ValueType = 'number' | 'string' | 'boolean';
|
||||
templateUrl: './system-preferences-form.component.html',
|
||||
})
|
||||
export class SystemPreferencesFormComponent extends BaseFormComponent {
|
||||
#initialConfiguration: SystemPreferences;
|
||||
readonly translations = systemPreferencesTranslations;
|
||||
readonly keys: { name: KeysOf<SystemPreferences>; type: ValueType }[] = [
|
||||
{ name: 'softDeleteCleanupTime', type: 'number' },
|
||||
{ name: 'downloadCleanupNotDownloadFilesHours', type: 'number' },
|
||||
{ name: 'downloadCleanupDownloadFilesHours', type: 'number' },
|
||||
];
|
||||
private _initialConfiguration: SystemPreferences;
|
||||
|
||||
constructor(
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _systemPreferencesService: SystemPreferencesService,
|
||||
private readonly _formBuilder: UntypedFormBuilder,
|
||||
private readonly _permissionsService: IqserPermissionsService,
|
||||
permissionsService: IqserPermissionsService,
|
||||
) {
|
||||
super();
|
||||
this.form = this._getForm();
|
||||
this._loadData();
|
||||
if (!_permissionsService.has(Roles.appConfiguration.write)) {
|
||||
this.form = this.#getForm();
|
||||
this.#loadData();
|
||||
if (!permissionsService.has(Roles.appConfiguration.write)) {
|
||||
this.form.disable();
|
||||
}
|
||||
}
|
||||
@ -38,19 +39,21 @@ export class SystemPreferencesFormComponent extends BaseFormComponent {
|
||||
async save(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
await this._systemPreferencesService.update(this.form.getRawValue());
|
||||
this._loadData();
|
||||
this.#loadData();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
private _getForm(): UntypedFormGroup {
|
||||
#getForm() {
|
||||
const controlsConfig = {};
|
||||
this.keys.forEach(key => (controlsConfig[key.name] = [this._systemPreferencesService.values[key.name], Validators.required]));
|
||||
this.keys.forEach(key => {
|
||||
controlsConfig[key.name] = [this._systemPreferencesService.values[key.name], Validators.required];
|
||||
});
|
||||
return this._formBuilder.group(controlsConfig);
|
||||
}
|
||||
|
||||
private _loadData() {
|
||||
this._initialConfiguration = this._systemPreferencesService.values;
|
||||
this.form.patchValue(this._initialConfiguration, { emitEvent: false });
|
||||
#loadData() {
|
||||
this.#initialConfiguration = this._systemPreferencesService.values;
|
||||
this.form.patchValue(this.#initialConfiguration, { emitEvent: false });
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,9 @@ import { CommonModule } from '@angular/common';
|
||||
import { DossierTemplateInfoScreenComponent } from './info-screen/dossier-template-info-screen.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { HasScrollbarDirective, IqserHelpModeModule, IqserUsersModule } from '@iqser/common-ui';
|
||||
import { HasScrollbarDirective, IqserHelpModeModule } from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
|
||||
const routes = [{ path: '', component: DossierTemplateInfoScreenComponent }];
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { DossierTemplatesService } from '@services/dossier-templates/dossier-tem
|
||||
import { DOSSIER_TEMPLATE_ID, type DossierTemplate, type DossierTemplateStats } from '@red/domain';
|
||||
import { DossierTemplateStatsService } from '@services/entity-services/dossier-template-stats.service';
|
||||
import { dossierTemplateStatusTranslations } from '@translations/dossier-template-status-translations';
|
||||
import { ContextComponent, getParam } from '@iqser/common-ui';
|
||||
import { ContextComponent, getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface Context {
|
||||
readonly dossierTemplate: DossierTemplate;
|
||||
|
||||
@ -2,12 +2,10 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
LoadingService,
|
||||
SortingOrders,
|
||||
TableColumnConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { DOSSIER_TEMPLATE_ID, Justification } from '@red/domain';
|
||||
@ -16,6 +14,8 @@ import { JustificationsDialogService } from '../justifications-dialog.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { SortingOrders } from '@iqser/common-ui/lib/sorting';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-justifications-screen',
|
||||
@ -28,6 +28,7 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
}),
|
||||
})
|
||||
export class JustificationsScreenComponent extends ListingComponent<Justification> implements OnInit {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly tableHeaderLabel = _('justifications-listing.table-header');
|
||||
@ -37,7 +38,6 @@ export class JustificationsScreenComponent extends ListingComponent<Justificatio
|
||||
{ label: _('justifications-listing.table-col-names.description'), width: '2fr' },
|
||||
];
|
||||
readonly canAddEditJustifications = this.permissionsService.canAddEditJustifications();
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
private readonly _justificationService: JustificationsService,
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, inject, Input } from '@angular/core';
|
||||
import { DOSSIER_TEMPLATE_ID, Justification } from '@red/domain';
|
||||
import { CircleButtonTypes, getParam } from '@iqser/common-ui';
|
||||
import { CircleButtonTypes } from '@iqser/common-ui';
|
||||
import { JustificationsDialogService } from '../justifications-dialog.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-table-item',
|
||||
@ -10,10 +11,10 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TableItemComponent {
|
||||
readonly #dossierTemplateId: string = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
@Input() justification: Justification;
|
||||
readonly canAddEditJustifications = inject(PermissionsService).canAddEditJustifications();
|
||||
readonly #dossierTemplateId: string = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(private readonly _dialogService: JustificationsDialogService) {}
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { size } from '@iqser/common-ui';
|
||||
import { size } from '@iqser/common-ui/lib/utils';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import type { DonutChartConfig, ILicenseReport } from '@red/domain';
|
||||
import { ChartDataset } from 'chart.js';
|
||||
import { ChartBlue, ChartGreen, ChartGrey, ChartRed } from '../../utils/constants';
|
||||
import { getLabelsFromMonthlyData, getLineConfig } from '../../utils/functions';
|
||||
import { getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@Component({
|
||||
@ -26,7 +26,7 @@ export class LicenseStorageComponent {
|
||||
}),
|
||||
map(license => ({
|
||||
datasets: this.#getDatasets(license),
|
||||
labels: getLabelsFromMonthlyData(license.monthlyData),
|
||||
labels: getLabelsFromLicense(license),
|
||||
})),
|
||||
);
|
||||
readonly size = size;
|
||||
|
||||
@ -5,7 +5,7 @@ import { map } from 'rxjs/operators';
|
||||
import type { ILicenseReport } from '@red/domain';
|
||||
import { ChartDataset } from 'chart.js';
|
||||
import { ChartBlue, ChartGreen, ChartRed } from '../../utils/constants';
|
||||
import { getLabelsFromMonthlyData, getLineConfig } from '../../utils/functions';
|
||||
import { getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
|
||||
@Component({
|
||||
selector: 'red-license-usage',
|
||||
@ -18,7 +18,7 @@ export class LicenseUsageComponent {
|
||||
map(() => this.licenseService.currentLicenseReport),
|
||||
map(license => ({
|
||||
datasets: this.#getDatasets(license),
|
||||
labels: getLabelsFromMonthlyData(license.monthlyData),
|
||||
labels: getLabelsFromLicense(license),
|
||||
})),
|
||||
);
|
||||
|
||||
@ -32,12 +32,14 @@ export class LicenseUsageComponent {
|
||||
|
||||
#getDatasets(license: ILicenseReport): ChartDataset[] {
|
||||
const monthlyData = license.monthlyData;
|
||||
|
||||
return [
|
||||
{
|
||||
data: monthlyData.flatMap(d => d.numberOfAnalyzedPages),
|
||||
label: 'Pages per Month',
|
||||
type: 'bar',
|
||||
backgroundColor: ChartBlue,
|
||||
borderColor: ChartBlue,
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ButtonConfig, getCurrentUser, IconButtonTypes, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { ButtonConfig, IconButtonTypes, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import type { User } from '@red/domain';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
templateUrl: './license-screen.component.html',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { FillTarget } from 'chart.js';
|
||||
import { hexToRgba } from '@utils/functions';
|
||||
import { ILicenseData } from '@red/domain';
|
||||
import { ILicenseData, ILicenseReport } from '@red/domain';
|
||||
import { ComplexFillTarget } from 'chart.js/dist/types';
|
||||
|
||||
const monthNames = dayjs.monthsShort();
|
||||
@ -30,3 +30,17 @@ export const getLineConfig: (
|
||||
});
|
||||
|
||||
export const getLabelsFromMonthlyData = (monthlyData: ILicenseData[]) => monthlyData.map(data => verboseDate(dayjs(data.startDate)));
|
||||
|
||||
export const getLabelsFromLicense = (license: ILicenseReport) => {
|
||||
let startMonth = dayjs(license.startDate);
|
||||
const endMonth = dayjs(license.endDate);
|
||||
|
||||
const result = [];
|
||||
|
||||
while (startMonth.isBefore(endMonth)) {
|
||||
result.push(verboseDate(startMonth));
|
||||
startMonth = startMonth.add(1, 'month');
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -1,13 +1,5 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
getCurrentUser,
|
||||
IqserPermissionsService,
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
LoadingService,
|
||||
SortingOrders,
|
||||
TableColumnConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { IqserPermissionsService, ListingComponent, listingProvidersFactory, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { PermissionsMapping, User } from '@red/domain';
|
||||
import { ConfigService } from '../config.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -20,6 +12,8 @@ import { switchMap, tap } from 'rxjs/operators';
|
||||
import { permissionsTranslations } from '@translations/permissions-translations';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { SortingOrders } from '@iqser/common-ui/lib/sorting';
|
||||
|
||||
@Component({
|
||||
templateUrl: './permissions-screen.component.html',
|
||||
@ -27,6 +21,7 @@ import { Roles } from '@users/roles';
|
||||
providers: listingProvidersFactory(PermissionsScreenComponent),
|
||||
})
|
||||
export class PermissionsScreenComponent extends ListingComponent<PermissionsMapping> implements OnDestroy {
|
||||
readonly #subscription: Subscription = new Subscription();
|
||||
readonly roles = Roles;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly translations = permissionsTranslations;
|
||||
@ -34,7 +29,6 @@ export class PermissionsScreenComponent extends ListingComponent<PermissionsMapp
|
||||
readonly tableHeaderLabel = _('permissions-screen.table-header.title');
|
||||
readonly targetObject: string;
|
||||
readonly mappedPermissions: string[];
|
||||
readonly #subscription: Subscription = new Subscription();
|
||||
|
||||
constructor(
|
||||
route: ActivatedRoute,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { DOSSIER_TEMPLATE_ID, IPlaceholdersResponse, IReportTemplate, User } from '@red/domain';
|
||||
import { download } from '@utils/file-download-utils';
|
||||
import { getCurrentUser, getParam, IConfirmationDialogData, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { IConfirmationDialogData, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import {
|
||||
generalPlaceholdersDescriptionsTranslations,
|
||||
@ -13,6 +13,8 @@ import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { ReportTemplateService } from '@services/report-template.service';
|
||||
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface Placeholder {
|
||||
placeholder: string;
|
||||
@ -30,12 +32,12 @@ const placeholderTypes: PlaceholderType[] = ['generalPlaceholders', 'fileAttribu
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ReportsScreenComponent implements OnInit {
|
||||
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly placeholders$ = new BehaviorSubject<Placeholder[]>([]);
|
||||
readonly availableTemplates$ = new BehaviorSubject<IReportTemplate[]>([]);
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly roles = Roles;
|
||||
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
private readonly _reportTemplateService: ReportTemplateService,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Debounce, getParam, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { RulesService } from '../../../services/rules.service';
|
||||
@ -8,6 +8,7 @@ import { firstValueFrom } from 'rxjs';
|
||||
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||
import { EditorThemeService } from '@services/editor-theme.service';
|
||||
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
||||
import { Debounce, getParam } from '@iqser/common-ui/lib/utils';
|
||||
import ICodeEditor = monaco.editor.ICodeEditor;
|
||||
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
||||
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
|
||||
@ -18,6 +19,11 @@ import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorCon
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
@ViewChild('fileInput')
|
||||
private _fileInput: ElementRef;
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _decorations: string[] = [];
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
||||
theme: 'vs',
|
||||
@ -25,19 +31,10 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
automaticLayout: true,
|
||||
readOnly: !this.permissionsService.canEditRules(),
|
||||
};
|
||||
|
||||
initialLines: string[] = [];
|
||||
currentLines: string[] = [];
|
||||
|
||||
isLeaving = false;
|
||||
|
||||
@ViewChild('fileInput')
|
||||
private _fileInput: ElementRef;
|
||||
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _decorations: string[] = [];
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _rulesService: RulesService,
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
ListingComponent,
|
||||
listingProvidersFactory,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
SearchPositions,
|
||||
TableColumnConfig,
|
||||
} from '@iqser/common-ui';
|
||||
@ -22,6 +21,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { userTypeChecker, userTypeFilters } from '../../../../utils';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
|
||||
function configToFilter({ key, label }: DonutChartConfig) {
|
||||
return new NestedFilter({
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -2,20 +2,7 @@ import { ChangeDetectorRef, Component, ElementRef, inject, OnInit, ViewChild } f
|
||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import {
|
||||
AsControl,
|
||||
BASE_HREF_FN,
|
||||
Debounce,
|
||||
getConfig,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
IqserPermissionsService,
|
||||
LoadingService,
|
||||
TenantsService,
|
||||
Toaster,
|
||||
trackByFactory,
|
||||
} from '@iqser/common-ui';
|
||||
import { getConfig, IconButtonTypes, IqserPermissionsService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import {
|
||||
AppConfig,
|
||||
DOSSIER_TEMPLATE_ID,
|
||||
@ -42,6 +29,9 @@ import { Roles } from '@users/roles';
|
||||
import { environment } from '@environments/environment';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { watermarkTranslations } from '@translations/watermark-translations';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { AsControl, BASE_HREF_FN, Debounce, getParam, trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
export const DEFAULT_WATERMARK: Partial<IWatermark> = {
|
||||
text: 'Watermark',
|
||||
@ -72,6 +62,13 @@ interface WatermarkForm {
|
||||
styleUrls: ['./watermark-screen.component.scss'],
|
||||
})
|
||||
export class WatermarkScreenComponent implements OnInit {
|
||||
@ViewChild('viewer', { static: true }) private readonly _viewer: ElementRef<HTMLDivElement>;
|
||||
private readonly _convertPath = inject(BASE_HREF_FN);
|
||||
readonly #loaded$ = new BehaviorSubject(false);
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly #watermarkId = Number(getParam(WATERMARK_ID));
|
||||
readonly #config = getConfig<AppConfig>();
|
||||
#watermark: Partial<IWatermark> = {};
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly translations = watermarkTranslations;
|
||||
readonly trackBy = trackByFactory();
|
||||
@ -89,13 +86,6 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
readonly watermarkHorizontalAlignments = Object.values(WATERMARK_HORIZONTAL_ALIGNMENTS);
|
||||
readonly watermarkVerticalAlignments = Object.values(WATERMARK_VERTICAL_ALIGNMENTS);
|
||||
currentAlignment: WatermarkAlignment;
|
||||
@ViewChild('viewer', { static: true }) private readonly _viewer: ElementRef<HTMLDivElement>;
|
||||
private readonly _convertPath = inject(BASE_HREF_FN);
|
||||
readonly #loaded$ = new BehaviorSubject(false);
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly #watermarkId = Number(getParam(WATERMARK_ID));
|
||||
readonly #config = getConfig<AppConfig>();
|
||||
#watermark: Partial<IWatermark> = {};
|
||||
|
||||
constructor(
|
||||
private readonly _http: HttpClient,
|
||||
|
||||
@ -9,12 +9,9 @@ import {
|
||||
HasScrollbarDirective,
|
||||
IconButtonComponent,
|
||||
IqserAllowDirective,
|
||||
IqserAuthGuard,
|
||||
IqserHelpModeModule,
|
||||
IqserListingModule,
|
||||
IqserRoutes,
|
||||
IqserUsersModule,
|
||||
TenantPipe,
|
||||
} from '@iqser/common-ui';
|
||||
import { RedRoleGuard } from '@users/red-role.guard';
|
||||
import { WATERMARK_ID } from '@red/domain';
|
||||
@ -27,6 +24,8 @@ import { MatSliderModule } from '@angular/material/slider';
|
||||
import { ColorPickerModule } from 'ngx-color-picker';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { IqserAuthGuard, IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
IconButtonTypes,
|
||||
IConfirmationDialogData,
|
||||
IqserPermissionsService,
|
||||
@ -18,6 +16,8 @@ import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './watermarks-listing-screen.component.html',
|
||||
@ -25,6 +25,7 @@ import { Roles } from '@users/roles';
|
||||
providers: listingProvidersFactory(WatermarksListingScreenComponent),
|
||||
})
|
||||
export class WatermarksListingScreenComponent extends ListingComponent<Watermark> {
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
@ -37,7 +38,6 @@ export class WatermarksListingScreenComponent extends ListingComponent<Watermark
|
||||
{ label: _('watermarks-listing.table-col-names.modified-on'), sortByKey: 'dateModified' },
|
||||
];
|
||||
readonly tableHeaderLabel = _('watermarks-listing.table-header.title');
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
private readonly _loadingService: LoadingService,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { filterEach, GenericService } from '@iqser/common-ui';
|
||||
import { GenericService } from '@iqser/common-ui';
|
||||
import { forkJoin, Observable, of } from 'rxjs';
|
||||
import {
|
||||
IDigitalSignatureRequest,
|
||||
@ -9,6 +9,7 @@ import {
|
||||
IPkcsDigitalSignatureRequest,
|
||||
} from '@red/domain';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { filterEach } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Injectable()
|
||||
export class DigitalSignatureService extends GenericService<IDigitalSignatureRequest> {
|
||||
|
||||
@ -5,9 +5,12 @@ import { adminSideNavTranslations } from '@translations/admin-side-nav-translati
|
||||
import { ActivatedRoute, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { AdminSideNavType, AdminSideNavTypes, DOSSIER_TEMPLATE_ID, ENTITY_TYPE, User, WATERMARK_ID } from '@red/domain';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser, IqserHelpModeModule, IqserPermissionsService, SideNavComponent, TenantPipe } from '@iqser/common-ui';
|
||||
import { IqserHelpModeModule, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NgForOf, NgIf } from '@angular/common';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { SideNavComponent } from '@iqser/common-ui/lib/shared';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
interface NavItem {
|
||||
readonly label: string;
|
||||
|
||||
@ -1,19 +1,14 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import {
|
||||
CircleButtonComponent,
|
||||
CircleButtonTypes,
|
||||
getCurrentUser,
|
||||
IqserHelpModeModule,
|
||||
LoadingService,
|
||||
TenantsService,
|
||||
} from '@iqser/common-ui';
|
||||
import { CircleButtonComponent, CircleButtonTypes, IqserHelpModeModule, LoadingService } from '@iqser/common-ui';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DOSSIER_TEMPLATE_ID, type User } from '@red/domain';
|
||||
import { NgIf } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-template-actions',
|
||||
|
||||
@ -8,7 +8,7 @@ import { DictionariesMapService } from '@services/entity-services/dictionaries-m
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TenantPipe } from '@iqser/common-ui';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-template-breadcrumbs',
|
||||
|
||||
@ -4,10 +4,11 @@ import { ArchivedDossiersScreenComponent } from './screens/archived-dossiers-scr
|
||||
import { ArchiveRoutingModule } from './archive-routing.module';
|
||||
import { TableItemComponent } from './components/table-item/table-item.component';
|
||||
import { ConfigService } from './services/config.service';
|
||||
import { IqserHelpModeModule, IqserListingModule, IqserUsersModule } from '@iqser/common-ui';
|
||||
import { IqserHelpModeModule, IqserListingModule } from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@NgModule({
|
||||
declarations: [TableItemComponent, ArchivedDossiersScreenComponent],
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { IFilterGroup, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
|
||||
import { TableColumnConfig } from '@iqser/common-ui';
|
||||
import { Dossier, User } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { dossierMemberChecker, dossierMemberQuickChecker, dossierOwnerQuickChecker, dossierTemplateChecker } from '@utils/index';
|
||||
import { UserService } from '@users/user.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { IFilterGroup, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { getCurrentUser, trackByFactory } from '@iqser/common-ui';
|
||||
import { trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||
import { User } from '@red/domain';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dashboard-screen.component.html',
|
||||
|
||||
@ -13,16 +13,7 @@ import {
|
||||
} from '@red/domain';
|
||||
import { TranslateChartService } from '@services/translate-chart.service';
|
||||
import { UserService } from '@users/user.service';
|
||||
import {
|
||||
ContextComponent,
|
||||
FilterService,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
INestedFilter,
|
||||
ProgressBarConfigModel,
|
||||
shareLast,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { ProgressBarConfigModel, Toaster } from '@iqser/common-ui';
|
||||
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { combineLatestWith, firstValueFrom } from 'rxjs';
|
||||
@ -31,6 +22,9 @@ import { map, tap } from 'rxjs/operators';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { FilterService, INestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
import { ContextComponent, getParam, shareLast } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface DossierDetailsContext {
|
||||
needsWorkFilters: INestedFilter[] | undefined;
|
||||
@ -45,17 +39,16 @@ interface DossierDetailsContext {
|
||||
styleUrls: ['./dossier-details.component.scss'],
|
||||
})
|
||||
export class DossierDetailsComponent extends ContextComponent<DossierDetailsContext> {
|
||||
#currentChartSubtitleIndex = 0;
|
||||
readonly #dossierId = getParam(DOSSIER_ID);
|
||||
@Input() dossierAttributes: DossierAttributeWithValue[];
|
||||
@Output() readonly toggleCollapse = new EventEmitter();
|
||||
|
||||
editingOwner = false;
|
||||
readonly roles = Roles;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly collapseTooltip = _('dossier-details.collapse');
|
||||
readonly expandTooltip = _('dossier-details.expand');
|
||||
chartConfig: DonutChartConfig[] = [];
|
||||
#currentChartSubtitleIndex = 0;
|
||||
readonly #dossierId = getParam(DOSSIER_ID);
|
||||
|
||||
constructor(
|
||||
private readonly _toaster: Toaster,
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
<ng-container *ngIf="configService.listingMode$ | async as mode">
|
||||
<div class="file-attribute" [ngClass]="{ 'workflow-attribute': mode === 'workflow' }" (click)="editFileAttribute($event)">
|
||||
<div class="value" [ngClass]="{ 'workflow-value': mode === 'workflow' }">
|
||||
<div (click)="editFileAttribute($event)" [ngClass]="{ 'workflow-attribute': mode === 'workflow' }" class="file-attribute">
|
||||
<div [ngClass]="{ 'workflow-value': mode === 'workflow' }" class="value">
|
||||
<b *ngIf="mode === 'workflow' && !isInEditMode"> {{ fileAttribute.label }}: </b>
|
||||
<span *ngIf="!isDate; else date" [ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }">
|
||||
<span
|
||||
*ngIf="!isDate; else date"
|
||||
[ngClass]="{ hide: isInEditMode, 'clamp-3': mode !== 'workflow' }"
|
||||
[matTooltip]="fileAttributeValue"
|
||||
>
|
||||
{{ fileAttributeValue || '-' }}</span
|
||||
>
|
||||
<ng-template #date>
|
||||
@ -14,20 +18,20 @@
|
||||
|
||||
<ng-container
|
||||
*ngIf="
|
||||
((fileAttributesService.isEditingFileAttribute$ | async) === false || isInEditMode) &&
|
||||
(fileAttributesService.isEditingFileAttribute() === false || isInEditMode) &&
|
||||
!file.isInitialProcessing &&
|
||||
permissionsService.canEditFileAttributes(file, dossier)
|
||||
"
|
||||
>
|
||||
<div
|
||||
*ngIf="!isInEditMode; else input"
|
||||
class="action-buttons edit-button"
|
||||
[ngClass]="{ 'workflow-edit-button': mode === 'workflow' }"
|
||||
[class.help-mode-button]="helpModeService.isHelpModeActive$ | async"
|
||||
(click)="editFileAttribute($event)"
|
||||
*ngIf="!isInEditMode; else input"
|
||||
[attr.help-mode-key]="'edit-file-attributes'"
|
||||
[class.help-mode-button]="helpModeService.isHelpModeActive$ | async"
|
||||
[ngClass]="{ 'workflow-edit-button': mode === 'workflow' }"
|
||||
class="action-buttons edit-button"
|
||||
>
|
||||
<div class="edit-icon" [ngClass]="{ 'workflow-edit-icon': mode === 'workflow' }">
|
||||
<div [ngClass]="{ 'workflow-edit-icon': mode === 'workflow' }" class="edit-icon">
|
||||
<mat-icon [svgIcon]="'iqser:edit'"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
@ -35,15 +39,15 @@
|
||||
</div>
|
||||
|
||||
<ng-template #input>
|
||||
<div class="edit-input" [ngClass]="{ 'workflow-edit-input': mode === 'workflow' }" stopPropagation>
|
||||
<form (submit)="save()" [formGroup]="form">
|
||||
<div [ngClass]="{ 'workflow-edit-input': mode === 'workflow' }" class="edit-input" iqserStopPropagation>
|
||||
<form [formGroup]="form">
|
||||
<iqser-dynamic-input
|
||||
(closedDatepicker)="closedDatepicker = $event"
|
||||
(keydown.escape)="close()"
|
||||
[formControlName]="fileAttribute.id"
|
||||
[id]="fileAttribute.id"
|
||||
[type]="fileAttribute.type"
|
||||
[ngClass]="{ 'workflow-input': mode === 'workflow' }"
|
||||
[type]="fileAttribute.type"
|
||||
></iqser-dynamic-input>
|
||||
|
||||
<iqser-circle-button
|
||||
@ -54,7 +58,7 @@
|
||||
class="save"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button (action)="close()" [size]="mode === 'workflow' ? 15 : 34" [icon]="'iqser:close'"></iqser-circle-button>
|
||||
<iqser-circle-button (action)="close()" [icon]="'iqser:close'" [size]="mode === 'workflow' ? 15 : 34"></iqser-circle-button>
|
||||
</form>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, HostListener, Input, OnDestroy } from '@angular/core';
|
||||
import { Component, computed, effect, HostListener, Input, OnDestroy } from '@angular/core';
|
||||
import { Dossier, File, FileAttributeConfigTypes, IFileAttributeConfig } from '@red/domain';
|
||||
import { BaseFormComponent, HelpModeService, ListingService, Toaster } from '@iqser/common-ui';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
import { firstValueFrom, Subscription } from 'rxjs';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
@ -25,6 +25,12 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
isInEditMode = false;
|
||||
closedDatepicker = true;
|
||||
readonly #subscriptions = new Subscription();
|
||||
readonly #shouldClose = computed(
|
||||
() =>
|
||||
this.fileAttributesService.isEditingFileAttribute() &&
|
||||
(this.fileAttribute.id !== this.fileAttributesService.openAttributeEdit() ||
|
||||
this.file.fileId !== this.fileAttributesService.fileEdit()),
|
||||
);
|
||||
|
||||
constructor(
|
||||
router: Router,
|
||||
@ -46,6 +52,15 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
tap(() => this.close()),
|
||||
);
|
||||
this.#subscriptions.add(sub2.subscribe());
|
||||
|
||||
effect(
|
||||
() => {
|
||||
if (this.#shouldClose()) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
{ allowSignalWrites: true },
|
||||
);
|
||||
}
|
||||
|
||||
get isDate(): boolean {
|
||||
@ -56,6 +71,13 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
return this.file.fileAttributes.attributeIdToValue[this.fileAttribute.id];
|
||||
}
|
||||
|
||||
@HostListener('document:click')
|
||||
clickOutside() {
|
||||
if (this.isInEditMode && this.closedDatepicker) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.#subscriptions.unsubscribe();
|
||||
}
|
||||
@ -64,6 +86,8 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
if (!this.file.isInitialProcessing && this.permissionsService.canEditFileAttributes(this.file, this.dossier)) {
|
||||
$event.stopPropagation();
|
||||
this.#toggleEdit();
|
||||
this.fileAttributesService.setFileEdit(this.file.fileId);
|
||||
this.fileAttributesService.setOpenAttributeEdit(this.fileAttribute.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,13 +119,6 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:click')
|
||||
clickOutside() {
|
||||
if (this.isInEditMode && this.closedDatepicker) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
#initFileAttributes() {
|
||||
const configs = this.fileAttributesService.getFileAttributeConfig(this.file.dossierTemplateId).fileAttributeConfigs;
|
||||
configs.forEach(config => {
|
||||
@ -116,13 +133,16 @@ 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];
|
||||
config[key] = [
|
||||
dayjs(attrValue, 'YYYY-MM-DD', true).isValid() ? dayjs(attrValue).toDate() : attrValue,
|
||||
Validators.pattern(/^(\s+\S+\s*)*(?!\s).*$/),
|
||||
];
|
||||
});
|
||||
return this._formBuilder.group(config);
|
||||
}
|
||||
|
||||
#formatAttributeValue(attrValue) {
|
||||
return this.isDate ? attrValue && dayjs(attrValue).format('YYYY-MM-DD') : attrValue;
|
||||
return this.isDate ? attrValue && dayjs(attrValue).format('YYYY-MM-DD') : attrValue.replaceAll(/\s\s+/g, ' ');
|
||||
}
|
||||
|
||||
#toggleEdit(): void {
|
||||
@ -133,7 +153,7 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
||||
}
|
||||
|
||||
this.isInEditMode = !this.isInEditMode;
|
||||
this.fileAttributesService.isEditingFileAttribute$.next(this.isInEditMode);
|
||||
this.fileAttributesService.isEditingFileAttribute.set(this.isInEditMode);
|
||||
|
||||
if (this.isInEditMode) {
|
||||
this.#focusOnEditInput();
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import {
|
||||
ActionConfig,
|
||||
CircleButtonTypes,
|
||||
EntitiesService,
|
||||
List,
|
||||
ListingService,
|
||||
LoadingService,
|
||||
some,
|
||||
SortingService,
|
||||
TenantsService,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { ActionConfig, CircleButtonTypes, EntitiesService, ListingService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { Dossier, File, IFile } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -22,6 +11,9 @@ import { ConfigService } from '../../config.service';
|
||||
import { PrimaryFileAttributeService } from '@services/primary-file-attribute.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { Roles } from '@users/roles';
|
||||
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
||||
import { List, some } from '@iqser/common-ui/lib/utils';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-overview-screen-header [dossier] [upload]',
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<div class="workflow-item" [class.help-mode-active]="helpModeService?.isHelpModeActive$ | async">
|
||||
<div [class.help-mode-active]="helpModeService?.isHelpModeActive$ | async" class="workflow-item">
|
||||
<div class="details-wrapper">
|
||||
<div class="details">
|
||||
<div
|
||||
[attr.help-mode-key]="'workflow_view'"
|
||||
[matTooltip]="file.filename"
|
||||
[routerLink]="file.routerLink | tenant"
|
||||
[attr.help-mode-key]="'workflow_view'"
|
||||
class="filename pointer"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
|
||||
<div *ngFor="let config of displayedAttributes" class="small-label mt-8 attribute">
|
||||
<redaction-file-attribute [file]="file" [dossier]="dossier" [fileAttribute]="config"></redaction-file-attribute>
|
||||
<redaction-file-attribute [dossier]="dossier" [fileAttribute]="config" [file]="file"></redaction-file-attribute>
|
||||
</div>
|
||||
|
||||
<redaction-file-workload [file]="file"></redaction-file-workload>
|
||||
@ -31,10 +31,11 @@
|
||||
<div #actionsWrapper class="actions-wrapper">
|
||||
<redaction-file-actions
|
||||
*ngIf="!file.isProcessing"
|
||||
[attr.help-mode-key]="'workflow_view'"
|
||||
[dossier]="dossier"
|
||||
[file]="file"
|
||||
[maxWidth]="width"
|
||||
[attr.help-mode-key]="'workflow_view'"
|
||||
iqserDisableStopPropagation
|
||||
type="dossier-overview-workflow"
|
||||
></redaction-file-actions>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core';
|
||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||
import { Debounce, HelpModeService } from '@iqser/common-ui';
|
||||
import { HelpModeService } from '@iqser/common-ui';
|
||||
import { Debounce } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-workflow-item [file] [dossier] [displayedAttributes]',
|
||||
@ -8,13 +9,12 @@ import { Debounce, HelpModeService } from '@iqser/common-ui';
|
||||
styleUrls: ['./workflow-item.component.scss'],
|
||||
})
|
||||
export class WorkflowItemComponent implements OnInit {
|
||||
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
||||
@Input() file: File;
|
||||
@Input() dossier: Dossier;
|
||||
@Input() displayedAttributes: IFileAttributeConfig[];
|
||||
width: number;
|
||||
|
||||
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
||||
|
||||
constructor(private readonly _changeRef: ChangeDetectorRef, @Optional() readonly helpModeService: HelpModeService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
@ -2,16 +2,9 @@ import { Injectable, TemplateRef } from '@angular/core';
|
||||
import {
|
||||
ActionConfig,
|
||||
getConfig,
|
||||
getCurrentUser,
|
||||
getParam,
|
||||
IFilterGroup,
|
||||
INestedFilter,
|
||||
IqserPermissionsService,
|
||||
keyChecker,
|
||||
List,
|
||||
ListingMode,
|
||||
ListingModes,
|
||||
NestedFilter,
|
||||
TableColumnConfig,
|
||||
WorkflowColumn,
|
||||
WorkflowConfig,
|
||||
@ -46,6 +39,9 @@ import { DictionariesMapService } from '@services/entity-services/dictionaries-m
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
import { getParam, List } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
|
||||
@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import {
|
||||
CircleButtonComponent,
|
||||
DisableStopPropagationDirective,
|
||||
DynamicInputComponent,
|
||||
HasScrollbarDirective,
|
||||
IqserAllowDirective,
|
||||
@ -10,10 +11,7 @@ import {
|
||||
IqserListingModule,
|
||||
IqserLoadingModule,
|
||||
IqserRoutes,
|
||||
IqserUsersModule,
|
||||
StatusBarComponent,
|
||||
StopPropagationDirective,
|
||||
TenantPipe,
|
||||
} from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { DossierOverviewScreenComponent } from './screen/dossier-overview-screen.component';
|
||||
@ -28,6 +26,9 @@ import { DossierOverviewScreenHeaderComponent } from './components/screen-header
|
||||
import { ViewModeSelectionComponent } from './components/view-mode-selection/view-mode-selection.component';
|
||||
import { FileAttributeComponent } from './components/file-attribute/file-attribute.component';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { StatusBarComponent } from '@iqser/common-ui/lib/shared';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
@ -70,6 +71,7 @@ const routes: IqserRoutes = [
|
||||
DynamicInputComponent,
|
||||
IqserAllowDirective,
|
||||
TenantPipe,
|
||||
DisableStopPropagationDirective,
|
||||
],
|
||||
})
|
||||
export class DossierOverviewModule {}
|
||||
|
||||
@ -19,16 +19,11 @@ import {
|
||||
CircleButtonTypes,
|
||||
CustomError,
|
||||
ErrorService,
|
||||
getParam,
|
||||
IqserPermissionsService,
|
||||
ListingComponent,
|
||||
ListingModes,
|
||||
listingProvidersFactory,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
OnAttach,
|
||||
OnDetach,
|
||||
shareLast,
|
||||
TableColumnConfig,
|
||||
TableComponent,
|
||||
WorkflowConfig,
|
||||
@ -46,6 +41,8 @@ import { BulkActionsService } from '../services/bulk-actions.service';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||
import { Roles } from '@users/roles';
|
||||
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
import { getParam, OnAttach, OnDetach, shareLast } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-overview-screen.component.html',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||
import { DossierStats, StatusSorter } from '@red/domain';
|
||||
import { List, StatusBarConfig } from '@iqser/common-ui';
|
||||
import { List } from '@iqser/common-ui/lib/utils';
|
||||
import { StatusBarConfig } from '@iqser/common-ui/lib/shared';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-documents-status',
|
||||
|
||||
@ -4,7 +4,7 @@ import { Observable } from 'rxjs';
|
||||
import { TranslateChartService } from '@services/translate-chart.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||
import { getParam } from '@iqser/common-ui';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossiers-listing-details',
|
||||
|
||||
@ -1,13 +1,5 @@
|
||||
import { Injectable, TemplateRef } from '@angular/core';
|
||||
import {
|
||||
ButtonConfig,
|
||||
IFilterGroup,
|
||||
INestedFilter,
|
||||
keyChecker,
|
||||
NestedFilter,
|
||||
OverlappingElements,
|
||||
TableColumnConfig,
|
||||
} from '@iqser/common-ui';
|
||||
import { ButtonConfig, TableColumnConfig } from '@iqser/common-ui';
|
||||
import {
|
||||
annotationDefaultColorConfig,
|
||||
AnnotationShapeMap,
|
||||
@ -35,6 +27,7 @@ import { DossierStatesMapService } from '@services/entity-services/dossier-state
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { SharedDialogService } from '@shared/services/dialog.service';
|
||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
|
||||
@ -1,14 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
CompositeRouteGuard,
|
||||
HasScrollbarDirective,
|
||||
IqserHelpModeModule,
|
||||
IqserListingModule,
|
||||
IqserRoutes,
|
||||
IqserUsersModule,
|
||||
StatusBarComponent,
|
||||
} from '@iqser/common-ui';
|
||||
import { CompositeRouteGuard, HasScrollbarDirective, IqserHelpModeModule, IqserListingModule, IqserRoutes } from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { DossiersListingScreenComponent } from './screen/dossiers-listing-screen.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
@ -21,6 +13,8 @@ import { DossierWorkloadColumnComponent } from './components/dossier-workload-co
|
||||
import { DossierDocumentsStatusComponent } from './components/dossier-documents-status/dossier-documents-status.component';
|
||||
import { DossierFilesGuard } from '@guards/dossier-files-guard';
|
||||
import { ACTIVE_DOSSIERS_SERVICE } from '../../tokens';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { StatusBarComponent } from '@iqser/common-ui/lib/shared';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { Dossier, DOSSIER_TEMPLATE_ID, DossierTemplate } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ButtonConfig, ListingComponent, listingProvidersFactory, OnAttach, TableComponent } from '@iqser/common-ui';
|
||||
import { ButtonConfig, ListingComponent, listingProvidersFactory, TableComponent } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { ConfigService } from '../config.service';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
@ -10,6 +10,7 @@ import { Router } from '@angular/router';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { SharedDialogService } from '@shared/services/dialog.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { OnAttach } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossiers-listing-screen.component.html',
|
||||
@ -18,17 +19,17 @@ import { DossierTemplatesService } from '@services/dossier-templates/dossier-tem
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DossiersListingScreenComponent extends ListingComponent<Dossier> implements OnInit, OnAttach {
|
||||
readonly tableColumnConfigs = this._configService.tableConfig;
|
||||
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
|
||||
readonly buttonConfigs: ButtonConfig[];
|
||||
readonly dossierTemplate: DossierTemplate;
|
||||
readonly computeFilters$ = this._activeDossiersService.all$.pipe(tap(() => this._computeAllFilters()));
|
||||
@ViewChild('needsWorkFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
static: true,
|
||||
})
|
||||
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
|
||||
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
|
||||
readonly tableColumnConfigs = this._configService.tableConfig;
|
||||
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
|
||||
readonly buttonConfigs: ButtonConfig[];
|
||||
readonly dossierTemplate: DossierTemplate;
|
||||
readonly computeFilters$ = this._activeDossiersService.all$.pipe(tap(() => this._computeAllFilters()));
|
||||
|
||||
constructor(
|
||||
router: Router,
|
||||
|
||||
@ -3,9 +3,9 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { annotationChangesTranslations } from '@translations/annotation-changes-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { KeysOf } from '@iqser/common-ui';
|
||||
import { ListItem } from '@models/file/list-item';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { KeysOf } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface Engine {
|
||||
readonly icon: string;
|
||||
@ -41,16 +41,14 @@ const changesProperties: KeysOf<AnnotationWrapper>[] = [
|
||||
styleUrls: ['./annotation-details.component.scss'],
|
||||
})
|
||||
export class AnnotationDetailsComponent implements OnChanges {
|
||||
private readonly _translateService = inject(TranslateService);
|
||||
private readonly _multiSelectService = inject(MultiSelectService);
|
||||
@Input() annotation: ListItem<AnnotationWrapper>;
|
||||
isPopoverOpen = false;
|
||||
|
||||
engines: Engine[];
|
||||
changesTooltip: string;
|
||||
noSelection: boolean;
|
||||
|
||||
private readonly _translateService = inject(TranslateService);
|
||||
private readonly _multiSelectService = inject(MultiSelectService);
|
||||
|
||||
getChangesTooltip(): string | undefined {
|
||||
const changes = changesProperties.filter(key => this.annotation.item[key]);
|
||||
|
||||
|
||||
@ -13,8 +13,8 @@
|
||||
(click)="comments.toggleExpandComments()"
|
||||
[matTooltip]="'comments.comments' | translate : { count: annotation.item.comments?.length }"
|
||||
class="comments-counter"
|
||||
iqserStopPropagation
|
||||
matTooltipPosition="above"
|
||||
stopPropagation
|
||||
>
|
||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||
{{ annotation.item.comments.length }}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectorRef, Component, computed, ElementRef, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { FilterService, HasScrollbarDirective, IqserEventTarget } from '@iqser/common-ui';
|
||||
import { HasScrollbarDirective } from '@iqser/common-ui';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
@ -9,6 +9,8 @@ import { EarmarkGroup } from '@red/domain';
|
||||
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
||||
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
||||
import { ListItem } from '@models/file/list-item';
|
||||
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
||||
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotations-list',
|
||||
@ -16,15 +18,14 @@ import { ListItem } from '@models/file/list-item';
|
||||
styleUrls: ['./annotations-list.component.scss'],
|
||||
})
|
||||
export class AnnotationsListComponent extends HasScrollbarDirective implements OnChanges {
|
||||
@Input() annotations: ListItem<AnnotationWrapper>[];
|
||||
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
|
||||
|
||||
readonly #earmarkGroups = computed(() => {
|
||||
if (this._viewModeService.isEarmarks()) {
|
||||
return this.#getEarmarksGroups();
|
||||
}
|
||||
return [] as EarmarkGroup[];
|
||||
});
|
||||
@Input() annotations: ListItem<AnnotationWrapper>[];
|
||||
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
|
||||
|
||||
constructor(
|
||||
protected readonly _elementRef: ElementRef,
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
<div
|
||||
(click)="toggleExpandComments()"
|
||||
class="all-caps-label pointer hide-comments"
|
||||
stopPropagation
|
||||
iqserStopPropagation
|
||||
translate="comments.hide-comments"
|
||||
></div>
|
||||
</ng-container>
|
||||
|
||||
@ -2,12 +2,14 @@ import { ChangeDetectorRef, Component, HostBinding, Input, OnInit, ViewChild } f
|
||||
import type { IComment, User } from '@red/domain';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ContextComponent, getCurrentUser, InputWithActionComponent, LoadingService, trackByFactory } from '@iqser/common-ui';
|
||||
import { InputWithActionComponent, LoadingService } from '@iqser/common-ui';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CommentingService } from '../../services/commenting.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
import { ContextComponent, trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface CommentsContext {
|
||||
hiddenComments: boolean;
|
||||
@ -19,12 +21,12 @@ interface CommentsContext {
|
||||
styleUrls: ['./comments.component.scss'],
|
||||
})
|
||||
export class CommentsComponent extends ContextComponent<CommentsContext> implements OnInit {
|
||||
@HostBinding('class.hidden') private _hidden = true;
|
||||
@ViewChild(InputWithActionComponent) private readonly _input: InputWithActionComponent;
|
||||
@Input() annotation: AnnotationWrapper;
|
||||
readonly trackBy = trackByFactory();
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
hiddenComments$: Observable<boolean>;
|
||||
@HostBinding('class.hidden') private _hidden = true;
|
||||
@ViewChild(InputWithActionComponent) private readonly _input: InputWithActionComponent;
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
|
||||
@ -6,7 +6,7 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
|
||||
import { type FileAttributeConfigType, FileAttributeConfigTypes } from '@red/domain';
|
||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
import { ContextComponent } from '@iqser/common-ui';
|
||||
import { ContextComponent } from '@iqser/common-ui/lib/utils';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
||||
interface FileAttribute {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
*ngIf="documentInfoService.hidden()"
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
[attr.help-mode-key]="'workload_in_editor'"
|
||||
[fileId]="state.file()?.id"
|
||||
[primaryFiltersSlug]="'primaryFilters'"
|
||||
[secondaryFiltersSlug]="'secondaryFilters'"
|
||||
></iqser-popup-filter>
|
||||
@ -216,6 +217,6 @@
|
||||
*ngIf="filter.id === 'skipped'"
|
||||
[icon]="skippedService.hideSkipped() ? 'red:visibility-off' : 'red:visibility'"
|
||||
[type]="circleButtonTypes.dark"
|
||||
preventDefault
|
||||
iqserPreventDefault
|
||||
></iqser-circle-button>
|
||||
</ng-template>
|
||||
|
||||
@ -1,18 +1,9 @@
|
||||
import { ChangeDetectorRef, Component, computed, effect, ElementRef, HostListener, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, computed, effect, ElementRef, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import {
|
||||
AutoUnsubscribe,
|
||||
bool,
|
||||
CircleButtonTypes,
|
||||
Debounce,
|
||||
FilterService,
|
||||
IconButtonTypes,
|
||||
INestedFilter,
|
||||
IqserEventTarget,
|
||||
} from '@iqser/common-ui';
|
||||
import { CircleButtonTypes, IconButtonTypes } from '@iqser/common-ui';
|
||||
import { combineLatest, delay, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ExcludedPagesService } from '../../services/excluded-pages.service';
|
||||
@ -30,6 +21,9 @@ import { REDDocumentViewer } from '../../../pdf-viewer/services/document-viewer.
|
||||
import { SuggestionsService } from '../../services/suggestions.service';
|
||||
import { ListItem } from '@models/file/list-item';
|
||||
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
||||
import { getLocalStorageDataByFileId } from '@utils/local-storage';
|
||||
import { FilterService, INestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
import { AutoUnsubscribe, bool, Debounce, IqserEventTarget } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
@ -39,10 +33,11 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
templateUrl: './file-workload.component.html',
|
||||
styleUrls: ['./file-workload.component.scss'],
|
||||
})
|
||||
export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
|
||||
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
displayedAnnotations = new Map<number, AnnotationWrapper[]>();
|
||||
displayedPages: number[] = [];
|
||||
pagesPanelActive = true;
|
||||
@ -51,8 +46,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
|
||||
this.viewModeService.isEarmarks() ? _('file-preview.tabs.highlights.label') : _('file-preview.tabs.annotations.label'),
|
||||
);
|
||||
readonly currentPageIsExcluded = computed(() => this.state.file().excludedPages.includes(this.pdf.currentPage()));
|
||||
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
|
||||
|
||||
constructor(
|
||||
readonly filterService: FilterService,
|
||||
@ -155,6 +148,20 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
setTimeout(() => {
|
||||
const showExcludePages = getLocalStorageDataByFileId(this.state.file()?.id, 'show-exclude-pages') ?? false;
|
||||
if (showExcludePages) {
|
||||
this.excludedPagesService.show();
|
||||
}
|
||||
|
||||
const showDocumentInfo = getLocalStorageDataByFileId(this.state.file()?.id, 'show-document-info') ?? false;
|
||||
if (showDocumentInfo) {
|
||||
this.documentInfoService.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selectAllOnActivePage() {
|
||||
this.listingService.selectAnnotations(this.activeAnnotations);
|
||||
}
|
||||
|
||||
@ -3,12 +3,13 @@ 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 { ContextComponent, getConfig } from '@iqser/common-ui';
|
||||
import { getConfig } from '@iqser/common-ui';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { AppConfig, ViewedPage } from '@red/domain';
|
||||
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||
import { pairwise } from 'rxjs';
|
||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { ContextComponent } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
interface PageIndicatorContext {
|
||||
isRotated: boolean;
|
||||
@ -21,14 +22,13 @@ interface PageIndicatorContext {
|
||||
styleUrls: ['./page-indicator.component.scss'],
|
||||
})
|
||||
export class PageIndicatorComponent extends ContextComponent<PageIndicatorContext> implements OnChanges, OnInit {
|
||||
readonly #config = getConfig<AppConfig>();
|
||||
@Input({ required: true }) number: number;
|
||||
@Input() showDottedIcon = false;
|
||||
@Input() activeSelection = false;
|
||||
@Input() read = false;
|
||||
@Output() readonly pageSelected = new EventEmitter<number>();
|
||||
|
||||
pageReadTimeout: number = null;
|
||||
readonly #config = getConfig<AppConfig>();
|
||||
|
||||
constructor(
|
||||
private readonly _viewedPagesService: ViewedPagesService,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, inject, Input } from '@angular/core';
|
||||
import { List } from '@iqser/common-ui';
|
||||
import { List } from '@iqser/common-ui/lib/utils';
|
||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
||||
@ -14,13 +14,13 @@ import { ViewedPage } from '@red/domain';
|
||||
styleUrls: ['./pages.component.scss'],
|
||||
})
|
||||
export class PagesComponent {
|
||||
@Input({ required: true }) pages: List<number>;
|
||||
@Input({ required: true }) displayedAnnotations: Map<number, AnnotationWrapper[]>;
|
||||
protected readonly _pdf = inject(PdfViewer);
|
||||
readonly #state = inject(FilePreviewStateService);
|
||||
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
|
||||
readonly #multiSelectService = inject(MultiSelectService);
|
||||
readonly #listingService = inject(AnnotationsListingService);
|
||||
protected readonly _pdf = inject(PdfViewer);
|
||||
@Input({ required: true }) pages: List<number>;
|
||||
@Input({ required: true }) displayedAnnotations: Map<number, AnnotationWrapper[]>;
|
||||
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
|
||||
|
||||
pageSelectedByClick($event: number): void {
|
||||
this._pdf.navigateTo($event);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { File, User } from '@red/domain';
|
||||
import { getCurrentUser, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -9,6 +9,7 @@ import { FilesService } from '@services/files/files.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { FileAssignService } from '../../../shared-dossiers/services/file-assign.service';
|
||||
import { moveElementInArray } from '@utils/functions';
|
||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-management',
|
||||
@ -16,17 +17,6 @@ import { moveElementInArray } from '@utils/functions';
|
||||
styleUrls: ['./user-management.component.scss'],
|
||||
})
|
||||
export class UserManagementComponent {
|
||||
readonly translations = workflowFileStatusTranslations;
|
||||
readonly statusBarConfig = computed(() => [{ length: 1, color: this.state.file().workflowStatus }]);
|
||||
readonly assignTooltip = computed(() => {
|
||||
const file = this.state.file();
|
||||
return file.isUnderApproval
|
||||
? _('dossier-overview.assign-approver')
|
||||
: file.assignee
|
||||
? _('file-preview.change-reviewer')
|
||||
: _('file-preview.assign-reviewer');
|
||||
});
|
||||
editingReviewer = false;
|
||||
protected readonly _canAssignToSelf = computed(() => this.permissionsService.canAssignToSelf(this.state.file(), this.state.dossier()));
|
||||
protected readonly _canAssignUser = computed(() => this.permissionsService.canAssignUser(this.state.file(), this.state.dossier()));
|
||||
protected readonly _canUnassignUser = computed(() => this.permissionsService.canUnassignUser(this.state.file(), this.state.dossier()));
|
||||
@ -45,6 +35,17 @@ export class UserManagementComponent {
|
||||
: this.#customSort([...dossier.memberIds, ...unassignUser]);
|
||||
});
|
||||
protected readonly _currentUserId = getCurrentUser().id;
|
||||
readonly translations = workflowFileStatusTranslations;
|
||||
readonly statusBarConfig = computed(() => [{ length: 1, color: this.state.file().workflowStatus }]);
|
||||
readonly assignTooltip = computed(() => {
|
||||
const file = this.state.file();
|
||||
return file.isUnderApproval
|
||||
? _('dossier-overview.assign-approver')
|
||||
: file.assignee
|
||||
? _('file-preview.change-reviewer')
|
||||
: _('file-preview.assign-reviewer');
|
||||
});
|
||||
editingReviewer = false;
|
||||
|
||||
constructor(
|
||||
readonly fileAssignService: FileAssignService,
|
||||
|
||||
@ -3,11 +3,12 @@ import { ViewMode, ViewModes } from '@red/domain';
|
||||
import { ViewModeService } from '../../services/view-mode.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
import { BASE_HREF, ConfirmOptions, IConfirmationDialogData, Toaster } from '@iqser/common-ui';
|
||||
import { ConfirmOptions, IConfirmationDialogData, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { BASE_HREF } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-view-switch',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<section class="dialog">
|
||||
<form (submit)="save()" [formGroup]="form">
|
||||
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
|
||||
<div [translate]="!hint ? 'redact-text.dialog.title' : 'redact-text.dialog.hint-title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="iqser-input-group w-450">
|
||||
@ -42,7 +42,7 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container *deny="roles.getRss; if: dictionaryRequest || type === 'HINT'">
|
||||
<div class="iqser-input-group required w-450">
|
||||
<div class="iqser-input-group required w-450" *ngIf="dictionaryRequest">
|
||||
<label [translate]="'redact-text.dialog.content.type'"></label>
|
||||
|
||||
<mat-form-field>
|
||||
@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="iqser-input-group w-450">
|
||||
<div *ngIf="!dictionaryRequest" class="iqser-input-group w-450">
|
||||
<label [translate]="'redact-text.dialog.content.comment'"></label>
|
||||
<textarea
|
||||
formControlName="comment"
|
||||
|
||||
@ -28,6 +28,7 @@ interface RedactTextData {
|
||||
dossierId: string;
|
||||
file: File;
|
||||
applyToAllDossiers: boolean;
|
||||
isApprover: boolean;
|
||||
}
|
||||
|
||||
interface DialogResult {
|
||||
@ -58,7 +59,7 @@ export class RedactTextDialogComponent
|
||||
|
||||
readonly #translations = redactTextTranslations;
|
||||
readonly #dossier: Dossier;
|
||||
readonly #hint: boolean;
|
||||
readonly hint: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _justificationsService: JustificationsService,
|
||||
@ -70,7 +71,7 @@ export class RedactTextDialogComponent
|
||||
super();
|
||||
this.#dossier = _activeDossiersService.find(this.data.dossierId);
|
||||
this.type = this.data.manualRedactionEntryWrapper.type;
|
||||
this.#hint = this.type === ManualRedactionEntryTypes.HINT;
|
||||
this.hint = this.type === ManualRedactionEntryTypes.HINT;
|
||||
this.#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
||||
this.#manualRedactionTypeExists = this._dictionaryService.hasManualType(this.#dossier.dossierTemplateId);
|
||||
this.options = this.#options();
|
||||
@ -98,14 +99,18 @@ export class RedactTextDialogComponent
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
if (this.dictionaryRequest || this.#hint) {
|
||||
if (this.dictionaryRequest || this.hint) {
|
||||
return !this.form.get('dictionary').value;
|
||||
}
|
||||
return !this.form.get('reason').value;
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.dictionaries = this._dictionaryService.getPossibleDictionaries(this.#dossier.dossierTemplateId, this.#hint);
|
||||
this.dictionaries = this._dictionaryService.getPossibleDictionaries(
|
||||
this.#dossier.dossierTemplateId,
|
||||
this.hint,
|
||||
!this.#applyToAllDossiers,
|
||||
);
|
||||
|
||||
const data = await firstValueFrom(this._justificationsService.getForDossierTemplate(this.#dossier.dossierTemplateId));
|
||||
this.legalOptions = data.map(lbm => ({
|
||||
@ -121,6 +126,19 @@ export class RedactTextDialogComponent
|
||||
|
||||
extraOptionChanged(option: DetailsRadioOption<RedactTextOption>): void {
|
||||
this.#applyToAllDossiers = option.extraOption.checked;
|
||||
|
||||
this.dictionaries = this._dictionaryService.getPossibleDictionaries(
|
||||
this.#dossier.dossierTemplateId,
|
||||
this.hint,
|
||||
!this.#applyToAllDossiers,
|
||||
);
|
||||
if (this.#applyToAllDossiers && this.form.get('dictionary').value) {
|
||||
const selectedDictionaryLabel = this.form.get('dictionary').value;
|
||||
const selectedDictionary = this.dictionaries.find(d => d.type === selectedDictionaryLabel);
|
||||
if (!selectedDictionary) {
|
||||
this.form.get('dictionary').setValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save(): void {
|
||||
@ -185,6 +203,7 @@ export class RedactTextDialogComponent
|
||||
addRedactionRequest.value = addRedactionRequest.rectangle
|
||||
? this.form.get('classification').value
|
||||
: this.form.get('selectedText').value;
|
||||
addRedactionRequest.addToAllDossiers = this.data.isApprover && this.dictionaryRequest && this.#applyToAllDossiers;
|
||||
}
|
||||
|
||||
#options() {
|
||||
@ -206,6 +225,7 @@ export class RedactTextDialogComponent
|
||||
extraOption: {
|
||||
label: this.#translations[this.type].inDossier.extraOptionLabel,
|
||||
checked: this.data.applyToAllDossiers ?? true,
|
||||
hidden: !this.data.isApprover,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { IqserDialogComponent } from '../../../../../../../../libs/common-ui/src
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { RedactTextOption } from '../redact-text-dialog/redact-text-options';
|
||||
|
||||
const PIN_ICON = 'red:push-pin';
|
||||
const FOLDER_ICON = 'red:folder';
|
||||
@ -25,6 +26,8 @@ export interface RemoveRedactionData {
|
||||
dossier: Dossier;
|
||||
falsePositiveContext: string;
|
||||
permissions: RemoveRedactionPermissions;
|
||||
applyToAllDossiers: boolean;
|
||||
isApprover: boolean;
|
||||
}
|
||||
|
||||
export interface RemoveRedactionResult {
|
||||
@ -50,6 +53,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
readonly #redaction: AnnotationWrapper;
|
||||
readonly #permissions: RemoveRedactionPermissions;
|
||||
readonly #translations = removeRedactionTranslations;
|
||||
#applyToAllDossiers: boolean;
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder, private readonly _permissionsService: PermissionsService) {
|
||||
super();
|
||||
@ -57,13 +61,17 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
this.#permissions = this.data.permissions;
|
||||
this.options = this.#options();
|
||||
this.form = this.#getForm();
|
||||
this.#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
||||
|
||||
this.form
|
||||
.get('option')
|
||||
.valueChanges.pipe(
|
||||
tap(() => {
|
||||
this.options[1].extraOption.checked = true;
|
||||
this.options[2].extraOption.checked = true;
|
||||
for (const option of this.options) {
|
||||
if (option.extraOption) {
|
||||
option.extraOption.checked = this.#applyToAllDossiers;
|
||||
}
|
||||
}
|
||||
}),
|
||||
takeUntilDestroyed(),
|
||||
)
|
||||
@ -101,7 +109,8 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
value: RemoveRedactionOptions.IN_DOSSIER,
|
||||
extraOption: {
|
||||
label: this.#translations.IN_DOSSIER.extraOptionLabel,
|
||||
checked: true,
|
||||
checked: this.data.applyToAllDossiers ?? true,
|
||||
hidden: !this.data.isApprover,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -114,7 +123,8 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
value: RemoveRedactionOptions.FALSE_POSITIVE,
|
||||
extraOption: {
|
||||
label: this.#translations.FALSE_POSITIVE.extraOptionLabel,
|
||||
checked: true,
|
||||
checked: this.data.applyToAllDossiers ?? true,
|
||||
hidden: !this.data.isApprover,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import { SkippedService } from './services/skipped.service';
|
||||
import { AnnotationActionsService } from './services/annotation-actions.service';
|
||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||
import { AnnotationReferencesService } from './services/annotation-references.service';
|
||||
import { EntitiesService, FilterService, ListingService, SearchService, SortingService } from '@iqser/common-ui';
|
||||
import { EntitiesService, ListingService, SearchService } from '@iqser/common-ui';
|
||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||
import { FileDataService } from './services/file-data.service';
|
||||
@ -15,6 +15,8 @@ import { AnnotationsListingService } from './services/annotations-listing.servic
|
||||
import { StampService } from './services/stamp.service';
|
||||
import { PdfProxyService } from './services/pdf-proxy.service';
|
||||
import { PdfAnnotationActionsService } from './services/pdf-annotation-actions.service';
|
||||
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
||||
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
||||
|
||||
export const filePreviewScreenProviders = [
|
||||
FilterService,
|
||||
|
||||
@ -11,27 +11,17 @@ import {
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||
import {
|
||||
AutoUnsubscribe,
|
||||
Bind,
|
||||
bool,
|
||||
CircleButtonTypes,
|
||||
ConfirmOption,
|
||||
ConfirmOptions,
|
||||
CustomError,
|
||||
Debounce,
|
||||
ErrorService,
|
||||
FilterService,
|
||||
HelpModeService,
|
||||
IConfirmationDialogData,
|
||||
List,
|
||||
IqserDialog,
|
||||
LoadingService,
|
||||
NestedFilter,
|
||||
OnAttach,
|
||||
OnDetach,
|
||||
processFilters,
|
||||
TenantsService,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
@ -41,7 +31,7 @@ import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.se
|
||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||
import { Dictionary, File, ViewModes } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, firstValueFrom, of, pairwise } from 'rxjs';
|
||||
import { combineLatest, first, firstValueFrom, of, pairwise } from 'rxjs';
|
||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||
import { byId, byPage, download, handleFilterDelta, hasChanges } from '../../utils';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
@ -74,9 +64,11 @@ 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';
|
||||
import { AutoUnsubscribe, Bind, bool, Debounce, List, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils';
|
||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
const textActions = [TextPopups.REDACT_TEXT, TextPopups.ADD_HINT, TextPopups.ADD_FALSE_POSITIVE];
|
||||
|
||||
@ -89,18 +81,18 @@ export class FilePreviewScreenComponent
|
||||
extends AutoUnsubscribe
|
||||
implements AfterViewInit, OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate
|
||||
{
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly roles = Roles;
|
||||
fullScreen = false;
|
||||
readonly fileId = this.state.fileId;
|
||||
readonly dossierId = this.state.dossierId;
|
||||
width: number;
|
||||
@ViewChild('annotationFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
static: false,
|
||||
})
|
||||
private readonly _filterTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('actionsWrapper', { static: false }) private readonly _actionsWrapper: ElementRef;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly roles = Roles;
|
||||
fullScreen = false;
|
||||
readonly fileId = this.state.fileId;
|
||||
readonly dossierId = this.state.dossierId;
|
||||
width: number;
|
||||
|
||||
constructor(
|
||||
readonly pdf: PdfViewer,
|
||||
@ -116,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,
|
||||
@ -167,18 +158,6 @@ export class FilePreviewScreenComponent
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const selectedText = this._documentViewer.selectedText();
|
||||
const canPerformActions = this.pdfProxyService.canPerformActions();
|
||||
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
|
||||
|
||||
if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
|
||||
this.pdf.enable(textActions);
|
||||
} else {
|
||||
this.pdf.disable(textActions);
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
if (this._viewModeService.viewMode()) {
|
||||
this.updateViewMode().then();
|
||||
@ -329,13 +308,14 @@ export class FilePreviewScreenComponent
|
||||
|
||||
this._loadingService.start();
|
||||
this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId).then();
|
||||
this._subscribeToFileUpdates();
|
||||
this.#subscribeToFileUpdates();
|
||||
|
||||
if (file?.analysisRequired && !file.excludedFromAutomaticAnalysis) {
|
||||
await this._reanalysisService.reanalyzeFilesForDossier([file], this.dossierId, { force: true });
|
||||
}
|
||||
|
||||
this.pdfProxyService.configureElements();
|
||||
this.#restoreOldFilters();
|
||||
document.documentElement.addEventListener('fullscreenchange', this.fullscreenListener);
|
||||
}
|
||||
|
||||
@ -374,34 +354,35 @@ export class FilePreviewScreenComponent
|
||||
async openRedactTextDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
|
||||
const file = this.state.file();
|
||||
const dossierTemplate = this._dossierTemplatesService.find(this.state.dossierTemplateId);
|
||||
const result = await this._iqserDialog
|
||||
.openDefault(RedactTextDialogComponent, {
|
||||
data: {
|
||||
manualRedactionEntryWrapper,
|
||||
dossierId: this.dossierId,
|
||||
file,
|
||||
applyToAllDossiers: dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault,
|
||||
},
|
||||
})
|
||||
.result();
|
||||
const isApprover = this.permissionsService.isApprover(this.state.dossier());
|
||||
const ref = this._iqserDialog.openDefault(RedactTextDialogComponent, {
|
||||
data: {
|
||||
manualRedactionEntryWrapper,
|
||||
dossierId: this.dossierId,
|
||||
file,
|
||||
applyToAllDossiers: isApprover ? dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault : false,
|
||||
isApprover,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const add$ = this._manualRedactionService.addAnnotation(
|
||||
[result.redaction],
|
||||
this.dossierId,
|
||||
this.fileId,
|
||||
result.dictionary?.label,
|
||||
);
|
||||
|
||||
if (result.applyToAllDossiers !== null) {
|
||||
const { ...body } = dossierTemplate;
|
||||
body.applyDictionaryUpdatesToAllDossiersByDefault = result.applyToAllDossiers;
|
||||
await this._dossierTemplatesService.createOrUpdate(body);
|
||||
}
|
||||
|
||||
const addAndReload$ = add$.pipe(switchMap(() => this._filesService.reload(this.dossierId, file)));
|
||||
return firstValueFrom(addAndReload$.pipe(catchError(() => of(undefined))));
|
||||
const result = await ref.result();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const add$ = this._manualRedactionService.addAnnotation([result.redaction], this.dossierId, this.fileId, result.dictionary?.label);
|
||||
|
||||
if (isApprover && result.applyToAllDossiers !== null) {
|
||||
const { ...body } = dossierTemplate;
|
||||
body.applyDictionaryUpdatesToAllDossiersByDefault = result.applyToAllDossiers;
|
||||
await this._dossierTemplatesService.createOrUpdate(body);
|
||||
}
|
||||
|
||||
const addAndReload$ = add$.pipe(
|
||||
tap(() => this._documentViewer.clearSelection()),
|
||||
switchMap(() => this._filesService.reload(this.dossierId, file)),
|
||||
);
|
||||
return firstValueFrom(addAndReload$.pipe(catchError(() => of(undefined))));
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
@ -630,7 +611,7 @@ export class FilePreviewScreenComponent
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private _subscribeToFileUpdates(): void {
|
||||
#subscribeToFileUpdates(): void {
|
||||
this.addActiveScreenSubscription = this.loadAnnotations().subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = this._dossiersService
|
||||
@ -662,7 +643,7 @@ export class FilePreviewScreenComponent
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this.pdfProxyService.redactTextRequested$.subscribe($event => {
|
||||
this.openRedactTextDialog($event);
|
||||
this.openRedactTextDialog($event).then();
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this.pdfProxyService.pageChanged$.subscribe(page =>
|
||||
@ -741,6 +722,20 @@ export class FilePreviewScreenComponent
|
||||
),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
const selectedTextEffect = this._documentViewer.selectedText$.pipe(
|
||||
tap(selectedText => {
|
||||
const canPerformActions = this.pdfProxyService.canPerformActions();
|
||||
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
|
||||
|
||||
if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
|
||||
this.pdf.enable(textActions);
|
||||
} else {
|
||||
this.pdf.disable(textActions);
|
||||
}
|
||||
}),
|
||||
);
|
||||
this.addActiveScreenSubscription = selectedTextEffect.subscribe();
|
||||
}
|
||||
|
||||
#handleDeletedDossier(): void {
|
||||
@ -822,4 +817,18 @@ export class FilePreviewScreenComponent
|
||||
private _isJapaneseString(text: string) {
|
||||
return text.match(/[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf]/);
|
||||
}
|
||||
|
||||
#restoreOldFilters() {
|
||||
combineLatest([
|
||||
this._filterService.getGroup$('primaryFilters').pipe(first(filterGroup => !!filterGroup?.filters.length)),
|
||||
this._filterService.getGroup$('secondaryFilters').pipe(first(secondaryFilters => !!secondaryFilters?.filters.length)),
|
||||
]).subscribe(([primaryFilters, secondaryFilters]) => {
|
||||
const localStorageFiltersString = localStorage.getItem('workload-filters') ?? '{}';
|
||||
const localStorageFilters = JSON.parse(localStorageFiltersString)[this.fileId];
|
||||
if (localStorageFilters) {
|
||||
copyLocalStorageFiltersValues(primaryFilters.filters, localStorageFilters.primaryFilters);
|
||||
copyLocalStorageFiltersValues(secondaryFilters.filters, localStorageFilters.secondaryFilters);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,17 +12,13 @@ import {
|
||||
InputWithActionComponent,
|
||||
IqserAllowDirective,
|
||||
IqserDenyDirective,
|
||||
IqserFiltersModule,
|
||||
IqserHelpModeModule,
|
||||
IqserRoutes,
|
||||
IqserUploadFileModule,
|
||||
IqserUsersModule,
|
||||
LogPipe,
|
||||
PreventDefaultDirective,
|
||||
RoundCheckboxComponent,
|
||||
StatusBarComponent,
|
||||
StopPropagationDirective,
|
||||
TenantPipe,
|
||||
} from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
@ -66,13 +62,16 @@ import { SharedModule } from '@shared/shared.module';
|
||||
import { SharedDossiersModule } from '../shared-dossiers/shared-dossiers.module';
|
||||
import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component';
|
||||
import { RemoveRedactionDialogComponent } from './dialogs/remove-redaction-dialog/remove-redaction-dialog.component';
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { IqserFiltersModule } from '@iqser/common-ui/lib/filtering';
|
||||
import { StatusBarComponent } from '@iqser/common-ui/lib/shared';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
path: '',
|
||||
component: FilePreviewScreenComponent,
|
||||
pathMatch: 'full',
|
||||
data: { reuse: true },
|
||||
canDeactivate: [PendingChangesGuard, DocumentUnloadedGuard],
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ManualRedactionService } from './manual-redaction.service';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { firstValueFrom, Observable, of } from 'rxjs';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
import { getFirstRelevantTextPart } from '../../../utils';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import {
|
||||
DictionaryEntryTypes,
|
||||
EarmarkOperation,
|
||||
IAddRedactionRequest,
|
||||
ILegalBasisChangeRequest,
|
||||
IRecategorizationRequest,
|
||||
IRectangle,
|
||||
@ -20,7 +19,7 @@ import {
|
||||
AcceptRecommendationDialogComponent,
|
||||
AcceptRecommendationReturnType,
|
||||
} from '../dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component';
|
||||
import { defaultDialogConfig, isJustOne, List } from '@iqser/common-ui';
|
||||
import { defaultDialogConfig } from '@iqser/common-ui';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { FilePreviewStateService } from './file-preview-state.service';
|
||||
@ -38,6 +37,8 @@ import {
|
||||
import { RemoveRedactionOptions } from '../dialogs/remove-redaction-dialog/remove-redaction-options';
|
||||
import { IqserDialog } from '../../../../../../../libs/common-ui/src/lib/dialog/iqser-dialog.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { isJustOne, List } from '@iqser/common-ui/lib/utils';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
|
||||
@Injectable()
|
||||
export class AnnotationActionsService {
|
||||
@ -54,6 +55,7 @@ export class AnnotationActionsService {
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _skippedService: SkippedService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
) {}
|
||||
|
||||
acceptSuggestion(annotations: AnnotationWrapper[]) {
|
||||
@ -125,6 +127,8 @@ export class AnnotationActionsService {
|
||||
canRemoveOrSuggestToRemoveFromDictionary: permissions.canRemoveOrSuggestToRemoveFromDictionary,
|
||||
canMarkAsFalsePositive: permissions.canMarkAsFalsePositive,
|
||||
};
|
||||
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
|
||||
const isApprover = this._permissionsService.isApprover(this._state.dossier());
|
||||
|
||||
const result: RemoveRedactionResult = await this._iqserDialog
|
||||
.openDefault(RemoveRedactionDialogComponent, {
|
||||
@ -133,24 +137,24 @@ export class AnnotationActionsService {
|
||||
dossier: this._state.dossier(),
|
||||
falsePositiveContext: this._getFalsePositiveText(redaction),
|
||||
permissions: removePermissions,
|
||||
applyToAllDossiers: isApprover ? dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault : false,
|
||||
isApprover,
|
||||
},
|
||||
})
|
||||
.result();
|
||||
|
||||
if (result) {
|
||||
if (result.option.value === RemoveRedactionOptions.FALSE_POSITIVE) {
|
||||
this.#setAsFalsePositive(redaction, result.comment);
|
||||
this.#setAsFalsePositive(redaction, result);
|
||||
} else {
|
||||
const removeFromDictionary = result.option.value === RemoveRedactionOptions.IN_DOSSIER;
|
||||
this.#removeRedaction(redaction, result.comment, removeFromDictionary);
|
||||
this.#removeRedaction(redaction, result);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.option.extraOption) {
|
||||
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
|
||||
const { ...body } = dossierTemplate;
|
||||
body.applyDictionaryUpdatesToAllDossiersByDefault = result.applyToAllDossiers;
|
||||
await this._dossierTemplatesService.createOrUpdate(body);
|
||||
if (isApprover && result.option.extraOption) {
|
||||
const { ...body } = dossierTemplate;
|
||||
body.applyDictionaryUpdatesToAllDossiersByDefault = result.option.extraOption.checked;
|
||||
await this._dossierTemplatesService.createOrUpdate(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,33 +384,36 @@ export class AnnotationActionsService {
|
||||
return words;
|
||||
}
|
||||
|
||||
#setAsFalsePositive(redaction: AnnotationWrapper, comment: string) {
|
||||
#setAsFalsePositive(redaction: AnnotationWrapper, dialogResult: RemoveRedactionResult) {
|
||||
const request = {
|
||||
sourceId: redaction.id,
|
||||
value: this._getFalsePositiveText(redaction),
|
||||
type: redaction.type,
|
||||
positions: redaction.positions,
|
||||
addToDictionary: true,
|
||||
addToAllDossiers: !!dialogResult.option.extraOption?.checked,
|
||||
reason: 'False Positive',
|
||||
dictionaryEntryType: redaction.isRecommendation
|
||||
? DictionaryEntryTypes.FALSE_RECOMMENDATION
|
||||
: DictionaryEntryTypes.FALSE_POSITIVE,
|
||||
comment: comment ? { text: comment } : null,
|
||||
comment: dialogResult.comment ? { text: dialogResult.comment } : null,
|
||||
};
|
||||
const { dossierId, fileId } = this._state;
|
||||
|
||||
this.#processObsAndEmit(this._manualRedactionService.addAnnotation([request], dossierId, fileId)).then();
|
||||
}
|
||||
|
||||
#removeRedaction(redaction: AnnotationWrapper, comment: string, removeFromDictionary: boolean) {
|
||||
#removeRedaction(redaction: AnnotationWrapper, dialogResult: RemoveRedactionResult) {
|
||||
const removeFromDictionary = dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER;
|
||||
const body = {
|
||||
annotationId: redaction.id,
|
||||
comment: comment,
|
||||
comment: dialogResult.comment,
|
||||
removeFromDictionary,
|
||||
removeFromAllDossiers: !!dialogResult.option.extraOption?.checked,
|
||||
};
|
||||
const { dossierId, fileId } = this._state;
|
||||
this.#processObsAndEmit(
|
||||
this._manualRedactionService.removeOrSuggestRemove([body], dossierId, fileId, removeFromDictionary, redaction.isHint),
|
||||
this._manualRedactionService.removeRedaction([body], dossierId, fileId, removeFromDictionary, redaction.isHint),
|
||||
).then();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { SuperTypeSorter } from '../../../utils';
|
||||
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui';
|
||||
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { annotationDefaultColorConfig } from '@red/domain';
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { EntitiesService, FilterService, ListingService, SearchService, SortingService } from '@iqser/common-ui';
|
||||
import { EntitiesService, ListingService, SearchService } from '@iqser/common-ui';
|
||||
import { filter, tap } from 'rxjs/operators';
|
||||
import { MultiSelectService } from './multi-select.service';
|
||||
import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
||||
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
||||
|
||||
@Injectable()
|
||||
export class AnnotationsListingService extends ListingService<AnnotationWrapper> implements OnDestroy {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { shareDistinctLast } from '@iqser/common-ui';
|
||||
import { shareDistinctLast } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
@Injectable()
|
||||
export class CommentingService {
|
||||
|
||||
@ -3,7 +3,7 @@ import { firstValueFrom, from, merge, Observable, of, pairwise, Subject, switchM
|
||||
import { Dictionary, Dossier, DOSSIER_ID, DOSSIER_TEMPLATE_ID, File, FILE_ID } from '@red/domain';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { getParam, LoadingService, wipeCache } from '@iqser/common-ui';
|
||||
import { LoadingService, wipeCache } from '@iqser/common-ui';
|
||||
import { filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { FileManagementService } from '@services/files/file-management.service';
|
||||
import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
|
||||
@ -13,8 +13,10 @@ import { HttpEvent, HttpEventType, HttpProgressEvent, HttpResponse } from '@angu
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ViewModeService } from './view-mode.service';
|
||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
const ONE_MEGABYTE = 1024 * 1024;
|
||||
|
||||
@ -31,6 +33,7 @@ function isDownload(event: HttpEvent<Blob>): event is HttpProgressEvent {
|
||||
|
||||
@Injectable()
|
||||
export class FilePreviewStateService {
|
||||
readonly #reloadBlob$ = new Subject();
|
||||
readonly file$: Observable<File>;
|
||||
readonly file: Signal<File>;
|
||||
readonly dossier: Signal<Dossier>;
|
||||
@ -38,18 +41,9 @@ export class FilePreviewStateService {
|
||||
readonly isWritable: Signal<boolean>;
|
||||
readonly dossierDictionary: Signal<Dictionary>;
|
||||
readonly blob$: Observable<Blob>;
|
||||
|
||||
readonly dossierId = getParam(DOSSIER_ID);
|
||||
readonly dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly fileId = getParam(FILE_ID);
|
||||
readonly #reloadBlob$ = new Subject();
|
||||
readonly #dossierFileChange: Signal<boolean>;
|
||||
|
||||
// readonly #routeKey = getReusableRouteKey(inject(ActivatedRoute).snapshot);
|
||||
// readonly isAttached = inject(CustomRouteReuseStrategy).attached$.pipe(
|
||||
// map(route => getReusableRouteKey(route) === this.#routeKey),
|
||||
// startWith(true),
|
||||
// );
|
||||
|
||||
constructor(
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
@ -60,28 +54,27 @@ export class FilePreviewStateService {
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
private readonly _logger: NGXLogger,
|
||||
) {
|
||||
this.dossier = toSignal(dossiersServiceResolver().getEntityChanged$(this.dossierId));
|
||||
this.file$ = inject(FilesMapService).watch$(this.dossierId, this.fileId);
|
||||
this.file = toSignal(this.file$);
|
||||
// this.file$ = combineLatest([this.isAttached, file$]).pipe(
|
||||
// filter(([isAttached]) => isAttached),
|
||||
// map(([, file]) => file),
|
||||
// log('file$'),
|
||||
// shareDistinctLast(),
|
||||
// );
|
||||
this.isWritable = computed(() => this._permissionsService.canPerformAnnotationActions(this.file(), this.dossier()));
|
||||
this.isWritable = computed(() => {
|
||||
const isWritable = this._permissionsService.canPerformAnnotationActions(this.file(), this.dossier());
|
||||
this._logger.info('[FILE] Is writeable:', isWritable);
|
||||
return isWritable;
|
||||
});
|
||||
this.isReadonly = computed(() => !this.isWritable());
|
||||
|
||||
this.blob$ = this.#blob$;
|
||||
|
||||
this.dossierDictionary = toSignal(inject(DossierDictionariesMapService).watch$(this.dossierId, 'dossier_redaction'));
|
||||
this.#dossierFileChange = toSignal(this.#dossierFilesChange$);
|
||||
effect(() => {
|
||||
if (this.#dossierFileChange()) {
|
||||
this._filesService.loadAll(this.dossierId);
|
||||
}
|
||||
});
|
||||
this.#dossierFilesChange$
|
||||
.pipe(
|
||||
switchMap(() => this._filesService.loadAll(this.dossierId)),
|
||||
takeUntilDestroyed(),
|
||||
)
|
||||
.subscribe();
|
||||
effect(
|
||||
() => {
|
||||
if (this._viewModeService.isEarmarks() && !this.file().hasHighlights) {
|
||||
|
||||
@ -11,7 +11,7 @@ import type {
|
||||
ManualRedactionActions,
|
||||
} from '@red/domain';
|
||||
import { type AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { GenericService, IqserPermissionsService, List, Toaster } from '@iqser/common-ui';
|
||||
import { GenericService, IqserPermissionsService, Toaster } from '@iqser/common-ui';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
|
||||
@ -22,6 +22,7 @@ import { type ManualRedactionEntryType } from '@models/file/manual-redaction-ent
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { Roles } from '@users/roles';
|
||||
import { firstValueFrom, of } from 'rxjs';
|
||||
import { List } from '@iqser/common-ui/lib/utils';
|
||||
|
||||
function getResponseType(error: boolean, isConflict: boolean) {
|
||||
const isConflictError = isConflict ? 'conflictError' : 'error';
|
||||
@ -96,15 +97,10 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
addAnnotation(requests: List<IAddRedactionRequest>, dossierId: string, fileId: string, dictionaryLabel?: string) {
|
||||
const toast = requests[0].addToDictionary ? this.#showAddToDictionaryToast(requests, dictionaryLabel) : this.#showToast('add');
|
||||
const canAddRedaction = this._iqaerPermissionsService.has(Roles.redactions.write);
|
||||
if (canAddRedaction && this._permissionsService.isApprover(this.#dossier(dossierId))) {
|
||||
if (canAddRedaction) {
|
||||
return this.add(requests, dossierId, fileId).pipe(toast);
|
||||
}
|
||||
|
||||
const canRequestRedaction = this._iqaerPermissionsService.has(Roles.redactions.request);
|
||||
if (canRequestRedaction) {
|
||||
return this.requestAdd(requests, dossierId, fileId).pipe(toast);
|
||||
}
|
||||
|
||||
return of(undefined);
|
||||
}
|
||||
|
||||
@ -140,20 +136,8 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
return this.requestResize(requests, dossierId, fileId);
|
||||
}
|
||||
|
||||
removeOrSuggestRemove(
|
||||
body: List<IRemoveRedactionRequest>,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
removeFromDictionary = false,
|
||||
isHint = false,
|
||||
) {
|
||||
if (this._permissionsService.isApprover(this.#dossier(dossierId))) {
|
||||
return this.remove(body, dossierId, fileId).pipe(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary));
|
||||
}
|
||||
|
||||
return this.requestRemoveRedaction(body, dossierId, fileId).pipe(
|
||||
this.#showToast(!isHint ? 'request-remove' : 'request-remove-hint', removeFromDictionary),
|
||||
);
|
||||
removeRedaction(body: List<IRemoveRedactionRequest>, dossierId: string, fileId: string, removeFromDictionary = false, isHint = false) {
|
||||
return this.remove(body, dossierId, fileId).pipe(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary));
|
||||
}
|
||||
|
||||
getTitle(type: ManualRedactionEntryType) {
|
||||
@ -193,10 +177,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
);
|
||||
}
|
||||
|
||||
requestRemoveRedaction(body: List<IRemoveRedactionRequest>, dossierId: string, fileId: string) {
|
||||
return this._post(body, `${this.#bulkRequest}/remove/${dossierId}/${fileId}`).pipe(this.#log('Request remove', body));
|
||||
}
|
||||
|
||||
approve(annotationIds: List, dossierId: string, fileId: string) {
|
||||
return this._post(annotationIds, `${this._defaultModelPath}/bulk/approve/${dossierId}/${fileId}`).pipe(
|
||||
this.#log('Approve', annotationIds),
|
||||
@ -219,10 +199,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
return this._post(body, `${this.#bulkRedaction}/remove/${dossierId}/${fileId}`).pipe(this.#log('Remove', body));
|
||||
}
|
||||
|
||||
requestAdd(body: List<IAddRedactionRequest>, dossierId: string, fileId: string) {
|
||||
return this._post(body, `${this.#bulkRequest}/add/${dossierId}/${fileId}`).pipe(this.#log('Request add', body));
|
||||
}
|
||||
|
||||
forceRedaction(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string) {
|
||||
return this._post(body, `${this.#bulkRedaction}/force/${dossierId}/${fileId}`).pipe(this.#log('Force redaction', body));
|
||||
}
|
||||
|
||||
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