Compare commits

..

9 Commits

Author SHA1 Message Date
Nicoleta Panaghiu
953c0c65cc RED-10139: removed some more getters. 2024-11-05 13:05:40 +02:00
Nicoleta Panaghiu
7ef5fc7bc9 Merge branch 'master' into RED-10139 2024-11-04 11:01:33 +02:00
Nicoleta Panaghiu
a2870531c9 RED-10139: refactoring dossier listing. 2024-10-21 17:15:53 +03:00
Nicoleta Panaghiu
b7ff80ecac RED-10139: removed getters from template and component refactoring.
for dossier overview.
2024-10-17 18:02:05 +03:00
Nicoleta Panaghiu
4bf2e79c57 Merge remote-tracking branch 'origin/master' into RED-10139 2024-10-17 18:01:00 +03:00
Nicoleta Panaghiu
4945b6a9f2 Merge branch 'master' into RED-10139 2024-10-17 12:16:00 +03:00
Nicoleta Panaghiu
03804df3b9 Merge branch 'master' into RED-10139 2024-10-16 15:49:35 +03:00
Nicoleta Panaghiu
57d3c008cc RED-10139: other getters to computed. 2024-10-16 13:49:34 +03:00
Nicoleta Panaghiu
f00a92a5ea RED-10139: rid file-preview module of anything resembling getters. 2024-10-16 13:46:19 +03:00
386 changed files with 8016 additions and 6870 deletions

View File

@ -16,7 +16,6 @@
}, },
"rules": { "rules": {
"rxjs/no-ignored-subscription": "warn", "rxjs/no-ignored-subscription": "warn",
"@angular-eslint/prefer-standalone": "off",
"@angular-eslint/no-conflicting-lifecycle": "error", "@angular-eslint/no-conflicting-lifecycle": "error",
"@angular-eslint/no-host-metadata-property": "error", "@angular-eslint/no-host-metadata-property": "error",
"@angular-eslint/no-input-rename": "error", "@angular-eslint/no-input-rename": "error",

View File

@ -4,60 +4,41 @@ variables:
PROJECT: red-ui PROJECT: red-ui
DOCKERFILELOCATION: 'docker/$PROJECT/Dockerfile' DOCKERFILELOCATION: 'docker/$PROJECT/Dockerfile'
include: include:
- project: 'gitlab/gitlab' - project: 'gitlab/gitlab'
ref: 'main' ref: 'main'
file: 'ci-templates/docker_build_nexus_v2.yml' file: 'ci-templates/docker_build_nexus_v2.yml'
rules: rules:
- if: $CI_PIPELINE_SOURCE != "schedule" - if: $CI_PIPELINE_SOURCE != "schedule"
sonarqube:
stage: test
image:
name: sonarsource/sonar-scanner-cli:11.1
entrypoint:
- ''
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: '0'
cache:
key: "${CI_JOB_NAME}"
paths:
- ".sonar/cache"
script:
- sonar-scanner
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
- if: "$CI_COMMIT_BRANCH =~ /^release/"
localazy update: localazy update:
image: node:20.5 image: node:20.5
cache: cache:
- key: - key:
files: files:
- yarn.lock - yarn.lock
paths: paths:
- .yarn-cache/ - .yarn-cache/
script: script:
# - git config user.email "${CI_EMAIL}" # - git config user.email "${CI_EMAIL}"
# - git config user.name "${CI_USERNAME}" # - git config user.name "${CI_USERNAME}"
# - git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git # - git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
- git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git - git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
- cd tools/localazy - cd tools/localazy
- yarn install --cache-folder .yarn-cache - yarn install --cache-folder .yarn-cache
- yarn start - yarn start
- cd ../.. - cd ../..
- git add . - git add .
- |- - |-
CHANGES=$(git status --porcelain | wc -l) CHANGES=$(git status --porcelain | wc -l)
if [ "$CHANGES" -gt "0" ] if [ "$CHANGES" -gt "0" ]
then then
git status git status
git commit -m "push back localazy update" git commit -m "push back localazy update"
git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME} git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME}
# git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git # git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
# git push # git push
fi fi
rules: rules:
- if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_PIPELINE_SOURCE == "schedule"

View File

@ -50,7 +50,7 @@
{ {
"glob": "**/*", "glob": "**/*",
"input": "node_modules/@pdftron/webviewer/public/", "input": "node_modules/@pdftron/webviewer/public/",
"output": "/assets/wv-resources/11.1.0/" "output": "/assets/wv-resources/11.0.0/"
}, },
{ {
"glob": "**/*", "glob": "**/*",

View File

@ -4,27 +4,26 @@ import { UserPreferenceService } from '@users/user-preference.service';
import { getConfig } from '@iqser/common-ui'; import { getConfig } from '@iqser/common-ui';
import { AppConfig } from '@red/domain'; import { AppConfig } from '@red/domain';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { filter, map, switchMap, take, tap } from 'rxjs/operators'; import { filter, map, switchMap, take } from 'rxjs/operators';
import { APP_TYPE_PATHS } from '@common-ui/utils/constants';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { TenantsService } from '@common-ui/tenants';
export function loadCustomTheme(cssFileName: string) { function loadCustomTheme() {
const head = document.getElementsByTagName('head')[0]; const cssFileName = getConfig<AppConfig>().THEME;
const link = document.createElement('link');
link.id = cssFileName; if (cssFileName) {
link.rel = 'stylesheet'; const head = document.getElementsByTagName('head')[0];
link.type = 'text/css'; const link = document.createElement('link');
link.href = 'assets/styles/themes/' + cssFileName + '.css'; link.id = cssFileName;
link.media = 'all'; link.rel = 'stylesheet';
head.appendChild(link); link.type = 'text/css';
link.href = 'assets/styles/themes/' + cssFileName + '.css';
link.media = 'all';
head.appendChild(link);
}
} }
@Component({ @Component({
selector: 'redaction-root', selector: 'redaction-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
standalone: false,
}) })
export class AppComponent { export class AppComponent {
constructor( constructor(
@ -35,12 +34,9 @@ export class AppComponent {
userPreferenceService: UserPreferenceService, userPreferenceService: UserPreferenceService,
renderer: Renderer2, renderer: Renderer2,
private readonly _router: Router, private readonly _router: Router,
private readonly _iconRegistry: MatIconRegistry,
private readonly _sanitizer: DomSanitizer,
private readonly _tenantsService: TenantsService,
) { ) {
const config = getConfig<AppConfig>();
renderer.addClass(document.body, userPreferenceService.getTheme()); renderer.addClass(document.body, userPreferenceService.getTheme());
loadCustomTheme();
const removeQueryParams = _router.events.pipe( const removeQueryParams = _router.events.pipe(
filter((event): event is NavigationEnd => event instanceof NavigationEnd), filter((event): event is NavigationEnd => event instanceof NavigationEnd),
@ -51,25 +47,9 @@ export class AppComponent {
); );
removeQueryParams.subscribe(); removeQueryParams.subscribe();
this._tenantsService if (getConfig().IS_DOCUMINE) {
.waitForSettingTenant() document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
.pipe( }
tap(() => {
const isDocumine = this._tenantsService.activeTenant.documine;
const logo = isDocumine ? 'documine' : 'redaction';
_iconRegistry.addSvgIconInNamespace(
'iqser',
'logo',
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${logo}-logo.svg`),
);
if (isDocumine) {
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
}
loadCustomTheme(isDocumine ? APP_TYPE_PATHS.SCM : APP_TYPE_PATHS.REDACT);
}),
take(1),
)
.subscribe();
} }
#removeKeycloakQueryParams() { #removeKeycloakQueryParams() {

View File

@ -1,6 +1,6 @@
import { APP_BASE_HREF, DatePipe as BaseDatePipe } from '@angular/common'; import { APP_BASE_HREF, DatePipe as BaseDatePipe } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorHandler, inject, NgModule, provideEnvironmentInitializer } from '@angular/core'; import { ENVIRONMENT_INITIALIZER, ErrorHandler, inject, NgModule } from '@angular/core';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
import { MatMenu, MatMenuContent, MatMenuItem, MatMenuTrigger } from '@angular/material/menu'; import { MatMenu, MatMenuContent, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
@ -117,7 +117,7 @@ export const appModuleFactory = (config: AppConfig) => {
resetTimeoutOnDuplicate: true, resetTimeoutOnDuplicate: true,
}), }),
TenantsModule.forRoot(), TenantsModule.forRoot(),
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY }), IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY || '/assets/i18n/redact/' }),
IqserLoadingModule.forRoot(), IqserLoadingModule.forRoot(),
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
LoggerModule.forRoot(undefined, { LoggerModule.forRoot(undefined, {
@ -135,20 +135,20 @@ export const appModuleFactory = (config: AppConfig) => {
features: { features: {
ANNOTATIONS: { ANNOTATIONS: {
color: 'aqua', color: 'aqua',
enabled: false, enabled: true,
level: NgxLoggerLevel.DEBUG, level: NgxLoggerLevel.DEBUG,
}, },
FILTERS: { FILTERS: {
enabled: false, enabled: false,
}, },
TENANTS: { TENANTS: {
enabled: false, enabled: true,
}, },
ROUTES: { ROUTES: {
enabled: false, enabled: true,
}, },
PDF: { PDF: {
enabled: false, enabled: true,
}, },
FILE: { FILE: {
enabled: false, enabled: false,
@ -171,9 +171,6 @@ export const appModuleFactory = (config: AppConfig) => {
DOSSIERS_CHANGES: { DOSSIERS_CHANGES: {
enabled: false, enabled: false,
}, },
GUARDS: {
enabled: false,
},
}, },
} as ILoggerConfig, } as ILoggerConfig,
}, },
@ -233,10 +230,14 @@ export const appModuleFactory = (config: AppConfig) => {
provide: ErrorHandler, provide: ErrorHandler,
useClass: GlobalErrorHandler, useClass: GlobalErrorHandler,
}, },
provideEnvironmentInitializer(async () => { {
const languageService = inject(LanguageService); provide: ENVIRONMENT_INITIALIZER,
return languageService.setInitialLanguage(); multi: true,
}), useValue: async () => {
const languageService = inject(LanguageService);
return languageService.setInitialLanguage();
},
},
{ {
provide: MAX_RETRIES_ON_SERVER_ERROR, provide: MAX_RETRIES_ON_SERVER_ERROR,
useFactory: () => config.MAX_RETRIES_ON_SERVER_ERROR, useFactory: () => config.MAX_RETRIES_ON_SERVER_ERROR,
@ -257,7 +258,6 @@ export const appModuleFactory = (config: AppConfig) => {
provide: MAT_TOOLTIP_DEFAULT_OPTIONS, provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
useValue: { useValue: {
disableTooltipInteractivity: true, disableTooltipInteractivity: true,
showDelay: 1,
}, },
}, },
BaseDatePipe, BaseDatePipe,

View File

@ -7,7 +7,6 @@ import { AppConfig } from '@red/domain';
selector: 'redaction-auth-error', selector: 'redaction-auth-error',
templateUrl: './auth-error.component.html', templateUrl: './auth-error.component.html',
styleUrls: ['./auth-error.component.scss'], styleUrls: ['./auth-error.component.scss'],
standalone: false,
}) })
export class AuthErrorComponent { export class AuthErrorComponent {
readonly #config = getConfig<AppConfig>(); readonly #config = getConfig<AppConfig>();

View File

@ -20,7 +20,6 @@ const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
@Component({ @Component({
templateUrl: './base-screen.component.html', templateUrl: './base-screen.component.html',
styleUrls: ['./base-screen.component.scss'], styleUrls: ['./base-screen.component.scss'],
standalone: false,
}) })
export class BaseScreenComponent { export class BaseScreenComponent {
readonly roles = Roles; readonly roles = Roles;

View File

@ -7,7 +7,6 @@ import { Breadcrumb, BreadcrumbDisplayType, BreadcrumbsService } from '@services
selector: 'redaction-breadcrumbs', selector: 'redaction-breadcrumbs',
templateUrl: './breadcrumbs.component.html', templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.scss'], styleUrls: ['./breadcrumbs.component.scss'],
standalone: false,
}) })
export class BreadcrumbsComponent { export class BreadcrumbsComponent {
constructor(readonly breadcrumbsService: BreadcrumbsService) {} constructor(readonly breadcrumbsService: BreadcrumbsService) {}

View File

@ -13,7 +13,6 @@ import { firstValueFrom } from 'rxjs';
entitiesService: FileDownloadService, entitiesService: FileDownloadService,
component: DownloadsListScreenComponent, component: DownloadsListScreenComponent,
}), }),
standalone: false,
}) })
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> implements OnDestroy { export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> implements OnDestroy {
readonly #interval: number; readonly #interval: number;

View File

@ -25,7 +25,6 @@ function chronologically(first: string, second: string) {
selector: 'redaction-notifications', selector: 'redaction-notifications',
templateUrl: './notifications.component.html', templateUrl: './notifications.component.html',
styleUrls: ['./notifications.component.scss'], styleUrls: ['./notifications.component.scss'],
standalone: false,
}) })
export class NotificationsComponent { export class NotificationsComponent {
readonly hasUnreadNotifications$: Observable<boolean>; readonly hasUnreadNotifications$: Observable<boolean>;

View File

@ -5,6 +5,5 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
templateUrl: './dashboard-skeleton.component.html', templateUrl: './dashboard-skeleton.component.html',
styleUrls: ['./dashboard-skeleton.component.scss'], styleUrls: ['./dashboard-skeleton.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}) })
export class DashboardSkeletonComponent {} export class DashboardSkeletonComponent {}

View File

@ -5,7 +5,6 @@ import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } fr
templateUrl: './dossier-skeleton.component.html', templateUrl: './dossier-skeleton.component.html',
styleUrls: ['./dossier-skeleton.component.scss'], styleUrls: ['./dossier-skeleton.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}) })
export class DossierSkeletonComponent implements OnInit { export class DossierSkeletonComponent implements OnInit {
@ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>; @ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>;

View File

@ -5,6 +5,5 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
templateUrl: './skeleton-stats.component.html', templateUrl: './skeleton-stats.component.html',
styleUrls: ['./skeleton-stats.component.scss'], styleUrls: ['./skeleton-stats.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}) })
export class SkeletonStatsComponent {} export class SkeletonStatsComponent {}

View File

@ -6,7 +6,6 @@ import { Title } from '@angular/platform-browser';
templateUrl: './skeleton-top-bar.component.html', templateUrl: './skeleton-top-bar.component.html',
styleUrls: ['./skeleton-top-bar.component.scss'], styleUrls: ['./skeleton-top-bar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}) })
export class SkeletonTopBarComponent { export class SkeletonTopBarComponent {
constructor(readonly titleService: Title) {} constructor(readonly titleService: Title) {}

View File

@ -9,7 +9,6 @@ import { MatMenuTrigger } from '@angular/material/menu';
templateUrl: './spotlight-search.component.html', templateUrl: './spotlight-search.component.html',
styleUrls: ['./spotlight-search.component.scss'], styleUrls: ['./spotlight-search.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
}) })
export class SpotlightSearchComponent { export class SpotlightSearchComponent {
@Input() actions: readonly SpotlightSearchAction[]; @Input() actions: readonly SpotlightSearchAction[];

View File

@ -9,7 +9,6 @@ interface ITenant extends IStoredTenantId {
selector: 'app-tenants-menu', selector: 'app-tenants-menu',
templateUrl: './tenants-menu.component.html', templateUrl: './tenants-menu.component.html',
styleUrls: ['./tenants-menu.component.scss'], styleUrls: ['./tenants-menu.component.scss'],
standalone: false,
}) })
export class TenantsMenuComponent { export class TenantsMenuComponent {
readonly tenantsService = inject(TenantsService); readonly tenantsService = inject(TenantsService);

View File

@ -20,7 +20,6 @@ interface MenuItem {
selector: 'app-user-menu', selector: 'app-user-menu',
templateUrl: './user-menu.component.html', templateUrl: './user-menu.component.html',
styleUrls: ['./user-menu.component.scss'], styleUrls: ['./user-menu.component.scss'],
standalone: false,
}) })
export class UserMenuComponent { export class UserMenuComponent {
readonly currentUser = getCurrentUser<User>(); readonly currentUser = getCurrentUser<User>();

View File

@ -23,14 +23,12 @@ export function templateExistsWhenEnteringAdmin(): CanActivateFn {
const defaultColorsService = inject(DefaultColorsService); const defaultColorsService = inject(DefaultColorsService);
const watermarksService = inject(WatermarkService); const watermarksService = inject(WatermarkService);
const router = inject(Router); const router = inject(Router);
const rulesService = inject(RulesService);
const isDocumine = getConfig().IS_DOCUMINE; const isDocumine = getConfig().IS_DOCUMINE;
const dossierTemplate = inject(DossierTemplateStatsService).get(dossierTemplateId); const dossierTemplate = inject(DossierTemplateStatsService).get(dossierTemplateId);
await firstValueFrom(fileAttributesService.loadFileAttributesConfig(dossierTemplateId)); await firstValueFrom(fileAttributesService.loadFileAttributesConfig(dossierTemplateId));
await firstValueFrom(dictionaryService.loadDictionaryDataForDossierTemplate(dossierTemplateId)); await firstValueFrom(dictionaryService.loadDictionaryDataForDossierTemplate(dossierTemplateId));
await firstValueFrom(defaultColorsService.loadForDossierTemplate(dossierTemplateId)); await firstValueFrom(defaultColorsService.loadForDossierTemplate(dossierTemplateId));
await firstValueFrom(rulesService.getFor(dossierTemplateId));
if (!isDocumine) { if (!isDocumine) {
await firstValueFrom(watermarksService.loadForDossierTemplate(dossierTemplateId)); await firstValueFrom(watermarksService.loadForDossierTemplate(dossierTemplateId));
} }

View File

@ -40,7 +40,8 @@ export function ifLoggedIn(): AsyncGuard {
logger.info('[KEYCLOAK] Keycloak init...'); logger.info('[KEYCLOAK] Keycloak init...');
await keycloakInitializer(tenant); await keycloakInitializer(tenant);
logger.info('[KEYCLOAK] Keycloak init done for tenant: ', { tenant }); logger.info('[KEYCLOAK] Keycloak init done!');
console.log({ tenant });
await tenantsService.selectTenant(tenant); await tenantsService.selectTenant(tenant);
await usersService.initialize(); await usersService.initialize();
await licenseService.loadLicenses(); await licenseService.loadLicenses();
@ -50,7 +51,6 @@ export function ifLoggedIn(): AsyncGuard {
const jwtToken = jwtDecode(token) as JwtToken; const jwtToken = jwtDecode(token) as JwtToken;
const authTime = (jwtToken.auth_time || jwtToken.iat).toString(); const authTime = (jwtToken.auth_time || jwtToken.iat).toString();
localStorage.setItem('authTime', authTime); localStorage.setItem('authTime', authTime);
localStorage.setItem('token', token);
} }
} }

View File

@ -69,5 +69,3 @@ export const canEditHint = (annotation: AnnotationWrapper) =>
((annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint) && !annotation.isImage; ((annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint) && !annotation.isImage;
export const canEditImage = (annotation: AnnotationWrapper) => annotation.isImage; export const canEditImage = (annotation: AnnotationWrapper) => annotation.isImage;
export const canRevertChanges = (annotation: AnnotationWrapper) => annotation.hasRedactionChanges;

View File

@ -17,7 +17,6 @@ import {
canRemoveRedaction, canRemoveRedaction,
canResizeAnnotation, canResizeAnnotation,
canResizeInDictionary, canResizeInDictionary,
canRevertChanges,
canUndo, canUndo,
} from './annotation-permissions.utils'; } from './annotation-permissions.utils';
import { AnnotationWrapper } from './annotation.wrapper'; import { AnnotationWrapper } from './annotation.wrapper';
@ -38,7 +37,6 @@ export class AnnotationPermissions {
canEditAnnotations = true; canEditAnnotations = true;
canEditHints = true; canEditHints = true;
canEditImages = true; canEditImages = true;
canRevertChanges = true;
static forUser( static forUser(
isApprover: boolean, isApprover: boolean,
@ -77,7 +75,6 @@ export class AnnotationPermissions {
permissions.canEditAnnotations = canEditAnnotation(annotation); permissions.canEditAnnotations = canEditAnnotation(annotation);
permissions.canEditHints = canEditHint(annotation); permissions.canEditHints = canEditHint(annotation);
permissions.canEditImages = canEditImage(annotation); permissions.canEditImages = canEditImage(annotation);
permissions.canRevertChanges = canRevertChanges(annotation);
summedPermissions._merge(permissions); summedPermissions._merge(permissions);
} }
return summedPermissions; return summedPermissions;
@ -100,7 +97,6 @@ export class AnnotationPermissions {
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true); result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true); result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true);
result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true); result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true);
result.canRevertChanges = permissions.reduce((acc, next) => acc && next.canRevertChanges, true);
return result; return result;
} }

View File

@ -40,7 +40,7 @@ export class AnnotationWrapper implements IListable {
typeLabel?: string; typeLabel?: string;
color: string; color: string;
numberOfComments = 0; numberOfComments = 0;
firstBottomLeftPoint: IPoint; firstTopLeftPoint: IPoint;
shortContent: string; shortContent: string;
content: AnnotationContent; content: AnnotationContent;
value: string; value: string;
@ -84,10 +84,7 @@ export class AnnotationWrapper implements IListable {
} }
get isRedactedImageHint() { get isRedactedImageHint() {
return ( return this.IMAGE_HINT && this.superType === SuperTypes.Redaction;
(this.IMAGE_HINT && this.superType === SuperTypes.Redaction) ||
(this.IMAGE_HINT && this.superType === SuperTypes.ManualRedaction)
);
} }
get isSkippedImageHint() { get isSkippedImageHint() {
@ -199,11 +196,11 @@ export class AnnotationWrapper implements IListable {
} }
get x() { get x() {
return this.firstBottomLeftPoint.x; return this.firstTopLeftPoint.x;
} }
get y() { get y() {
return this.firstBottomLeftPoint.y; return this.firstTopLeftPoint.y;
} }
get legalBasis() { get legalBasis() {
@ -228,7 +225,7 @@ export class AnnotationWrapper implements IListable {
annotationWrapper.value = 'Imported'; annotationWrapper.value = 'Imported';
annotationWrapper.color = earmark.hexColor; annotationWrapper.color = earmark.hexColor;
annotationWrapper.positions = earmark.positions; annotationWrapper.positions = earmark.positions;
annotationWrapper.firstBottomLeftPoint = earmark.positions[0]?.topLeft; annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType]; annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
return annotationWrapper; return annotationWrapper;
@ -249,7 +246,7 @@ export class AnnotationWrapper implements IListable {
annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType; annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType;
annotationWrapper.type = logEntry.type; annotationWrapper.type = logEntry.type;
annotationWrapper.value = logEntry.value; annotationWrapper.value = logEntry.value;
annotationWrapper.firstBottomLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] }; annotationWrapper.firstTopLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber; annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
annotationWrapper.positions = logEntry.positions.map(p => ({ annotationWrapper.positions = logEntry.positions.map(p => ({
page: p.pageNumber, page: p.pageNumber,
@ -378,7 +375,7 @@ export class AnnotationWrapper implements IListable {
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) { static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
if (annotationWrapper.legalBasis) { if (annotationWrapper.legalBasis) {
const lb = legalBasisList.find(lbm => lbm.technicalName?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase())); const lb = legalBasisList.find(lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
if (lb) { if (lb) {
return lb.name; return lb.name;
} }

View File

@ -1,14 +1,15 @@
<iqser-side-nav [title]="'account-settings' | translate"> <iqser-side-nav [title]="'account-settings' | translate">
<ng-container *ngFor="let item of items"> @for (item of items; track item.helpModeKey) {
<div @if (item.show) {
*ngIf="item.show" <div
[routerLinkActiveOptions]="{ exact: false }" [routerLinkActiveOptions]="{ exact: false }"
[routerLink]="'../' + item.screen" [routerLink]="'../' + item.screen"
[attr.help-mode-key]="item.helpModeKey" [attr.help-mode-key]="item.helpModeKey"
class="item" class="item"
routerLinkActive="active" routerLinkActive="active"
> >
{{ item.label | translate }} {{ item.label | translate }}
</div> </div>
</ng-container> }
}
</iqser-side-nav> </iqser-side-nav>

View File

@ -6,7 +6,6 @@ import { User } from '@red/domain';
import { getCurrentUser } from '@iqser/common-ui/lib/users'; import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { SideNavComponent } from '@common-ui/shared'; import { SideNavComponent } from '@common-ui/shared';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { NgForOf, NgIf } from '@angular/common';
import { RouterLink, RouterLinkActive } from '@angular/router'; import { RouterLink, RouterLinkActive } from '@angular/router';
interface NavItem { interface NavItem {
@ -21,7 +20,8 @@ interface NavItem {
templateUrl: './account-side-nav.component.html', templateUrl: './account-side-nav.component.html',
styleUrls: ['./account-side-nav.component.scss'], styleUrls: ['./account-side-nav.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [SideNavComponent, TranslateModule, NgForOf, NgIf, RouterLinkActive, RouterLink], standalone: true,
imports: [SideNavComponent, TranslateModule, RouterLinkActive, RouterLink],
}) })
export class AccountSideNavComponent { export class AccountSideNavComponent {
readonly currentUser = getCurrentUser<User>(); readonly currentUser = getCurrentUser<User>();

View File

@ -13,6 +13,7 @@
<div class="heading-l" [translate]="translations[path]"></div> <div class="heading-l" [translate]="translations[path]"></div>
</div> </div>
} }
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>
</div> </div>

View File

@ -10,6 +10,7 @@ import { TranslateModule } from '@ngx-translate/core';
templateUrl: './base-account-screen-component.html', templateUrl: './base-account-screen-component.html',
styleUrls: ['./base-account-screen-component.scss'], styleUrls: ['./base-account-screen-component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgClass, RouterOutlet, AccountSideNavComponent, TranslateModule], imports: [NgClass, RouterOutlet, AccountSideNavComponent, TranslateModule],
}) })
export class BaseAccountScreenComponent implements OnInit { export class BaseAccountScreenComponent implements OnInit {

View File

@ -1,6 +1,6 @@
<form (submit)="save()" [formGroup]="form"> <form (submit)="save()" [formGroup]="form">
<div class="dialog-content"> <div class="dialog-content">
<div *ngFor="let category of notificationCategories"> @for (category of notificationCategories; track category) {
<div class="iqser-input-group header w-full"> <div class="iqser-input-group header w-full">
<mat-slide-toggle color="primary" formControlName="{{ category }}Enabled" <mat-slide-toggle color="primary" formControlName="{{ category }}Enabled"
>{{ translations[category] | translate }} >{{ translations[category] | translate }}
@ -8,26 +8,31 @@
</div> </div>
<!-- TODO: This lots of getters--> <!-- TODO: This lots of getters-->
<div *ngIf="isCategoryActive(category)" class="options-content"> @if (isCategoryActive(category)) {
<div [translate]="'notifications-screen.options-title'" class="statement"></div> <div class="options-content">
<div [translate]="'notifications-screen.options-title'" class="statement"></div>
<div *ngFor="let key of notificationGroupsKeys; let i = index" class="group"> @for (key of notificationGroupsKeys; track key) {
<div [translate]="translations[key]" class="group-title"></div> <div class="group">
<div class="iqser-input-group"> <div [translate]="translations[key]" class="group-title"></div>
<ng-container *ngFor="let preference of getRssFilteredSettings(notificationGroupsValues[i])"> <div class="iqser-input-group">
<mat-checkbox @for (preference of getRssFilteredSettings(notificationGroupsValues[$index]); track preference) {
(change)="addRemovePreference($event.checked, category, preference)" @if (!skipPreference(preference)) {
*ngIf="!skipPreference(preference)" <mat-checkbox
[checked]="isPreferenceChecked(category, preference)" (change)="addRemovePreference($event.checked, category, preference)"
color="primary" [checked]="isPreferenceChecked(category, preference)"
> color="primary"
{{ translations[preference] | translate }} >
</mat-checkbox> {{ translations[preference] | translate }}
</ng-container> </mat-checkbox>
</div> }
}
</div>
</div>
}
</div> </div>
</div> }
</div> }
</div> </div>
<div class="dialog-actions"> <div class="dialog-actions">

View File

@ -14,7 +14,6 @@ import {
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations'; import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
import { getCurrentUser } from '@iqser/common-ui/lib/users'; import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { NgForOf, NgIf } from '@angular/common';
import { MatSlideToggle } from '@angular/material/slide-toggle'; import { MatSlideToggle } from '@angular/material/slide-toggle';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
@ -25,7 +24,8 @@ const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_RE
templateUrl: './notifications-screen.component.html', templateUrl: './notifications-screen.component.html',
styleUrls: ['./notifications-screen.component.scss'], styleUrls: ['./notifications-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, NgForOf, MatSlideToggle, TranslateModule, NgIf, MatCheckbox, IconButtonComponent], standalone: true,
imports: [ReactiveFormsModule, MatSlideToggle, TranslateModule, MatCheckbox, IconButtonComponent],
}) })
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit { export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
readonly #toaster = inject(Toaster); readonly #toaster = inject(Toaster);

View File

@ -12,7 +12,7 @@
}}</mat-option> }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox *ngIf="displayExtraOptionAddRedaction" formControlName="addRedactionApplyToAll">{{ <mat-checkbox *ngIf="displayExtraOptionAddRedaction()" formControlName="addRedactionApplyToAll">{{
'dialog-defaults-form.extra-option-label' | translate 'dialog-defaults-form.extra-option-label' | translate
}}</mat-checkbox> }}</mat-checkbox>
</div> </div>
@ -25,7 +25,7 @@
}}</mat-option> }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox *ngIf="displayExtraOptionRemoveRedaction" formControlName="removeRedactionApplyToAll">{{ <mat-checkbox *ngIf="displayExtraOptionRemoveRedaction()" formControlName="removeRedactionApplyToAll">{{
'dialog-defaults-form.extra-option-label' | translate 'dialog-defaults-form.extra-option-label' | translate
}}</mat-checkbox> }}</mat-checkbox>
</div> </div>
@ -39,7 +39,7 @@
}}</mat-option> }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox *ngIf="displayExtraOptionRemoveRecommendation" formControlName="removeRecommendationApplyToAll">{{ <mat-checkbox *ngIf="displayExtraOptionRemoveRecommendation()" formControlName="removeRecommendationApplyToAll">{{
'dialog-defaults-form.extra-option-label' | translate 'dialog-defaults-form.extra-option-label' | translate
}}</mat-checkbox> }}</mat-checkbox>
</div> </div>
@ -51,7 +51,7 @@
<mat-option *ngFor="let option of hintAddOptions" [value]="option.value">{{ option.label | translate }}</mat-option> <mat-option *ngFor="let option of hintAddOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox *ngIf="displayExtraOptionAddHint" formControlName="addHintApplyToAll">{{ <mat-checkbox *ngIf="displayExtraOptionAddHint()" formControlName="addHintApplyToAll">{{
'dialog-defaults-form.extra-option-label' | translate 'dialog-defaults-form.extra-option-label' | translate
}}</mat-checkbox> }}</mat-checkbox>
</div> </div>
@ -62,7 +62,7 @@
<mat-option *ngFor="let option of removeOptions" [value]="option.value">{{ option.label | translate }}</mat-option> <mat-option *ngFor="let option of removeOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox *ngIf="displayExtraOptionRemoveHint" formControlName="removeHintApplyToAll">{{ <mat-checkbox *ngIf="displayExtraOptionRemoveHint()" formControlName="removeHintApplyToAll">{{
'dialog-defaults-form.extra-option-label' | translate 'dialog-defaults-form.extra-option-label' | translate
}}</mat-checkbox> }}</mat-checkbox>
</div> </div>

View File

@ -1,11 +1,11 @@
import { NgForOf, NgIf } from '@angular/common'; import { NgForOf, NgIf } from '@angular/common';
import { ChangeDetectorRef, Component, inject } from '@angular/core'; import { ChangeDetectorRef, Component, computed, inject } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
import { MatFormField } from '@angular/material/form-field'; import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect } from '@angular/material/select'; import { MatOption, MatSelect } from '@angular/material/select';
import { BaseFormComponent } from '@common-ui/form'; import { BaseFormComponent } from '@common-ui/form';
import { AsControl } from '@common-ui/utils'; import { AsControl, formValueToSignal } from '@common-ui/utils';
import { IconButtonComponent } from '@iqser/common-ui'; import { IconButtonComponent } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service'; import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
@ -41,63 +41,60 @@ interface DefaultOptionsForm {
selector: 'redaction-dialog-defaults', selector: 'redaction-dialog-defaults',
templateUrl: './dialog-defaults.component.html', templateUrl: './dialog-defaults.component.html',
styleUrl: './dialog-defaults.component.scss', styleUrl: './dialog-defaults.component.scss',
standalone: true,
imports: [ReactiveFormsModule, TranslateModule, MatFormField, MatSelect, MatOption, NgForOf, MatCheckbox, NgIf, IconButtonComponent], imports: [ReactiveFormsModule, TranslateModule, MatFormField, MatSelect, MatOption, NgForOf, MatCheckbox, NgIf, IconButtonComponent],
}) })
export class DialogDefaultsComponent extends BaseFormComponent { export class DialogDefaultsComponent extends BaseFormComponent {
readonly #formBuilder = inject(FormBuilder); readonly #formBuilder = inject(FormBuilder);
readonly #userPreferences = inject(UserPreferenceService); readonly #userPreferences = inject(UserPreferenceService);
readonly #changeDetectorRef = inject(ChangeDetectorRef); readonly #changeDetectorRef = inject(ChangeDetectorRef);
form: FormGroup<AsControl<DefaultOptionsForm>> = this.#formBuilder.group({
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
addHint: this.#userPreferences.getAddHintDefaultOption(),
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
});
initialFormValue = this.form.getRawValue();
readonly redactionAddOptions = redactionAddOptions; readonly redactionAddOptions = redactionAddOptions;
readonly hintAddOptions = hintAddOptions; readonly hintAddOptions = hintAddOptions;
readonly removeOptions = removeOptions; readonly removeOptions = removeOptions;
readonly redactionRemoveOptions = redactionRemoveOptions; readonly redactionRemoveOptions = redactionRemoveOptions;
readonly recommendationRemoveOptions = recommendationRemoveOptions; readonly recommendationRemoveOptions = recommendationRemoveOptions;
constructor() { form: FormGroup<AsControl<DefaultOptionsForm>> = this.#formBuilder.group(this.#initialFormValue);
super(); initialFormValue = this.form.getRawValue();
}
get displayExtraOptionAddRedaction() { readonly formToSignalMap = {
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addRedaction.value; addRedaction: formValueToSignal<DefaultOptionsForm['addRedaction']>(this.form.controls.addRedaction),
} addHint: formValueToSignal<DefaultOptionsForm['addHint']>(this.form.controls.addHint),
removeRedaction: formValueToSignal<DefaultOptionsForm['removeRedaction']>(this.form.controls.removeRedaction),
removeHint: formValueToSignal<DefaultOptionsForm['removeHint']>(this.form.controls.removeHint),
removeRecommendation: formValueToSignal<DefaultOptionsForm['removeRecommendation']>(this.form.controls.removeRecommendation),
};
get displayExtraOptionAddHint() { readonly displayExtraOptionAddRedaction = computed(() => RedactOrHintOptions.IN_DOSSIER === this.formToSignalMap['addRedaction']());
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addHint.value; readonly displayExtraOptionAddHint = computed(() => RedactOrHintOptions.IN_DOSSIER === this.formToSignalMap['addHint']());
} readonly displayExtraOptionRemoveRedaction = computed(() =>
(
get displayExtraOptionRemoveRedaction() {
return (
[RemoveRedactionOptions.IN_DOSSIER, RemoveRedactionOptions.FALSE_POSITIVE] as Partial< [RemoveRedactionOptions.IN_DOSSIER, RemoveRedactionOptions.FALSE_POSITIVE] as Partial<
RemoveRedactionOption | SystemDefaultType RemoveRedactionOption | SystemDefaultType
>[] >[]
).includes(this.form.controls.removeRedaction.value); ).includes(this.formToSignalMap['removeRedaction']()),
} );
readonly displayExtraOptionRemoveHint = computed(() => RemoveRedactionOptions.IN_DOSSIER === this.formToSignalMap['removeHint']());
readonly displayExtraOptionRemoveRecommendation = computed(
() => RemoveRedactionOptions.DO_NOT_RECOMMEND === this.formToSignalMap['removeRecommendation'](),
);
get displayExtraOptionRemoveHint() { get #initialFormValue() {
return RemoveRedactionOptions.IN_DOSSIER === this.form.controls.removeHint.value; return {
} addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
addHint: this.#userPreferences.getAddHintDefaultOption(),
get displayExtraOptionRemoveRecommendation() { removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
return RemoveRedactionOptions.DO_NOT_RECOMMEND === this.form.controls.removeRecommendation.value; removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
} as DefaultOptionsForm;
} }
async save(): Promise<any> { async save(): Promise<any> {
const formValue = this.form.value;
if (this.initialFormValue.addRedaction !== this.form.controls.addRedaction.value) { if (this.initialFormValue.addRedaction !== this.form.controls.addRedaction.value) {
await this.#userPreferences.saveAddRedactionDefaultOption(this.form.controls.addRedaction.value); await this.#userPreferences.saveAddRedactionDefaultOption(this.form.controls.addRedaction.value);
} }
@ -114,7 +111,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
await this.#userPreferences.saveRemoveHintDefaultOption(this.form.controls.removeHint.value); await this.#userPreferences.saveRemoveHintDefaultOption(this.form.controls.removeHint.value);
} }
if (this.displayExtraOptionAddRedaction) { if (this.displayExtraOptionAddRedaction()) {
if (this.initialFormValue.addRedactionApplyToAll !== this.form.controls.addRedactionApplyToAll.value) { if (this.initialFormValue.addRedactionApplyToAll !== this.form.controls.addRedactionApplyToAll.value) {
await this.#userPreferences.saveAddRedactionDefaultExtraOption(this.form.controls.addRedactionApplyToAll.value); await this.#userPreferences.saveAddRedactionDefaultExtraOption(this.form.controls.addRedactionApplyToAll.value);
} }
@ -122,7 +119,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
await this.#userPreferences.saveAddRedactionDefaultExtraOption('undefined'); await this.#userPreferences.saveAddRedactionDefaultExtraOption('undefined');
} }
if (this.displayExtraOptionAddHint) { if (this.displayExtraOptionAddHint()) {
if (this.initialFormValue.addHintApplyToAll !== this.form.controls.addHintApplyToAll.value) { if (this.initialFormValue.addHintApplyToAll !== this.form.controls.addHintApplyToAll.value) {
await this.#userPreferences.saveAddHintDefaultExtraOption(this.form.controls.addHintApplyToAll.value); await this.#userPreferences.saveAddHintDefaultExtraOption(this.form.controls.addHintApplyToAll.value);
} }
@ -130,7 +127,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
await this.#userPreferences.saveAddHintDefaultExtraOption('undefined'); await this.#userPreferences.saveAddHintDefaultExtraOption('undefined');
} }
if (this.displayExtraOptionRemoveRedaction) { if (this.displayExtraOptionRemoveRedaction()) {
if (this.initialFormValue.removeRedactionApplyToAll !== this.form.controls.removeRedactionApplyToAll.value) { if (this.initialFormValue.removeRedactionApplyToAll !== this.form.controls.removeRedactionApplyToAll.value) {
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption(this.form.controls.removeRedactionApplyToAll.value); await this.#userPreferences.saveRemoveRedactionDefaultExtraOption(this.form.controls.removeRedactionApplyToAll.value);
} }
@ -138,7 +135,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption('undefined'); await this.#userPreferences.saveRemoveRedactionDefaultExtraOption('undefined');
} }
if (this.displayExtraOptionRemoveHint) { if (this.displayExtraOptionRemoveHint()) {
if (this.initialFormValue.removeHintApplyToAll !== this.form.controls.removeHintApplyToAll.value) { if (this.initialFormValue.removeHintApplyToAll !== this.form.controls.removeHintApplyToAll.value) {
await this.#userPreferences.saveRemoveHintDefaultExtraOption(this.form.controls.removeHintApplyToAll.value); await this.#userPreferences.saveRemoveHintDefaultExtraOption(this.form.controls.removeHintApplyToAll.value);
} }
@ -146,7 +143,7 @@ export class DialogDefaultsComponent extends BaseFormComponent {
await this.#userPreferences.saveRemoveHintDefaultExtraOption('undefined'); await this.#userPreferences.saveRemoveHintDefaultExtraOption('undefined');
} }
if (this.displayExtraOptionRemoveRecommendation) { if (this.displayExtraOptionRemoveRecommendation()) {
if (this.initialFormValue.removeRecommendationApplyToAll !== this.form.controls.removeRecommendationApplyToAll.value) { if (this.initialFormValue.removeRecommendationApplyToAll !== this.form.controls.removeRecommendationApplyToAll.value) {
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption( await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption(
this.form.controls.removeRecommendationApplyToAll.value, this.form.controls.removeRecommendationApplyToAll.value,
@ -163,17 +160,6 @@ export class DialogDefaultsComponent extends BaseFormComponent {
} }
#patchValues() { #patchValues() {
this.form.patchValue({ this.form.patchValue(this.#initialFormValue);
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
addHint: this.#userPreferences.getAddHintDefaultOption(),
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
});
} }
} }

View File

@ -44,6 +44,7 @@ const Screens = {
templateUrl: './preferences.component.html', templateUrl: './preferences.component.html',
styleUrls: ['./preferences.component.scss'], styleUrls: ['./preferences.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ imports: [
DialogDefaultsComponent, DialogDefaultsComponent,
NgClass, NgClass,

View File

@ -10,6 +10,7 @@ interface FormType {
@Component({ @Component({
templateUrl: './confirm-password-dialog.component.html', templateUrl: './confirm-password-dialog.component.html',
standalone: true,
imports: [ReactiveFormsModule, IconButtonComponent, TranslateModule, CircleButtonComponent], imports: [ReactiveFormsModule, IconButtonComponent, TranslateModule, CircleButtonComponent],
}) })
export class ConfirmPasswordDialogComponent extends BaseDialogComponent { export class ConfirmPasswordDialogComponent extends BaseDialogComponent {

View File

@ -16,16 +16,14 @@
<input formControlName="lastName" name="lastName" type="text" /> <input formControlName="lastName" name="lastName" type="text" />
</div> </div>
<div class="iqser-input-group"> <div *ngIf="devMode" class="iqser-input-group">
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label> <label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
<mat-form-field> <mat-form-field>
<mat-select formControlName="language"> <mat-select formControlName="language">
<mat-select-trigger>{{ languageSelectLabel() | translate }}</mat-select-trigger> <mat-select-trigger>{{ languageSelectLabel() | translate }}</mat-select-trigger>
@for (language of languages; track language) { <mat-option *ngFor="let language of languages" [value]="language">
<mat-option [value]="language"> {{ translations[language] | translate }}
{{ translations[language] | translate }} </mat-option>
</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@ -34,13 +32,11 @@
<a (click)="resetPassword()" target="_blank"> {{ 'user-profile-screen.actions.change-password' | translate }}</a> <a (click)="resetPassword()" target="_blank"> {{ 'user-profile-screen.actions.change-password' | translate }}</a>
</div> </div>
@if (devMode) { <div *ngIf="devMode" class="iqser-input-group">
<div class="iqser-input-group"> <mat-slide-toggle color="primary" formControlName="darkTheme">
<mat-slide-toggle color="primary" formControlName="darkTheme"> {{ 'user-profile-screen.form.dark-theme' | translate }}
{{ 'user-profile-screen.form.dark-theme' | translate }} </mat-slide-toggle>
</mat-slide-toggle> </div>
</div>
}
</div> </div>
</div> </div>

View File

@ -17,12 +17,12 @@ import { UserPreferenceService } from '@users/user-preference.service';
import { UserService } from '@users/user.service'; import { UserService } from '@users/user.service';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { UserProfileDialogService } from '../services/user-profile-dialog.service'; import { UserProfileDialogService } from '../services/user-profile-dialog.service';
import { NgForOf, NgIf } from '@angular/common';
import { MatFormField } from '@angular/material/form-field'; import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { MatSlideToggle } from '@angular/material/slide-toggle'; import { MatSlideToggle } from '@angular/material/slide-toggle';
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service'; import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
import { formControlToSignal } from '@utils/functions'; import { AsControl, formValueToSignal } from '@common-ui/utils';
import { AsControl } from '@common-ui/utils';
interface UserProfileForm { interface UserProfileForm {
email: string; email: string;
@ -36,11 +36,14 @@ interface UserProfileForm {
templateUrl: './user-profile-screen.component.html', templateUrl: './user-profile-screen.component.html',
styleUrls: ['./user-profile-screen.component.scss'], styleUrls: ['./user-profile-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ imports: [
ReactiveFormsModule, ReactiveFormsModule,
NgIf,
MatFormField, MatFormField,
MatSelect, MatSelect,
MatOption, MatOption,
NgForOf,
TranslateModule, TranslateModule,
MatSlideToggle, MatSlideToggle,
IconButtonComponent, IconButtonComponent,
@ -55,7 +58,7 @@ export class UserProfileScreenComponent extends BaseFormComponent {
readonly profileKeys = ['email', 'firstName', 'lastName']; readonly profileKeys = ['email', 'firstName', 'lastName'];
readonly languages = this._translateService.langs; readonly languages = this._translateService.langs;
readonly language = formControlToSignal<UserProfileForm['language']>(this.form.controls.language); readonly language = formValueToSignal<UserProfileForm['language']>(this.form.controls.language);
readonly languageSelectLabel = computed(() => this.translations[this.language()]); readonly languageSelectLabel = computed(() => this.translations[this.language()]);
constructor( constructor(
@ -78,28 +81,28 @@ export class UserProfileScreenComponent extends BaseFormComponent {
this._loadingService.stop(); this._loadingService.stop();
} }
get languageChanged(): boolean { get #languageChanged(): boolean {
return this.initialFormValue['language'] !== this.form.controls.language.value; return this.initialFormValue['language'] !== this.form.controls.language.value;
} }
get themeChanged(): boolean { get #themeChanged(): boolean {
return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value; return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value;
} }
get emailChanged(): boolean { get #emailChanged(): boolean {
return this.initialFormValue['email'] !== this.form.controls.email.value; return this.initialFormValue['email'] !== this.form.controls.email.value;
} }
get profileChanged(): boolean { get #profileChanged(): boolean {
return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value); return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value);
} }
async save(): Promise<void> { async save(): Promise<void> {
try { try {
if (this.profileChanged) { if (this.#profileChanged) {
const value = this.form.getRawValue() as IProfile; const value = this.form.getRawValue() as IProfile;
if (this.emailChanged) { if (this.#emailChanged) {
const dialogRef = this._dialogService.openDialog('confirmPassword'); const dialogRef = this._dialogService.openDialog('confirmPassword');
const password = await firstValueFrom(dialogRef.afterClosed()); const password = await firstValueFrom(dialogRef.afterClosed());
if (!password) { if (!password) {
@ -115,12 +118,12 @@ export class UserProfileScreenComponent extends BaseFormComponent {
await firstValueFrom(this._userService.loadAll()); await firstValueFrom(this._userService.loadAll());
} }
if (this.languageChanged) { if (this.#languageChanged) {
await this._languageService.change(this.form.controls.language.value); await this._languageService.change(this.form.controls.language.value);
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage); await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
} }
if (this.themeChanged) { if (this.#themeChanged) {
await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light'); await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light');
} }

View File

@ -1,24 +1,22 @@
import { inject, provideEnvironmentInitializer } from '@angular/core';
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
import { PermissionsGuard } from '@guards/permissions-guard';
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui'; import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
import { CopilotService } from '@services/copilot.service';
import { RedRoleGuard } from '@users/red-role.guard'; import { RedRoleGuard } from '@users/red-role.guard';
import { Roles } from '@users/roles'; import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component'; import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component'; import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component'; import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
import { AuditScreenComponent } from './screens/audit/audit-screen.component'; import { PermissionsGuard } from '@guards/permissions-guard';
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component'; import { Roles } from '@users/roles';
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component'; import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
import { AdminDialogService } from './services/admin-dialog.service'; import { AdminDialogService } from './services/admin-dialog.service';
import { AuditService } from './services/audit.service'; import { AuditService } from './services/audit.service';
import { DigitalSignatureService } from './services/digital-signature.service'; import { DigitalSignatureService } from './services/digital-signature.service';
@ -80,12 +78,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
}, },
type: 'ENTITY', type: 'ENTITY',
}, },
providers: [ providers: [RulesService],
RulesService,
provideEnvironmentInitializer(() => {
return inject(CopilotService).connectAsync('/api/llm/llm-websocket');
}),
],
}, },
{ {
path: 'component-rules', path: 'component-rules',

View File

@ -5,6 +5,7 @@ import { RouterOutlet } from '@angular/router';
@Component({ @Component({
templateUrl: './base-admin-screen.component.html', templateUrl: './base-admin-screen.component.html',
styleUrls: ['./base-admin-screen.component.scss'], styleUrls: ['./base-admin-screen.component.scss'],
standalone: true,
imports: [AdminSideNavComponent, RouterOutlet], imports: [AdminSideNavComponent, RouterOutlet],
}) })
export class BaseAdminScreenComponent {} export class BaseAdminScreenComponent {}

View File

@ -9,6 +9,7 @@ import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin
@Component({ @Component({
templateUrl: './base-dossier-template-screen.component.html', templateUrl: './base-dossier-template-screen.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ imports: [
DossierTemplateBreadcrumbsComponent, DossierTemplateBreadcrumbsComponent,
DossierTemplateActionsComponent, DossierTemplateActionsComponent,

View File

@ -18,6 +18,7 @@ import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin
@Component({ @Component({
templateUrl: './base-entity-screen.component.html', templateUrl: './base-entity-screen.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ imports: [
DossierTemplateBreadcrumbsComponent, DossierTemplateBreadcrumbsComponent,
CircleButtonComponent, CircleButtonComponent,

View File

@ -8,6 +8,7 @@ import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.
selector: 'redaction-users-stats', selector: 'redaction-users-stats',
templateUrl: './users-stats.component.html', templateUrl: './users-stats.component.html',
styleUrls: ['./users-stats.component.scss'], styleUrls: ['./users-stats.component.scss'],
standalone: true,
imports: [CircleButtonComponent, TranslateModule, DonutChartComponent], imports: [CircleButtonComponent, TranslateModule, DonutChartComponent],
}) })
export class UsersStatsComponent { export class UsersStatsComponent {

View File

@ -16,6 +16,7 @@ export interface CloneTemplateData {
@Component({ @Component({
templateUrl: './add-clone-dossier-template-dialog.component.html', templateUrl: './add-clone-dossier-template-dialog.component.html',
styleUrls: ['./add-clone-dossier-template-dialog.component.scss'], styleUrls: ['./add-clone-dossier-template-dialog.component.scss'],
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent], imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent],
}) })
export class AddCloneDossierTemplateDialogComponent extends BaseDialogComponent { export class AddCloneDossierTemplateDialogComponent extends BaseDialogComponent {

View File

@ -8,6 +8,7 @@ import { UserDetailsComponent } from './user-details/user-details.component';
@Component({ @Component({
selector: 'redaction-add-edit-user-dialog', selector: 'redaction-add-edit-user-dialog',
templateUrl: './add-edit-user-dialog.component.html', templateUrl: './add-edit-user-dialog.component.html',
standalone: true,
imports: [UserDetailsComponent, ResetPasswordComponent, CircleButtonComponent], imports: [UserDetailsComponent, ResetPasswordComponent, CircleButtonComponent],
}) })
export class AddEditUserDialogComponent extends BaseDialogComponent { export class AddEditUserDialogComponent extends BaseDialogComponent {

View File

@ -10,6 +10,7 @@ import { NamePipe } from '@common-ui/users/name.pipe';
@Component({ @Component({
selector: 'redaction-reset-password', selector: 'redaction-reset-password',
templateUrl: './reset-password.component.html', templateUrl: './reset-password.component.html',
standalone: true,
imports: [TranslateModule, NamePipe, ReactiveFormsModule, IconButtonComponent], imports: [TranslateModule, NamePipe, ReactiveFormsModule, IconButtonComponent],
}) })
export class ResetPasswordComponent { export class ResetPasswordComponent {

View File

@ -32,7 +32,7 @@
[formControlName]="role" [formControlName]="role"
color="primary" color="primary"
> >
{{ translations[role] | translate: { count: 1 } }} {{ translations[role] | translate }}
</mat-checkbox> </mat-checkbox>
</div> </div>
</div> </div>

View File

@ -15,6 +15,7 @@ import { NgForOf, NgIf } from '@angular/common';
selector: 'redaction-user-details', selector: 'redaction-user-details',
templateUrl: './user-details.component.html', templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.scss'], styleUrls: ['./user-details.component.scss'],
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, MatCheckbox, NgForOf, IconButtonComponent, NgIf], imports: [TranslateModule, ReactiveFormsModule, MatCheckbox, NgForOf, IconButtonComponent, NgIf],
}) })
export class UserDetailsComponent extends BaseFormComponent implements OnInit { export class UserDetailsComponent extends BaseFormComponent implements OnInit {
@ -37,7 +38,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
super(); super();
} }
get activeRoles(): string[] { get #activeRoles(): string[] {
return this.ROLES.reduce((acc, role) => { return this.ROLES.reduce((acc, role) => {
if (this.form.get(role).value) { if (this.form.get(role).value) {
acc.push(role); acc.push(role);
@ -46,7 +47,7 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
}, []); }, []);
} }
get sendSetPasswordMail() { get #sendSetPasswordMail() {
return !this.form.controls.sendSetPasswordMail.value; return !this.form.controls.sendSetPasswordMail.value;
} }
@ -88,8 +89,8 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
this._loadingService.start(); this._loadingService.start();
const userData: IProfileUpdateRequest = { const userData: IProfileUpdateRequest = {
...this.form.getRawValue(), ...this.form.getRawValue(),
roles: this.activeRoles, roles: this.#activeRoles,
sendSetPasswordMail: this.sendSetPasswordMail, sendSetPasswordMail: this.#sendSetPasswordMail,
}; };
if (!this.user()) { if (!this.user()) {

View File

@ -19,6 +19,7 @@ interface DialogData {
@Component({ @Component({
templateUrl: './add-entity-dialog.component.html', templateUrl: './add-entity-dialog.component.html',
styleUrls: ['./add-entity-dialog.component.scss'], styleUrls: ['./add-entity-dialog.component.scss'],
standalone: true,
imports: [AddEditEntityComponent, TranslateModule, IconButtonComponent, NgIf, CircleButtonComponent, HelpButtonComponent], imports: [AddEditEntityComponent, TranslateModule, IconButtonComponent, NgIf, CircleButtonComponent, HelpButtonComponent],
}) })
export class AddEntityDialogComponent extends BaseDialogComponent { export class AddEntityDialogComponent extends BaseDialogComponent {

View File

@ -16,6 +16,7 @@ type OrderFn = (a: KeyValue<string, string>, b: KeyValue<string, string>) => num
templateUrl: './audit-info-dialog.component.html', templateUrl: './audit-info-dialog.component.html',
styleUrls: ['./audit-info-dialog.component.scss'], styleUrls: ['./audit-info-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [TranslateModule, NgForOf, KeyValuePipe, HumanizePipe, CircleButtonComponent, JsonPipe], imports: [TranslateModule, NgForOf, KeyValuePipe, HumanizePipe, CircleButtonComponent, JsonPipe],
}) })
export class AuditInfoDialogComponent extends BaseDialogComponent { export class AuditInfoDialogComponent extends BaseDialogComponent {

View File

@ -20,6 +20,7 @@ const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
@Component({ @Component({
templateUrl: './configure-certificate-dialog.component.html', templateUrl: './configure-certificate-dialog.component.html',
styleUrls: ['./configure-certificate-dialog.component.scss'], styleUrls: ['./configure-certificate-dialog.component.scss'],
standalone: true,
imports: [ imports: [
DetailsRadioComponent, DetailsRadioComponent,
NgIf, NgIf,

View File

@ -13,6 +13,7 @@ import { TranslateModule } from '@ngx-translate/core';
templateUrl: './kms-signature-configuration.component.html', templateUrl: './kms-signature-configuration.component.html',
styleUrls: ['./kms-signature-configuration.component.scss'], styleUrls: ['./kms-signature-configuration.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule], imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule],
}) })
export class KmsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit { export class KmsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {

View File

@ -14,6 +14,7 @@ import { TranslateModule } from '@ngx-translate/core';
templateUrl: './pkcs-signature-configuration.component.html', templateUrl: './pkcs-signature-configuration.component.html',
styleUrls: ['./pkcs-signature-configuration.component.scss'], styleUrls: ['./pkcs-signature-configuration.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule], imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule],
}) })
export class PkcsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit { export class PkcsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {

View File

@ -20,6 +20,7 @@ interface IEditColorData {
@Component({ @Component({
templateUrl: './edit-color-dialog.component.html', templateUrl: './edit-color-dialog.component.html',
styleUrls: ['./edit-color-dialog.component.scss'], styleUrls: ['./edit-color-dialog.component.scss'],
standalone: true,
imports: [ReactiveFormsModule, TranslateModule, ColorPickerModule, MatIcon, NgIf, IconButtonComponent, CircleButtonComponent], imports: [ReactiveFormsModule, TranslateModule, ColorPickerModule, MatIcon, NgIf, IconButtonComponent, CircleButtonComponent],
}) })
export class EditColorDialogComponent extends BaseDialogComponent { export class EditColorDialogComponent extends BaseDialogComponent {

View File

@ -9,6 +9,7 @@ import { TranslateModule } from '@ngx-translate/core';
@Component({ @Component({
selector: 'redaction-smtp-auth-dialog', selector: 'redaction-smtp-auth-dialog',
templateUrl: './smtp-auth-dialog.component.html', templateUrl: './smtp-auth-dialog.component.html',
standalone: true,
imports: [ReactiveFormsModule, TranslateModule, IconButtonComponent, CircleButtonComponent, MatDialogClose], imports: [ReactiveFormsModule, TranslateModule, IconButtonComponent, CircleButtonComponent, MatDialogClose],
}) })
export class SmtpAuthDialogComponent extends BaseDialogComponent { export class SmtpAuthDialogComponent extends BaseDialogComponent {

View File

@ -7,6 +7,7 @@ import { TranslateModule } from '@ngx-translate/core';
templateUrl: './upload-dictionary-dialog.component.html', templateUrl: './upload-dictionary-dialog.component.html',
styleUrls: ['./upload-dictionary-dialog.component.scss'], styleUrls: ['./upload-dictionary-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [IconButtonComponent, TranslateModule], imports: [IconButtonComponent, TranslateModule],
}) })
export class UploadDictionaryDialogComponent { export class UploadDictionaryDialogComponent {

View File

@ -36,6 +36,7 @@ const PAGE_SIZE = 50;
templateUrl: './audit-screen.component.html', templateUrl: './audit-screen.component.html',
styleUrls: ['./audit-screen.component.scss'], styleUrls: ['./audit-screen.component.scss'],
providers: listingProvidersFactory(AuditScreenComponent), providers: listingProvidersFactory(AuditScreenComponent),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
TranslateModule, TranslateModule,

View File

@ -1,18 +1,12 @@
import { Component, OnInit, signal } from '@angular/core'; import { Component, OnInit, signal } from '@angular/core';
import { import { BaseFormComponent, CircleButtonComponent, IconButtonComponent, listingProvidersFactory, LoadingService } from '@iqser/common-ui';
BaseFormComponent,
CircleButtonComponent,
HasScrollbarDirective,
IconButtonComponent,
listingProvidersFactory,
LoadingService,
} from '@iqser/common-ui';
import { ComponentDefinitionsService } from '@services/entity-services/component-definitions.service'; import { ComponentDefinitionsService } from '@services/entity-services/component-definitions.service';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { getParam } from '@common-ui/utils'; import { getParam } from '@common-ui/utils';
import { DOSSIER_TEMPLATE_ID, IComponentDefinition } from '@red/domain'; import { DOSSIER_TEMPLATE_ID, IComponentDefinition } from '@red/domain';
import { toObservable } from '@angular/core/rxjs-interop'; import { toObservable } from '@angular/core/rxjs-interop';
import { CommonModule } from '@angular/common'; import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
import { CommonModule, NgIf } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
@ -24,8 +18,11 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
templateUrl: './component-definitions.component.html', templateUrl: './component-definitions.component.html',
styleUrls: ['./component-definitions.component.scss'], styleUrls: ['./component-definitions.component.scss'],
providers: listingProvidersFactory(ComponentDefinitionsComponent), providers: listingProvidersFactory(ComponentDefinitionsComponent),
standalone: true,
imports: [ imports: [
IconButtonComponent, IconButtonComponent,
InputWithActionComponent,
NgIf,
TranslateModule, TranslateModule,
CommonModule, CommonModule,
MatIcon, MatIcon,
@ -35,7 +32,6 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
CircleButtonComponent, CircleButtonComponent,
HasScrollbarDirective,
], ],
}) })
export default class ComponentDefinitionsComponent extends BaseFormComponent implements OnInit { export default class ComponentDefinitionsComponent extends BaseFormComponent implements OnInit {
@ -107,7 +103,6 @@ export default class ComponentDefinitionsComponent extends BaseFormComponent imp
this._dialogService.openDialog('confirm', null, async () => { this._dialogService.openDialog('confirm', null, async () => {
await firstValueFrom(this._componentDefinitionsService.deleteComponentDefinitions(this.#dossierTemplateId, [componentId])); await firstValueFrom(this._componentDefinitionsService.deleteComponentDefinitions(this.#dossierTemplateId, [componentId]));
await this.#loadData(); await this.#loadData();
this.selectedComponent = null;
}); });
} }

View File

@ -7,7 +7,7 @@ import { NgForOf, NgIf } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { MatOption } from '@angular/material/autocomplete'; import { MatOption } from '@angular/material/autocomplete';
import { MatSelect } from '@angular/material/select'; import { MatSelect, MatSelectTrigger } from '@angular/material/select';
import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations'; import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { ComponentMappingsService } from '@services/entity-services/component-mappings.service'; import { ComponentMappingsService } from '@services/entity-services/component-mappings.service';
@ -31,6 +31,7 @@ interface DialogResult {
@Component({ @Component({
templateUrl: './add-edit-component-mapping-dialog.component.html', templateUrl: './add-edit-component-mapping-dialog.component.html',
styleUrls: ['./add-edit-component-mapping-dialog.component.scss'], styleUrls: ['./add-edit-component-mapping-dialog.component.scss'],
standalone: true,
imports: [ imports: [
TranslateModule, TranslateModule,
ReactiveFormsModule, ReactiveFormsModule,
@ -40,6 +41,7 @@ interface DialogResult {
CircleButtonComponent, CircleButtonComponent,
MatDialogModule, MatDialogModule,
MatOption, MatOption,
MatSelectTrigger,
MatSelect, MatSelect,
IconButtonComponent, IconButtonComponent,
UploadFileComponent, UploadFileComponent,

View File

@ -1,5 +1,6 @@
import { AsyncPipe, NgIf } from '@angular/common'; import { AsyncPipe, NgIf } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { RouterLink } from '@angular/router';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component'; import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
import { getCurrentUser } from '@common-ui/users'; import { getCurrentUser } from '@common-ui/users';
@ -24,6 +25,9 @@ import { Roles } from '@users/roles';
import { combineLatest, firstValueFrom } from 'rxjs'; import { combineLatest, firstValueFrom } from 'rxjs';
import { map, tap } from 'rxjs/operators'; import { map, tap } from 'rxjs/operators';
import { AdminDialogService } from '../../services/admin-dialog.service'; import { AdminDialogService } from '../../services/admin-dialog.service';
import { AdminSideNavComponent } from '../../shared/components/admin-side-nav/admin-side-nav.component';
import { DossierTemplateActionsComponent } from '../../shared/components/dossier-template-actions/dossier-template-actions.component';
import { DossierTemplateBreadcrumbsComponent } from '../../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
import { AddEditComponentMappingDialogComponent } from './add-edit-component-mapping-dialog/add-edit-component-mapping-dialog.component'; import { AddEditComponentMappingDialogComponent } from './add-edit-component-mapping-dialog/add-edit-component-mapping-dialog.component';
import { download } from '@utils/file-download-utils'; import { download } from '@utils/file-download-utils';
import { MatTooltip } from '@angular/material/tooltip'; import { MatTooltip } from '@angular/material/tooltip';
@ -32,11 +36,16 @@ import { MatTooltip } from '@angular/material/tooltip';
templateUrl: './component-mappings-screen.component.html', templateUrl: './component-mappings-screen.component.html',
styleUrls: ['./component-mappings-screen.component.scss'], styleUrls: ['./component-mappings-screen.component.scss'],
providers: listingProvidersFactory(ComponentMappingsScreenComponent), providers: listingProvidersFactory(ComponentMappingsScreenComponent),
standalone: true,
imports: [ imports: [
DossierTemplateBreadcrumbsComponent,
AsyncPipe, AsyncPipe,
NgIf, NgIf,
DossierTemplateActionsComponent,
CircleButtonComponent, CircleButtonComponent,
TranslateModule, TranslateModule,
RouterLink,
AdminSideNavComponent,
IqserListingModule, IqserListingModule,
InputWithActionComponent, InputWithActionComponent,
IconButtonComponent, IconButtonComponent,

View File

@ -33,6 +33,7 @@ interface ListItem extends IListable {
styleUrls: ['./default-colors-screen.component.scss'], styleUrls: ['./default-colors-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
providers: listingProvidersFactory(DefaultColorsScreenComponent), providers: listingProvidersFactory(DefaultColorsScreenComponent),
standalone: true,
imports: [IqserListingModule, NgStyle, CircleButtonComponent, IqserAllowDirective, TranslateModule, AsyncPipe, NgIf], imports: [IqserListingModule, NgStyle, CircleButtonComponent, IqserAllowDirective, TranslateModule, AsyncPipe, NgIf],
}) })
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> implements OnInit { export class DefaultColorsScreenComponent extends ListingComponent<ListItem> implements OnInit {

View File

@ -25,6 +25,7 @@ import { DigitalSignatureService } from '../../services/digital-signature.servic
selector: 'redaction-digital-signature-screen', selector: 'redaction-digital-signature-screen',
templateUrl: './digital-signature-screen.component.html', templateUrl: './digital-signature-screen.component.html',
styleUrls: ['./digital-signature-screen.component.scss'], styleUrls: ['./digital-signature-screen.component.scss'],
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
EmptyStateComponent, EmptyStateComponent,

View File

@ -20,6 +20,7 @@ export interface AddEditDossierAttributeDialogData {
@Component({ @Component({
templateUrl: './add-edit-dossier-attribute-dialog.component.html', templateUrl: './add-edit-dossier-attribute-dialog.component.html',
styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss'], styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss'],
standalone: true,
imports: [ imports: [
TranslateModule, TranslateModule,
ReactiveFormsModule, ReactiveFormsModule,

View File

@ -35,6 +35,7 @@ import { TableItemComponent } from './table-item/table-item.component';
entitiesService: DossierAttributesService, entitiesService: DossierAttributesService,
component: DossierAttributesListingScreenComponent, component: DossierAttributesListingScreenComponent,
}), }),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
TranslateModule, TranslateModule,

View File

@ -10,6 +10,7 @@ import { NgIf } from '@angular/common';
selector: 'redaction-table-item [attribute] [canEditDossierAttributes]', selector: 'redaction-table-item [attribute] [canEditDossierAttributes]',
templateUrl: './table-item.component.html', templateUrl: './table-item.component.html',
styleUrls: ['./table-item.component.scss'], styleUrls: ['./table-item.component.scss'],
standalone: true,
imports: [MatTooltip, TranslateModule, CircleButtonComponent, NgIf], imports: [MatTooltip, TranslateModule, CircleButtonComponent, NgIf],
}) })
export class TableItemComponent { export class TableItemComponent {

View File

@ -18,6 +18,7 @@ export interface AddEditDossierStateDialogData {
@Component({ @Component({
templateUrl: './add-edit-dossier-state-dialog.component.html', templateUrl: './add-edit-dossier-state-dialog.component.html',
styleUrls: ['./add-edit-dossier-state-dialog.component.scss'], styleUrls: ['./add-edit-dossier-state-dialog.component.scss'],
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, ColorPickerModule, MatIcon, NgIf, IconButtonComponent, CircleButtonComponent], imports: [TranslateModule, ReactiveFormsModule, ColorPickerModule, MatIcon, NgIf, IconButtonComponent, CircleButtonComponent],
}) })
export class AddEditDossierStateDialogComponent extends BaseDialogComponent { export class AddEditDossierStateDialogComponent extends BaseDialogComponent {

View File

@ -11,7 +11,7 @@ import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.se
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
import { MatFormField } from '@angular/material/form-field'; import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect } from '@angular/material/select'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { NgForOf, NgIf } from '@angular/common'; import { NgForOf, NgIf } from '@angular/common';
export interface ConfirmDeleteDossierStateDialogData { export interface ConfirmDeleteDossierStateDialogData {
@ -23,11 +23,13 @@ export interface ConfirmDeleteDossierStateDialogData {
@Component({ @Component({
templateUrl: './confirm-delete-dossier-state-dialog.component.html', templateUrl: './confirm-delete-dossier-state-dialog.component.html',
styleUrls: ['./confirm-delete-dossier-state-dialog.component.scss'], styleUrls: ['./confirm-delete-dossier-state-dialog.component.scss'],
standalone: true,
imports: [ imports: [
TranslateModule, TranslateModule,
ReactiveFormsModule, ReactiveFormsModule,
MatCheckbox, MatCheckbox,
MatFormField, MatFormField,
MatSelectTrigger,
MatSelect, MatSelect,
MatOption, MatOption,
NgForOf, NgForOf,

View File

@ -33,6 +33,7 @@ import { DossierStatesTableItemComponent } from '../dossier-states-table-item/do
templateUrl: './dossier-states-listing-screen.component.html', templateUrl: './dossier-states-listing-screen.component.html',
styleUrls: ['./dossier-states-listing-screen.component.scss'], styleUrls: ['./dossier-states-listing-screen.component.scss'],
providers: listingProvidersFactory(DossierStatesListingScreenComponent), providers: listingProvidersFactory(DossierStatesListingScreenComponent),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
DonutChartComponent, DonutChartComponent,

View File

@ -20,6 +20,7 @@ import { SnakeCasePipe } from '@common-ui/pipes/snake-case.pipe';
selector: 'redaction-dossier-states-table-item', selector: 'redaction-dossier-states-table-item',
templateUrl: './dossier-states-table-item.component.html', templateUrl: './dossier-states-table-item.component.html',
styleUrls: ['./dossier-states-table-item.component.scss'], styleUrls: ['./dossier-states-table-item.component.scss'],
standalone: true,
imports: [MatTooltip, CircleButtonComponent, TranslateModule, NgIf, SnakeCasePipe], imports: [MatTooltip, CircleButtonComponent, TranslateModule, NgIf, SnakeCasePipe],
}) })
export class DossierStatesTableItemComponent { export class DossierStatesTableItemComponent {

View File

@ -33,6 +33,7 @@ import { TableItemComponent } from '../table-item/table-item.component';
entitiesService: DossierTemplatesService, entitiesService: DossierTemplatesService,
component: DossierTemplatesListingScreenComponent, component: DossierTemplatesListingScreenComponent,
}), }),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
TranslateModule, TranslateModule,

View File

@ -17,6 +17,7 @@ import { DatePipe } from '@shared/pipes/date.pipe';
templateUrl: './table-item.component.html', templateUrl: './table-item.component.html',
styleUrls: ['./table-item.component.scss'], styleUrls: ['./table-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [MatTooltip, NgIf, AsyncPipe, MatIcon, TranslateModule, DatePipe, DossierTemplateActionsComponent, InitialsAvatarComponent], imports: [MatTooltip, NgIf, AsyncPipe, MatIcon, TranslateModule, DatePipe, DossierTemplateActionsComponent, InitialsAvatarComponent],
}) })
export class TableItemComponent implements OnChanges { export class TableItemComponent implements OnChanges {

View File

@ -87,7 +87,6 @@
[routerLink]="dict.routerLink" [routerLink]="dict.routerLink"
[tooltip]="'entities-listing.action.edit' | translate" [tooltip]="'entities-listing.action.edit' | translate"
icon="iqser:edit" icon="iqser:edit"
iqserStopPropagation
></iqser-circle-button> ></iqser-circle-button>
</div> </div>
</div> </div>

View File

@ -11,7 +11,6 @@ import {
ListingComponent, ListingComponent,
listingProvidersFactory, listingProvidersFactory,
LoadingService, LoadingService,
StopPropagationDirective,
TableColumnConfig, TableColumnConfig,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { getParam } from '@iqser/common-ui/lib/utils'; import { getParam } from '@iqser/common-ui/lib/utils';
@ -30,6 +29,7 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
templateUrl: './entities-listing-screen.component.html', templateUrl: './entities-listing-screen.component.html',
styleUrls: ['./entities-listing-screen.component.scss'], styleUrls: ['./entities-listing-screen.component.scss'],
providers: listingProvidersFactory(EntitiesListingScreenComponent), providers: listingProvidersFactory(EntitiesListingScreenComponent),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
TranslateModule, TranslateModule,
@ -41,7 +41,6 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
AnnotationIconComponent, AnnotationIconComponent,
AsyncPipe, AsyncPipe,
RouterLink, RouterLink,
StopPropagationDirective,
], ],
}) })
export class EntitiesListingScreenComponent extends ListingComponent<Dictionary> implements OnInit { export class EntitiesListingScreenComponent extends ListingComponent<Dictionary> implements OnInit {

View File

@ -16,6 +16,7 @@ import { AsyncPipe } from '@angular/common';
templateUrl: './dictionary-screen.component.html', templateUrl: './dictionary-screen.component.html',
styleUrls: ['./dictionary-screen.component.scss'], styleUrls: ['./dictionary-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [AsyncPipe, DictionaryManagerComponent], imports: [AsyncPipe, DictionaryManagerComponent],
}) })
export class DictionaryScreenComponent implements OnInit { export class DictionaryScreenComponent implements OnInit {

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, HostListener, ViewChild } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostListener, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { getConfig, HasScrollbarDirective, IconButtonComponent, IconButtonTypes } from '@iqser/common-ui'; import { getConfig, HasScrollbarDirective, HelpButtonComponent, IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
import { IqserEventTarget } from '@iqser/common-ui/lib/utils'; import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
import { Dictionary, DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain'; import { Dictionary, DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
@ -17,7 +17,17 @@ import { TranslateModule } from '@ngx-translate/core';
templateUrl: './entity-info.component.html', templateUrl: './entity-info.component.html',
styleUrls: ['./entity-info.component.scss'], styleUrls: ['./entity-info.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [HasScrollbarDirective, MatIcon, NgIf, TranslateModule, AsyncPipe, IconButtonComponent, AddEditEntityComponent], standalone: true,
imports: [
HasScrollbarDirective,
MatIcon,
NgIf,
TranslateModule,
AsyncPipe,
IconButtonComponent,
AddEditEntityComponent,
HelpButtonComponent,
],
}) })
export class EntityInfoComponent { export class EntityInfoComponent {
@ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent; @ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent;

View File

@ -22,6 +22,7 @@ export interface AddEditFileAttributeDialogData {
@Component({ @Component({
templateUrl: './add-edit-file-attribute-dialog.component.html', templateUrl: './add-edit-file-attribute-dialog.component.html',
styleUrls: ['./add-edit-file-attribute-dialog.component.scss'], styleUrls: ['./add-edit-file-attribute-dialog.component.scss'],
standalone: true,
imports: [ imports: [
ReactiveFormsModule, ReactiveFormsModule,
TranslateModule, TranslateModule,

View File

@ -13,6 +13,7 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
@Component({ @Component({
templateUrl: './file-attributes-configurations-dialog.component.html', templateUrl: './file-attributes-configurations-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ imports: [
ReactiveFormsModule, ReactiveFormsModule,
MatSlideToggleModule, MatSlideToggleModule,
@ -57,7 +58,6 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo
if (supportCsvMapping) { if (supportCsvMapping) {
return { return {
...this.#configuration, ...this.#configuration,
keyColumn: this.form.get('keyColumn').value,
filenameMappingColumnHeaderName: this.form.get('keyColumn').value, filenameMappingColumnHeaderName: this.form.get('keyColumn').value,
delimiter: this.form.get('delimiter').value, delimiter: this.form.get('delimiter').value,
encoding: this.form.get('encodingType').value, encoding: this.form.get('encodingType').value,
@ -67,14 +67,13 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo
return { return {
...this.#configuration, ...this.#configuration,
filenameMappingColumnHeaderName: '', filenameMappingColumnHeaderName: '',
keyColumn: this.form.get('keyColumn').value,
}; };
} }
#getForm() { #getForm() {
return this._formBuilder.group({ return this._formBuilder.group({
supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName], supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName],
keyColumn: [this.#configuration.filenameMappingColumnHeaderName || this.#configuration.keyColumn || '', [Validators.required]], keyColumn: [this.#configuration.filenameMappingColumnHeaderName || '', [Validators.required]],
delimiter: [this.#configuration.delimiter || '', [Validators.required]], delimiter: [this.#configuration.delimiter || '', [Validators.required]],
encodingType: [this.#configuration.encoding || FileAttributeEncodingTypes['UTF-8'], [Validators.required]], encodingType: [this.#configuration.encoding || FileAttributeEncodingTypes['UTF-8'], [Validators.required]],
}); });

View File

@ -2,8 +2,8 @@ import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatFormField } from '@angular/material/form-field'; import { MatFormField } from '@angular/material/form-field';
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu'; import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
import { MatOption, MatSelect } from '@angular/material/select'; import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { MatSlideToggle } from '@angular/material/slide-toggle'; import { MatSlideToggle } from '@angular/material/slide-toggle';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ChevronButtonComponent } from '@common-ui/buttons/chevron-button'; import { ChevronButtonComponent } from '@common-ui/buttons/chevron-button';
@ -19,6 +19,7 @@ import { fileAttributeTypesTranslations } from '@translations/file-attribute-typ
templateUrl: './active-fields-listing.component.html', templateUrl: './active-fields-listing.component.html',
styleUrls: ['./active-fields-listing.component.scss'], styleUrls: ['./active-fields-listing.component.scss'],
providers: listingProvidersFactory(ActiveFieldsListingComponent), providers: listingProvidersFactory(ActiveFieldsListingComponent),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
CircleButtonComponent, CircleButtonComponent,
@ -29,6 +30,7 @@ import { fileAttributeTypesTranslations } from '@translations/file-attribute-typ
MatMenu, MatMenu,
EditableInputComponent, EditableInputComponent,
MatFormField, MatFormField,
MatSelectTrigger,
MatSelect, MatSelect,
MatOption, MatOption,
FormsModule, FormsModule,
@ -36,7 +38,6 @@ import { fileAttributeTypesTranslations } from '@translations/file-attribute-typ
RoundCheckboxComponent, RoundCheckboxComponent,
NgForOf, NgForOf,
NgIf, NgIf,
MatMenuItem,
], ],
}) })
export class ActiveFieldsListingComponent extends ListingComponent<IField> implements OnChanges { export class ActiveFieldsListingComponent extends ListingComponent<IField> implements OnChanges {

View File

@ -4,7 +4,7 @@ import { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGr
import { MatAutocomplete, MatAutocompleteTrigger, MatOption } from '@angular/material/autocomplete'; import { MatAutocomplete, MatAutocompleteTrigger, MatOption } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from '@angular/material/dialog';
import { MatFormField } from '@angular/material/form-field'; import { MatFormField } from '@angular/material/form-field';
import { MatSelect } from '@angular/material/select'; import { MatSelect, MatSelectTrigger } from '@angular/material/select';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component'; import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
import { import {
@ -38,6 +38,7 @@ export interface IFileAttributesCSVImportData {
styleUrls: ['./file-attributes-csv-import-dialog.component.scss'], styleUrls: ['./file-attributes-csv-import-dialog.component.scss'],
providers: listingProvidersFactory(), providers: listingProvidersFactory(),
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ imports: [
ReactiveFormsModule, ReactiveFormsModule,
MatFormField, MatFormField,
@ -45,6 +46,7 @@ export interface IFileAttributesCSVImportData {
MatAutocomplete, MatAutocomplete,
AsyncPipe, AsyncPipe,
MatOption, MatOption,
MatSelectTrigger,
MatSelect, MatSelect,
CircleButtonComponent, CircleButtonComponent,
NgIf, NgIf,

View File

@ -46,6 +46,7 @@ import {
templateUrl: './file-attributes-listing-screen.component.html', templateUrl: './file-attributes-listing-screen.component.html',
styleUrls: ['./file-attributes-listing-screen.component.scss'], styleUrls: ['./file-attributes-listing-screen.component.scss'],
providers: listingProvidersFactory(FileAttributesListingScreenComponent), providers: listingProvidersFactory(FileAttributesListingScreenComponent),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
NgForOf, NgForOf,
@ -85,7 +86,6 @@ export default class FileAttributesListingScreenComponent extends ListingCompone
}, },
]; ];
readonly roles = Roles; readonly roles = Roles;
keyColumnValue: string = '';
constructor( constructor(
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
@ -172,13 +172,13 @@ export default class FileAttributesListingScreenComponent extends ListingCompone
FileAttributesConfigurationsDialogComponent, FileAttributesConfigurationsDialogComponent,
{ {
...defaultDialogConfig, ...defaultDialogConfig,
data: { ...this.#existingConfiguration, keyColumn: this.keyColumnValue }, data: this.#existingConfiguration,
}, },
); );
const configuration = await firstValueFrom(ref.afterClosed()); const configuration = await firstValueFrom(ref.afterClosed());
if (configuration) { if (configuration) {
this.keyColumnValue = configuration.keyColumn;
await this.#setConfigAndLoadData(configuration); await this.#setConfigAndLoadData(configuration);
} }
} }

View File

@ -13,6 +13,7 @@ import { MatSlideToggle } from '@angular/material/slide-toggle';
@Component({ @Component({
selector: 'redaction-general-config-form', selector: 'redaction-general-config-form',
templateUrl: './general-config-form.component.html', templateUrl: './general-config-form.component.html',
standalone: true,
imports: [ReactiveFormsModule, TranslateModule, NgIf, MatSlideToggle, IconButtonComponent], imports: [ReactiveFormsModule, TranslateModule, NgIf, MatSlideToggle, IconButtonComponent],
}) })
export class GeneralConfigFormComponent extends BaseFormComponent implements OnInit { export class GeneralConfigFormComponent extends BaseFormComponent implements OnInit {

View File

@ -13,6 +13,7 @@ import { ILicenseFeature } from '@red/domain';
selector: 'redaction-general-config-screen', selector: 'redaction-general-config-screen',
templateUrl: './general-config-screen.component.html', templateUrl: './general-config-screen.component.html',
styleUrls: ['./general-config-screen.component.scss'], styleUrls: ['./general-config-screen.component.scss'],
standalone: true,
imports: [IqserListingModule, GeneralConfigFormComponent, SystemPreferencesFormComponent, SmtpFormComponent, TranslateModule], imports: [IqserListingModule, GeneralConfigFormComponent, SystemPreferencesFormComponent, SmtpFormComponent, TranslateModule],
}) })
export class GeneralConfigScreenComponent extends BaseFormComponent implements AfterViewInit { export class GeneralConfigScreenComponent extends BaseFormComponent implements AfterViewInit {

View File

@ -21,6 +21,7 @@ import { NgIf } from '@angular/common';
@Component({ @Component({
selector: 'redaction-smtp-form', selector: 'redaction-smtp-form',
templateUrl: './smtp-form.component.html', templateUrl: './smtp-form.component.html',
standalone: true,
imports: [ReactiveFormsModule, TranslateModule, MatSlideToggle, IconButtonComponent, NgIf], imports: [ReactiveFormsModule, TranslateModule, MatSlideToggle, IconButtonComponent, NgIf],
}) })
export class SmtpFormComponent extends BaseFormComponent implements OnInit { export class SmtpFormComponent extends BaseFormComponent implements OnInit {

View File

@ -15,6 +15,7 @@ export type ValueType = 'number' | 'string' | 'boolean';
@Component({ @Component({
selector: 'redaction-system-preferences-form', selector: 'redaction-system-preferences-form',
templateUrl: './system-preferences-form.component.html', templateUrl: './system-preferences-form.component.html',
standalone: true,
imports: [NgIf, ReactiveFormsModule, NgForOf, TranslateModule, MatSlideToggle, IconButtonComponent], imports: [NgIf, ReactiveFormsModule, NgForOf, TranslateModule, MatSlideToggle, IconButtonComponent],
}) })
export class SystemPreferencesFormComponent extends BaseFormComponent { export class SystemPreferencesFormComponent extends BaseFormComponent {

View File

@ -34,20 +34,6 @@
<span [innerHTML]="'dossier-template-info-screen.created-on' | translate: { date: createdOn }"></span> <span [innerHTML]="'dossier-template-info-screen.created-on' | translate: { date: createdOn }"></span>
</div> </div>
<div *ngIf="areRulesLocked()">
<mat-icon
(click)="resetRules()"
[matTooltip]="
currentUser.isAdmin
? ('dossier-template-info-screen.rules-reset.tooltip' | translate)
: ('dossier-template-info-screen.rules-reset.disabled-action' | translate)
"
[class.action-icon]="currentUser.isAdmin"
svgIcon="iqser:alert-circle"
></mat-icon>
<span class="error">{{ 'dossier-template-info-screen.rules-reset.label' | translate }}</span>
</div>
<div> <div>
<mat-icon svgIcon="red:entries"></mat-icon> <mat-icon svgIcon="red:entries"></mat-icon>
{{ 'dossier-template-info-screen.entries' | translate: { count: ctx.stats.numberOfEntries } }} {{ 'dossier-template-info-screen.entries' | translate: { count: ctx.stats.numberOfEntries } }}

View File

@ -18,11 +18,3 @@
padding-right: 24px; padding-right: 24px;
margin-right: 24px; margin-right: 24px;
} }
.error {
color: var(--iqser-primary);
}
.action-icon {
cursor: pointer;
}

View File

@ -1,4 +1,4 @@
import { Component, computed, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { ContextComponent } from '@iqser/common-ui/lib/utils'; import { ContextComponent } from '@iqser/common-ui/lib/utils';
import { type DossierTemplate, type DossierTemplateStats } from '@red/domain'; import { type DossierTemplate, type DossierTemplateStats } from '@red/domain';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service'; import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
@ -9,12 +9,6 @@ import { MatIcon } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { InitialsAvatarComponent } from '@common-ui/users'; import { InitialsAvatarComponent } from '@common-ui/users';
import { DatePipe } from '@shared/pipes/date.pipe'; import { DatePipe } from '@shared/pipes/date.pipe';
import { RulesService } from '../../../services/rules.service';
import { Toaster } from '@iqser/common-ui';
import { MatTooltip } from '@angular/material/tooltip';
import { firstValueFrom } from 'rxjs';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { getCurrentUser } from '@users/user.service';
interface Context { interface Context {
readonly dossierTemplate: DossierTemplate; readonly dossierTemplate: DossierTemplate;
@ -25,21 +19,17 @@ interface Context {
selector: 'redaction-dossier-template-details', selector: 'redaction-dossier-template-details',
templateUrl: './dossier-template-details.component.html', templateUrl: './dossier-template-details.component.html',
styleUrls: ['./dossier-template-details.component.scss'], styleUrls: ['./dossier-template-details.component.scss'],
imports: [NgIf, AsyncPipe, MatIcon, TranslateModule, DatePipe, InitialsAvatarComponent, MatTooltip], standalone: true,
imports: [NgIf, AsyncPipe, MatIcon, TranslateModule, DatePipe, InitialsAvatarComponent],
}) })
export class DossierTemplateDetailsComponent extends ContextComponent<Context> implements OnInit { export class DossierTemplateDetailsComponent extends ContextComponent<Context> implements OnInit {
readonly translations = dossierTemplateStatusTranslations; readonly translations = dossierTemplateStatusTranslations;
@Input({ required: true }) dossierTemplateId: string; @Input({ required: true }) dossierTemplateId: string;
readonly areRulesLocked = computed(() => {
return this._rulesService.currentTemplateRules().timeoutDetected;
});
readonly currentUser = getCurrentUser();
constructor( constructor(
private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dossierTemplateStatsService: DossierTemplateStatsService, private readonly _dossierTemplateStatsService: DossierTemplateStatsService,
private readonly _rulesService: RulesService,
private readonly _toaster: Toaster,
) { ) {
super(); super();
} }
@ -50,15 +40,4 @@ export class DossierTemplateDetailsComponent extends ContextComponent<Context> i
stats: this._dossierTemplateStatsService.watch$(this.dossierTemplateId), stats: this._dossierTemplateStatsService.watch$(this.dossierTemplateId),
}); });
} }
async resetRules() {
if (!this.currentUser.isAdmin) return;
try {
await firstValueFrom(this._rulesService.reset(this.dossierTemplateId));
this._toaster.success(_('dossier-template-info-screen.rules-reset.success'));
await firstValueFrom(this._rulesService.getFor(this.dossierTemplateId));
} catch (error) {
this._toaster.rawError(error.error.message);
}
}
} }

View File

@ -22,6 +22,7 @@ import { Observable } from 'rxjs';
import { DossierTemplateDetailsComponent } from '../dossier-template-details/dossier-template-details.component'; import { DossierTemplateDetailsComponent } from '../dossier-template-details/dossier-template-details.component';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
import { NgIf } from '@angular/common';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
import { SelectComponent } from '@shared/components/select/select.component'; import { SelectComponent } from '@shared/components/select/select.component';
@ -35,12 +36,14 @@ const downloadTypes = ['ORIGINAL', 'PREVIEW', 'OPTIMIZED_PREVIEW', 'DELTA_PREVIE
@Component({ @Component({
templateUrl: './dossier-template-info-screen.component.html', templateUrl: './dossier-template-info-screen.component.html',
styleUrls: ['./dossier-template-info-screen.component.scss'], styleUrls: ['./dossier-template-info-screen.component.scss'],
standalone: true,
imports: [ imports: [
HasScrollbarDirective, HasScrollbarDirective,
ReactiveFormsModule, ReactiveFormsModule,
DossierTemplateDetailsComponent, DossierTemplateDetailsComponent,
TranslateModule, TranslateModule,
MatCheckbox, MatCheckbox,
NgIf,
MatDatepickerModule, MatDatepickerModule,
SelectComponent, SelectComponent,
IconButtonComponent, IconButtonComponent,

View File

@ -6,8 +6,8 @@ import { JustificationsService } from '@services/entity-services/justifications.
import { BaseDialogComponent, CircleButtonComponent, HasScrollbarDirective, IconButtonComponent } from '@iqser/common-ui'; import { BaseDialogComponent, CircleButtonComponent, HasScrollbarDirective, IconButtonComponent } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { formControlToSignal } from '@utils/functions';
import { toSignal } from '@angular/core/rxjs-interop'; import { toSignal } from '@angular/core/rxjs-interop';
import { formValueToSignal } from '@common-ui/utils';
interface DialogData { interface DialogData {
justification?: Justification; justification?: Justification;
@ -17,11 +17,12 @@ interface DialogData {
@Component({ @Component({
templateUrl: './add-edit-justification-dialog.component.html', templateUrl: './add-edit-justification-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent, HasScrollbarDirective], imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent, HasScrollbarDirective],
}) })
export class AddEditJustificationDialogComponent extends BaseDialogComponent { export class AddEditJustificationDialogComponent extends BaseDialogComponent {
readonly form = this.#getForm(); readonly form = this.#getForm();
readonly name = formControlToSignal(this.form.controls['name']); readonly name = formValueToSignal(this.form.controls['name']);
readonly allJustifications = toSignal(this._justificationService.all$); readonly allJustifications = toSignal(this._justificationService.all$);
readonly technicalName = computed(() => { readonly technicalName = computed(() => {
if (this.data.justification) { if (this.data.justification) {

View File

@ -33,6 +33,7 @@ import { TranslateModule } from '@ngx-translate/core';
}), }),
JustificationsDialogService, JustificationsDialogService,
], ],
standalone: true,
imports: [IqserListingModule, CircleButtonComponent, NgIf, TableItemComponent, TranslateModule, AsyncPipe, IconButtonComponent], imports: [IqserListingModule, CircleButtonComponent, NgIf, TableItemComponent, TranslateModule, AsyncPipe, IconButtonComponent],
}) })
export default class JustificationsScreenComponent extends ListingComponent<Justification> implements OnInit { export default class JustificationsScreenComponent extends ListingComponent<Justification> implements OnInit {

View File

@ -12,6 +12,7 @@ import { TranslateModule } from '@ngx-translate/core';
selector: 'redaction-table-item', selector: 'redaction-table-item',
templateUrl: './table-item.component.html', templateUrl: './table-item.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [MatTooltip, CircleButtonComponent, NgIf, TranslateModule], imports: [MatTooltip, CircleButtonComponent, NgIf, TranslateModule],
}) })
export class TableItemComponent { export class TableItemComponent {

View File

@ -6,6 +6,7 @@ import { BaseChartDirective, provideCharts, withDefaultRegisterables } from 'ng2
selector: 'redaction-chart', selector: 'redaction-chart',
templateUrl: './chart.component.html', templateUrl: './chart.component.html',
styleUrls: ['./chart.component.scss'], styleUrls: ['./chart.component.scss'],
standalone: true,
imports: [BaseChartDirective], imports: [BaseChartDirective],
providers: [provideCharts(withDefaultRegisterables())], providers: [provideCharts(withDefaultRegisterables())],
}) })

View File

@ -16,6 +16,7 @@ import { ChartComponent } from '../chart/chart.component';
selector: 'red-license-analysis-capacity-usage', selector: 'red-license-analysis-capacity-usage',
templateUrl: './license-analysis-capacity-usage.component.html', templateUrl: './license-analysis-capacity-usage.component.html',
styleUrls: ['./license-analysis-capacity-usage.component.scss'], styleUrls: ['./license-analysis-capacity-usage.component.scss'],
standalone: true,
imports: [SizePipe, DecimalPipe, ChartComponent, AsyncPipe, NgIf, TranslateModule], imports: [SizePipe, DecimalPipe, ChartComponent, AsyncPipe, NgIf, TranslateModule],
}) })
export class LicenseAnalysisCapacityUsageComponent { export class LicenseAnalysisCapacityUsageComponent {

View File

@ -14,6 +14,7 @@ import { ChartComponent } from '../chart/chart.component';
selector: 'red-license-page-usage', selector: 'red-license-page-usage',
templateUrl: './license-page-usage.component.html', templateUrl: './license-page-usage.component.html',
styleUrls: ['./license-page-usage.component.scss'], styleUrls: ['./license-page-usage.component.scss'],
standalone: true,
imports: [DecimalPipe, TranslateModule, ChartComponent, NgIf, AsyncPipe], imports: [DecimalPipe, TranslateModule, ChartComponent, NgIf, AsyncPipe],
}) })
export class LicensePageUsageComponent { export class LicensePageUsageComponent {

View File

@ -17,6 +17,7 @@ import { ChartComponent } from '../chart/chart.component';
selector: 'red-license-retention-capacity', selector: 'red-license-retention-capacity',
templateUrl: './license-retention-capacity.component.html', templateUrl: './license-retention-capacity.component.html',
styleUrls: ['./license-retention-capacity.component.scss'], styleUrls: ['./license-retention-capacity.component.scss'],
standalone: true,
imports: [SizePipe, DecimalPipe, NgIf, DonutChartComponent, AsyncPipe, ChartComponent, TranslateModule], imports: [SizePipe, DecimalPipe, NgIf, DonutChartComponent, AsyncPipe, ChartComponent, TranslateModule],
}) })
export class LicenseRetentionCapacityComponent { export class LicenseRetentionCapacityComponent {

View File

@ -6,7 +6,7 @@
</mat-select-trigger> </mat-select-trigger>
<mat-option *ngFor="let license of licenses" [value]="license"> <mat-option *ngFor="let license of licenses" [value]="license">
<ng-container *ngTemplateOutlet="licenseInfo; context: { license: license }"></ng-container> <ng-container *ngTemplateOutlet="licenseInfo; context: { license: this.license }"></ng-container>
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>

View File

@ -19,6 +19,7 @@ const translations = {
selector: 'redaction-license-select', selector: 'redaction-license-select',
templateUrl: './license-select.component.html', templateUrl: './license-select.component.html',
styleUrls: ['./license-select.component.scss'], styleUrls: ['./license-select.component.scss'],
standalone: true,
imports: [ imports: [
AsyncPipe, AsyncPipe,
NgIf, NgIf,

View File

@ -16,6 +16,7 @@ import { DatePipe } from '@shared/pipes/date.pipe';
@Component({ @Component({
templateUrl: './license-screen.component.html', templateUrl: './license-screen.component.html',
styleUrls: ['./license-screen.component.scss'], styleUrls: ['./license-screen.component.scss'],
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
TranslateModule, TranslateModule,

View File

@ -30,6 +30,7 @@ import { TranslateModule } from '@ngx-translate/core';
templateUrl: './permissions-screen.component.html', templateUrl: './permissions-screen.component.html',
styleUrls: ['./permissions-screen.component.scss'], styleUrls: ['./permissions-screen.component.scss'],
providers: listingProvidersFactory(PermissionsScreenComponent), providers: listingProvidersFactory(PermissionsScreenComponent),
standalone: true,
imports: [IqserListingModule, NgIf, NgForOf, MatSlideToggle, AsyncPipe, TranslateModule], imports: [IqserListingModule, NgIf, NgForOf, MatSlideToggle, AsyncPipe, TranslateModule],
}) })
export default class PermissionsScreenComponent extends ListingComponent<PermissionsMapping> implements OnInit { export default class PermissionsScreenComponent extends ListingComponent<PermissionsMapping> implements OnInit {

View File

@ -41,6 +41,7 @@ const placeholderTypes: PlaceholderType[] = ['generalPlaceholders', 'fileAttribu
templateUrl: './reports-screen.component.html', templateUrl: './reports-screen.component.html',
styleUrls: ['./reports-screen.component.scss'], styleUrls: ['./reports-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [HasScrollbarDirective, AsyncPipe, TranslateModule, CircleButtonComponent, IqserAllowDirective, SnakeCasePipe], imports: [HasScrollbarDirective, AsyncPipe, TranslateModule, CircleButtonComponent, IqserAllowDirective, SnakeCasePipe],
}) })
export default class ReportsScreenComponent implements OnInit { export default class ReportsScreenComponent implements OnInit {

View File

@ -1,57 +1,8 @@
<div class="header-container"> <div class="header-container">
<div [translate]="translations[type()]['title']" class="heading-l"></div> <div [translate]="translations[this.type]['title']" class="heading-l"></div>
</div> </div>
<div class="flex" style="height: 100%"> <ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
<div [class.collapsed]="collapsed()" class="right-container flex-column">
<div class="collapsed-wrapper">
<ng-container
*ngTemplateOutlet="collapsible; context: { action: 'expand', tooltip: ('copilot.label' | translate) }"
></ng-container>
<div class="all-caps-label" translate="copilot.label"></div>
</div>
<div class="header-wrapper flex mt-8">
<div class="heading-xl flex-1">{{ 'copilot.label' | translate | titlecase }}</div>
<ng-container
*ngTemplateOutlet="collapsible; context: { action: 'collapse', tooltip: ('copilot.label' | translate) }"
></ng-container>
</div>
<div class="mt-24">
@for (comment of conversation(); track comment) {
<div class="comment">
<div class="comment-details-wrapper">
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date: 'exactDate'" class="small-label">
{{ comment.date | date: 'sophisticatedDate' }}
</div>
</div>
<pre class="text-auto">{{ comment.text }}</pre>
</div>
}
<iqser-input-with-action
(action)="add($event)"
[placeholder]="'comments.add-comment' | translate"
autocomplete="off"
icon="iqser:collapse"
width="full"
></iqser-input-with-action>
</div>
</div>
</div>
<ng-template #collapsible let-action="action" let-tooltip="tooltip">
<iqser-circle-button
(action)="toggleCollapse()"
[icon]="'iqser:' + action"
[tooltipPosition]="IqserTooltipPositions.before"
[tooltip]="tooltip"
></iqser-circle-button>
</ng-template>
<div *ngIf="changed && permissionsService.canEditRules() && !isLeaving" class="changes-box"> <div *ngIf="changed && permissionsService.canEditRules() && !isLeaving" class="changes-box">
<div (click)="goToErrors()" *ngIf="numberOfErrors() || numberOfWarnings()" class="errors"> <div (click)="goToErrors()" *ngIf="numberOfErrors() || numberOfWarnings()" class="errors">
@ -61,15 +12,15 @@
<span <span
*ngIf="numberOfErrors()" *ngIf="numberOfErrors()"
[translateParams]="{ errors: numberOfErrors() }" [translateParams]="{ errors: numberOfErrors() }"
[translate]="translations[type()]['errors-found']" [translate]="translations[this.type]['errors-found']"
> >
</span> </span>
<span <span
*ngIf="numberOfWarnings()" *ngIf="numberOfWarnings()"
[class.only-warning]="!numberOfErrors()"
[translateParams]="{ warnings: numberOfWarnings() }" [translateParams]="{ warnings: numberOfWarnings() }"
[translate]="translations[type()]['warnings-found']" [translate]="translations[this.type]['warnings-found']"
class="warning" class="warning"
[class.only-warning]="!numberOfErrors()"
></span> ></span>
</div> </div>
</span> </span>
@ -78,11 +29,11 @@
<div class="actions"> <div class="actions">
<iqser-icon-button <iqser-icon-button
(action)="save()" (action)="save()"
[label]="translations[type()]['save-changes'] | translate" [label]="translations[this.type]['save-changes'] | translate"
[type]="iconButtonTypes.primary" [type]="iconButtonTypes.primary"
icon="iqser:check" icon="iqser:check"
></iqser-icon-button> ></iqser-icon-button>
<div (click)="revert()" [translate]="translations[type()]['revert-changes']" class="all-caps-label cancel"></div> <div (click)="revert()" [translate]="translations[this.type]['revert-changes']" class="all-caps-label cancel"></div>
</div> </div>
</div> </div>

View File

@ -81,22 +81,3 @@ ngx-monaco-editor {
gap: 24px; gap: 24px;
} }
} }
.right-container {
display: flex;
width: 750px;
min-width: 375px;
padding: 16px 24px 16px 24px;
&.has-scrollbar:hover {
padding-right: 13px;
}
redaction-dossier-details {
width: 100%;
}
}
.text-auto {
text-wrap: auto;
}

View File

@ -1,40 +1,22 @@
import { NgIf, NgTemplateOutlet, TitleCasePipe } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, OnInit, signal } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
computed,
DestroyRef,
inject,
input,
OnInit,
signal,
viewChild,
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
import { MatIcon } from '@angular/material/icon';
import { MatTooltip } from '@angular/material/tooltip';
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
import { TenantsService } from '@common-ui/tenants';
import { getCurrentUser } from '@common-ui/users';
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import { CircleButtonComponent, IconButtonComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { Debounce, IqserTooltipPositions } from '@iqser/common-ui/lib/utils';
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { TranslateModule } from '@ngx-translate/core';
import { DroolsKeywords, IRules } from '@red/domain';
import { CopilotService } from '@services/copilot.service';
import { EditorThemeService } from '@services/editor-theme.service';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { DatePipe } from '@shared/pipes/date.pipe'; import { IconButtonComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { firstValueFrom } from 'rxjs';
import { map } from 'rxjs/operators';
import { RulesService } from '../../../services/rules.service'; import { RulesService } from '../../../services/rules.service';
import { firstValueFrom } from 'rxjs';
import { DOSSIER_TEMPLATE_ID, DroolsKeywords, IRules } 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 { ActivatedRoute } from '@angular/router';
import { rulesScreenTranslations } from '../../../translations/rules-screen-translations'; import { rulesScreenTranslations } from '../../../translations/rules-screen-translations';
import ICodeEditor = monaco.editor.ICodeEditor; import ICodeEditor = monaco.editor.ICodeEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration; import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions; import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { MatIcon } from '@angular/material/icon';
import { FormsModule } from '@angular/forms';
import { NgIf } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
interface SyntaxError { interface SyntaxError {
line: number; line: number;
@ -49,57 +31,19 @@ interface UploadResponse {
deprecatedWarnings: SyntaxError[]; deprecatedWarnings: SyntaxError[];
} }
export const SentenceTypes = {
question: 'question',
answer: 'answer',
} as const;
export type SentenceType = keyof typeof SentenceTypes;
interface Sentence {
text: string | null;
date: string;
type: SentenceType;
}
const endingSentence: Sentence = { text: null, date: new Date().toISOString(), type: SentenceTypes.answer };
const RULE_VALIDATION_TIMEOUT = 2000; const RULE_VALIDATION_TIMEOUT = 2000;
@Component({ @Component({
templateUrl: './rules-screen.component.html', templateUrl: './rules-screen.component.html',
styleUrls: ['./rules-screen.component.scss'], styleUrls: ['./rules-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ standalone: true,
MonacoEditorModule, imports: [MonacoEditorModule, MatIcon, FormsModule, IconButtonComponent, NgIf, TranslateModule],
MatIcon,
FormsModule,
IconButtonComponent,
NgIf,
TranslateModule,
CircleButtonComponent,
InputWithActionComponent,
MatTooltip,
NgTemplateOutlet,
DatePipe,
TitleCasePipe,
],
}) })
export default class RulesScreenComponent implements OnInit, ComponentCanDeactivate { export default class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
readonly #errorGlyphs = signal<string[]>([]);
#codeEditor: ICodeEditor;
#decorations: string[] = [];
readonly #errors = signal<SyntaxError[]>([]);
#ruleValidationTimeout: number = null;
readonly #copilotService = inject(CopilotService);
readonly #currentUser = getCurrentUser();
readonly #conversation = signal<Sentence[]>([endingSentence]);
protected readonly collapsed = signal(true);
protected readonly IqserTooltipPositions = IqserTooltipPositions;
readonly dossierTemplateId = input.required<string>();
readonly translations = rulesScreenTranslations; readonly translations = rulesScreenTranslations;
readonly iconButtonTypes = IconButtonTypes; readonly iconButtonTypes = IconButtonTypes;
readonly inputWithAction = viewChild(InputWithActionComponent);
readonly editorOptions: IStandaloneEditorConstructionOptions = { readonly editorOptions: IStandaloneEditorConstructionOptions = {
theme: 'vs', theme: 'vs',
language: 'java', language: 'java',
@ -111,43 +55,19 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
initialLines: string[] = []; initialLines: string[] = [];
currentLines: string[] = []; currentLines: string[] = [];
isLeaving = false; isLeaving = false;
readonly type = input.required<IRules['ruleFileType']>(); readonly type: IRules['ruleFileType'];
readonly #errorGlyphs = signal<string[]>([]);
readonly numberOfErrors = computed(() => this.#errors().filter(e => !e.warning).length); readonly numberOfErrors = computed(() => this.#errors().filter(e => !e.warning).length);
readonly numberOfWarnings = computed(() => this.#errors().filter(e => e.warning).length); readonly numberOfWarnings = computed(() => this.#errors().filter(e => e.warning).length);
readonly conversation = computed(() => this.#conversation().filter(r => !!r.text)); readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
#codeEditor: ICodeEditor;
constructor( #decorations: string[] = [];
readonly permissionsService: PermissionsService, #errors = signal<SyntaxError[]>([]);
private readonly _rulesService: RulesService, #ruleValidationTimeout: number = null;
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _toaster: Toaster,
private readonly _loadingService: LoadingService,
private readonly _editorThemeService: EditorThemeService,
) {
const username = this.#currentUser.id;
const tenant = inject(TenantsService).activeTenantId;
inject(DestroyRef).onDestroy(() => this.#copilotService.deactivate());
this.#copilotService
.listen<{ token?: string }>('/user/' + username + '/queue/' + tenant + '/rules-copilot')
.pipe(
takeUntilDestroyed(),
map(res => res?.token),
)
.subscribe(token => {
if (token === null) {
this.#conversation.update(responses => [...responses, { ...endingSentence }]);
return;
}
this.#conversation.update(responses => {
const last = responses.pop();
return [...responses, { ...last, text: (last.text ?? '') + token }];
});
});
}
set isLeavingPage(isLeaving: boolean) { set isLeavingPage(isLeaving: boolean) {
this.isLeaving = isLeaving; this.isLeaving = isLeaving;
this._changeDetectorRef.markForCheck(); this._changeDetectorRef.detectChanges();
} }
get changed(): boolean { get changed(): boolean {
@ -165,23 +85,16 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
this.#closeProblemsView(); this.#closeProblemsView();
} }
toggleCollapse() { constructor(
this.collapsed.update(collapsed => !collapsed); readonly permissionsService: PermissionsService,
if (this.#conversation().length === 1) { private readonly _rulesService: RulesService,
this.#copilotService.send('Hello!'); private readonly _changeDetectorRef: ChangeDetectorRef,
} private readonly _toaster: Toaster,
} private readonly _loadingService: LoadingService,
private readonly _editorThemeService: EditorThemeService,
add(question: string) { private readonly _route: ActivatedRoute,
this.#conversation.update(responses => { ) {
const last = responses.pop(); this.type = this._route.snapshot.data.type;
last.text = question;
last.type = SentenceTypes.question;
last.date = new Date().toISOString();
return [...responses, last, { ...endingSentence }];
});
this.inputWithAction().reset();
this.#copilotService.send(question);
} }
async ngOnInit() { async ngOnInit() {
@ -209,7 +122,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
} }
(window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(true)); (window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(true));
await this.#configureSyntaxHighlighting(); await this.#configureSyntaxHighlighting();
this._changeDetectorRef.markForCheck(); this._changeDetectorRef.detectChanges();
} }
@Debounce() @Debounce()
@ -229,20 +142,12 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
await this.#uploadRules(); await this.#uploadRules();
} }
revert(): void {
this.currentLines = this.initialLines;
this.#decorations = this.#codeEditor?.deltaDecorations(this.#decorations, []) || [];
this.#removeErrorMarkers();
this._changeDetectorRef.markForCheck();
this._loadingService.stop();
}
async #uploadRules(dryRun = false) { async #uploadRules(dryRun = false) {
return firstValueFrom( return firstValueFrom(
this._rulesService.uploadRules({ this._rulesService.uploadRules({
rules: this.#getValue(), rules: this.#getValue(),
dossierTemplateId: this.dossierTemplateId(), dossierTemplateId: this.#dossierTemplateId,
ruleFileType: this.type(), ruleFileType: this.type,
dryRun, dryRun,
}), }),
).then( ).then(
@ -251,7 +156,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
this.#drawErrorMarkers(errors); this.#drawErrorMarkers(errors);
if (!dryRun) { if (!dryRun) {
await this.#initialize(); await this.#initialize();
this._toaster.success(rulesScreenTranslations[this.type()]['success.generic']); this._toaster.success(rulesScreenTranslations[this.type]['success.generic']);
} }
}, },
error => { error => {
@ -268,12 +173,20 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
this.#drawErrorMarkers(errors); this.#drawErrorMarkers(errors);
this._loadingService.stop(); this._loadingService.stop();
if (!dryRun) { if (!dryRun) {
this._toaster.error(rulesScreenTranslations[this.type()]['error.generic']); this._toaster.error(rulesScreenTranslations[this.type]['error.generic']);
} }
}, },
); );
} }
revert(): void {
this.currentLines = this.initialLines;
this.#decorations = this.#codeEditor?.deltaDecorations(this.#decorations, []) || [];
this.#removeErrorMarkers();
this._changeDetectorRef.detectChanges();
this._loadingService.stop();
}
#mapErrors(response: UploadResponse, dryRun = false) { #mapErrors(response: UploadResponse, dryRun = false) {
const warnings = response.deprecatedWarnings.map(w => ({ ...w, warning: true })); const warnings = response.deprecatedWarnings.map(w => ({ ...w, warning: true }));
if (dryRun) { if (dryRun) {
@ -383,7 +296,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
async #initialize() { async #initialize() {
this._loadingService.start(); this._loadingService.start();
await firstValueFrom(this._rulesService.download(this.dossierTemplateId(), this.type())).then( await firstValueFrom(this._rulesService.download(this.#dossierTemplateId, this.type)).then(
rules => { rules => {
this.currentLines = this.initialLines = rules.rules.split('\n'); this.currentLines = this.initialLines = rules.rules.split('\n');
this.revert(); this.revert();

View File

@ -46,6 +46,7 @@ function configToFilter({ key, label }: DonutChartConfig) {
entitiesService: UserService, entitiesService: UserService,
component: UserListingScreenComponent, component: UserListingScreenComponent,
}), }),
standalone: true,
imports: [ imports: [
IqserListingModule, IqserListingModule,
TranslateModule, TranslateModule,
@ -125,7 +126,7 @@ export class UserListingScreenComponent extends ListingComponent<User> implement
getDisplayRoles(user: User) { getDisplayRoles(user: User) {
const oldRedRoles = user.roles.filter(role => role.startsWith('RED_')); const oldRedRoles = user.roles.filter(role => role.startsWith('RED_'));
const translatedRoles = oldRedRoles.map(role => this._translateService.instant(this.translations[role], { count: 1 })); const translatedRoles = oldRedRoles.map(role => this._translateService.instant(this.translations[role]));
return translatedRoles.join(', ') || this._translateService.instant(this.translations['NO_ROLE']); return translatedRoles.join(', ') || this._translateService.instant(this.translations['NO_ROLE']);
} }

Some files were not shown because too many files have changed in this diff Show More