diff --git a/apps/red-ui/ngsw-config.json b/apps/red-ui/ngsw-config.json index 5b78c22f4..90d383880 100644 --- a/apps/red-ui/ngsw-config.json +++ b/apps/red-ui/ngsw-config.json @@ -16,6 +16,14 @@ "resources": { "files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] } + }, + { + "name": "pdftron", + "installMode": "prefetch", + "updateMode": "prefetch", + "resources": { + "files": ["/assets/wv-resources/**/*.*"] + } } ] } diff --git a/apps/red-ui/src/app/models/file/annotation.permissions.ts b/apps/red-ui/src/app/models/file/annotation.permissions.ts index 2c5d1e02d..7ce766bc0 100644 --- a/apps/red-ui/src/app/models/file/annotation.permissions.ts +++ b/apps/red-ui/src/app/models/file/annotation.permissions.ts @@ -38,13 +38,13 @@ export class AnnotationPermissions { permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive && annotationEntity.hasDictionary; permissions.canRemoveOrSuggestToRemoveOnlyHere = - (annotation.isRedacted || annotation.isHint) && !annotation.pending && !annotation.isImage; + !annotation.pending && (annotation.isRedacted || (annotation.isHint && !annotation.isImage)); permissions.canRemoveOrSuggestToRemoveFromDictionary = annotation.isModifyDictionary && (annotation.isRedacted || annotation.isSkipped || annotation.isHint) && !annotation.pending; - permissions.canChangeLegalBasis = annotation.isRedacted && !annotation.imported && !annotation.pending; + permissions.canChangeLegalBasis = annotation.isRedacted && !annotation.pending; permissions.canRecategorizeImage = ((annotation.isImage && !annotation.isSuggestion) || annotation.isSuggestionRecategorizeImage) && !annotation.pending; diff --git a/apps/red-ui/src/app/models/file/annotation.wrapper.ts b/apps/red-ui/src/app/models/file/annotation.wrapper.ts index 509b98f4d..0ce9794ae 100644 --- a/apps/red-ui/src/app/models/file/annotation.wrapper.ts +++ b/apps/red-ui/src/app/models/file/annotation.wrapper.ts @@ -1,4 +1,4 @@ -import { annotationTypesTranslations } from '../../translations/annotation-types-translations'; +import { annotationTypesTranslations, SuggestionAddFalsePositive } from '../../translations/annotation-types-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { Highlight, IComment, IManualChange, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain'; import { RedactionLogEntry } from '@models/file/redaction-log.entry'; @@ -292,11 +292,18 @@ export class AnnotationWrapper implements IListable, Record { this._createContent(annotationWrapper, redactionLogEntry); this._setSuperType(annotationWrapper, redactionLogEntry); this._handleRecommendations(annotationWrapper, redactionLogEntry); - annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType]; + annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper); return annotationWrapper; } + static #getTypeLabel(redactionLogEntry: RedactionLogEntry, annotation: AnnotationWrapper): string { + if (redactionLogEntry.reason?.toLowerCase() === 'false positive') { + return annotationTypesTranslations[SuggestionAddFalsePositive]; + } + return annotationTypesTranslations[annotation.superType]; + } + private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntry) { if (annotationWrapper.superType === SuperTypes.Recommendation) { annotationWrapper.recommendationType = redactionLogEntry.type; diff --git a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts index d142d6642..2fcbb5317 100644 --- a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts @@ -7,7 +7,6 @@ import { PendingChangesGuard } from '@guards/can-deactivate.guard'; import { FileAttributesListingScreenComponent } from './screens/file-attributes-listing/file-attributes-listing-screen.component'; import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component'; import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component'; -import { LicenseInformationScreenComponent } from './screens/license-information/license-information-screen.component'; import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component'; import { AuditScreenComponent } from './screens/audit/audit-screen.component'; import { RouterModule, Routes } from '@angular/router'; @@ -26,125 +25,127 @@ import { ACTIVE_DOSSIERS_SERVICE } from '../../tokens'; import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component'; import { PermissionsGuard } from '../../guards/permissions-guard'; -const routes: Routes = [ - { path: '', redirectTo: 'dossier-templates', pathMatch: 'full' }, +const dossierTemplateIdRoutes = [ { - path: 'dossier-templates', + path: 'info', + canActivate: [CompositeRouteGuard], + component: BaseDossierTemplateScreenComponent, + loadChildren: () => import('./screens/info/dossier-template-info.module').then(m => m.DossierTemplateInfoModule), + }, + { + path: 'entities', children: [ { path: '', - component: BaseAdminScreenComponent, + component: EntitiesListingScreenComponent, canActivate: [CompositeRouteGuard], data: { routeGuards: [AuthGuard, RedRoleGuard], }, - loadChildren: () => - import('./screens/dossier-templates-listing/dossier-templates-listing.module').then( - m => m.DossierTemplatesListingModule, - ), }, { - path: `:${DOSSIER_TEMPLATE_ID}`, - children: [ - { - path: 'info', - canActivate: [CompositeRouteGuard], - component: BaseDossierTemplateScreenComponent, - loadChildren: () => import('./screens/info/dossier-template-info.module').then(m => m.DossierTemplateInfoModule), - }, - { - path: 'entities', - children: [ - { - path: '', - component: EntitiesListingScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - { - path: `:${ENTITY_TYPE}`, - component: BaseEntityScreenComponent, - canActivate: [CompositeRouteGuard], - loadChildren: () => import('./screens/entities/entities.module').then(m => m.EntitiesModule), - data: { - routeGuards: [AuthGuard, RedRoleGuard, EntityExistsGuard], - }, - }, - ], - }, - { - path: 'rules', - component: BaseDossierTemplateScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule), - }, - { - path: 'file-attributes', - component: FileAttributesListingScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - { - path: 'watermark', - component: BaseDossierTemplateScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule), - }, - { - path: 'reports', - component: BaseDossierTemplateScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - loadChildren: () => import('./screens/reports/reports.module').then(m => m.ReportsModule), - }, - { - path: 'dossier-attributes', - component: DossierAttributesListingScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - { - path: 'dossier-states', - component: DossierStatesListingScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - { - path: 'default-colors', - component: DefaultColorsScreenComponent, - canActivate: [CompositeRouteGuard], - data: { - routeGuards: [AuthGuard, RedRoleGuard], - }, - }, - { - path: 'justifications', - component: BaseDossierTemplateScreenComponent, - canActivate: [CompositeRouteGuard], - loadChildren: () => import('./screens/justifications/justifications.module').then(m => m.JustificationsModule), - }, - { path: '', redirectTo: 'info', pathMatch: 'full' }, - ], + path: `:${ENTITY_TYPE}`, + component: BaseEntityScreenComponent, canActivate: [CompositeRouteGuard], - data: { routeGuards: [DossierTemplateExistsGuard] }, + loadChildren: () => import('./screens/entities/entities.module').then(m => m.EntitiesModule), + data: { + routeGuards: [AuthGuard, RedRoleGuard, EntityExistsGuard], + }, }, ], + }, + { + path: 'rules', + component: BaseDossierTemplateScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule), + }, + { + path: 'file-attributes', + component: FileAttributesListingScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + }, + { + path: 'watermark', + component: BaseDossierTemplateScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule), + }, + { + path: 'reports', + component: BaseDossierTemplateScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + loadChildren: () => import('./screens/reports/reports.module').then(m => m.ReportsModule), + }, + { + path: 'dossier-attributes', + component: DossierAttributesListingScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + }, + { + path: 'dossier-states', + component: DossierStatesListingScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + }, + { + path: 'default-colors', + component: DefaultColorsScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + }, + { + path: 'justifications', + component: BaseDossierTemplateScreenComponent, + canActivate: [CompositeRouteGuard], + loadChildren: () => import('./screens/justifications/justifications.module').then(m => m.JustificationsModule), + }, + { path: '', redirectTo: 'info', pathMatch: 'full' }, +]; + +const dossierTemplatesRoutes: Routes = [ + { + path: '', + component: BaseAdminScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard], + }, + loadChildren: () => + import('./screens/dossier-templates-listing/dossier-templates-listing.module').then(m => m.DossierTemplatesListingModule), + }, + { + path: `:${DOSSIER_TEMPLATE_ID}`, + children: dossierTemplateIdRoutes, + canActivate: [CompositeRouteGuard], + data: { routeGuards: [DossierTemplateExistsGuard] }, + }, +]; + +const routes: Routes = [ + { path: '', redirectTo: 'dossier-templates', pathMatch: 'full' }, + { + path: 'dossier-templates', + children: dossierTemplatesRoutes, canActivate: [CompositeRouteGuard], data: { routeGuards: [AuthGuard, RedRoleGuard, DossierTemplatesGuard], @@ -173,12 +174,13 @@ const routes: Routes = [ }, { path: 'license-info', - component: LicenseInformationScreenComponent, + component: BaseAdminScreenComponent, canActivate: [CompositeRouteGuard], data: { routeGuards: [AuthGuard, RedRoleGuard], requiredRoles: ['RED_ADMIN'], }, + loadChildren: () => import('./screens/license/license.module').then(m => m.LicenseModule), }, { path: 'digital-signature', diff --git a/apps/red-ui/src/app/modules/admin/admin.module.ts b/apps/red-ui/src/app/modules/admin/admin.module.ts index b13bdebea..f3be32ed2 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -7,7 +7,6 @@ import { DefaultColorsScreenComponent } from './screens/default-colors/default-c import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component'; import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component'; import { FileAttributesListingScreenComponent } from './screens/file-attributes-listing/file-attributes-listing-screen.component'; -import { LicenseInformationScreenComponent } from './screens/license-information/license-information-screen.component'; import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component'; import { DossierTemplateBreadcrumbsComponent } from './components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component'; import { ColorPickerModule } from 'ngx-color-picker'; @@ -15,8 +14,6 @@ import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-att import { AddEditDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component'; import { AddEntityDialogComponent } from './dialogs/add-entity-dialog/add-entity-dialog.component'; import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component'; -import { ComboChartComponent, ComboSeriesVerticalComponent } from './components/combo-chart'; -import { NgxChartsModule } from '@swimlane/ngx-charts'; import { AdminDialogService } from './services/admin-dialog.service'; import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component'; import { SmtpAuthDialogComponent } from './dialogs/smtp-auth-dialog/smtp-auth-dialog.component'; @@ -24,7 +21,6 @@ import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-e import { UsersStatsComponent } from './components/users-stats/users-stats.component'; import { FileAttributesCsvImportDialogComponent } from './dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component'; import { ActiveFieldsListingComponent } from './dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component'; -import { AdminSideNavComponent } from './admin-side-nav/admin-side-nav.component'; import { ResetPasswordComponent } from './dialogs/add-edit-user-dialog/reset-password/reset-password.component'; import { UserDetailsComponent } from './dialogs/add-edit-user-dialog/user-details/user-details.component'; import { AddEditDossierAttributeDialogComponent } from './dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component'; @@ -33,7 +29,6 @@ import { TrashScreenComponent } from './screens/trash/trash-screen.component'; import { AuditService } from './services/audit.service'; import { DigitalSignatureService } from './services/digital-signature.service'; import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component'; -import { LicenseReportService } from './services/licence-report.service'; import { RulesService } from './services/rules.service'; import { SmtpConfigService } from './services/smtp-config.service'; import { UploadDictionaryDialogComponent } from './dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component'; @@ -49,6 +44,7 @@ import { ConfirmDeleteDossierStateDialogComponent } from './dialogs/confirm-dele import { TrashTableItemComponent } from './screens/trash/trash-table-item/trash-table-item.component'; import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component'; import { CloneDossierTemplateDialogComponent } from './dialogs/clone-dossier-template-dialog/clone-dossier-template-dialog.component'; +import { AdminSideNavComponent } from './admin-side-nav/admin-side-nav.component'; const dialogs = [ AddEditDossierTemplateDialogComponent, @@ -70,7 +66,6 @@ const screens = [ EntitiesListingScreenComponent, DigitalSignatureScreenComponent, FileAttributesListingScreenComponent, - LicenseInformationScreenComponent, UserListingScreenComponent, GeneralConfigScreenComponent, DossierAttributesListingScreenComponent, @@ -79,11 +74,9 @@ const screens = [ const components = [ DossierTemplateBreadcrumbsComponent, - ComboChartComponent, - ComboSeriesVerticalComponent, UsersStatsComponent, - ActiveFieldsListingComponent, AdminSideNavComponent, + ActiveFieldsListingComponent, ResetPasswordComponent, UserDetailsComponent, BaseAdminScreenComponent, @@ -104,7 +97,7 @@ const components = [ ConfirmDeleteDossierStateDialogComponent, TrashTableItemComponent, ], - providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService], - imports: [CommonModule, SharedModule, AdminRoutingModule, SharedAdminModule, NgxChartsModule, ColorPickerModule, A11yModule], + providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, SmtpConfigService], + imports: [CommonModule, SharedModule, AdminRoutingModule, SharedAdminModule, ColorPickerModule, A11yModule], }) export class AdminModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.ts deleted file mode 100644 index ae953b5d7..000000000 --- a/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ConfigService } from '@services/config.service'; -import { TranslateService } from '@ngx-translate/core'; -import { ButtonConfig, IconButtonTypes, LoadingService } from '@iqser/common-ui'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { UserService } from '@services/user.service'; -import { RouterHistoryService } from '@services/router-history.service'; -import { LicenseReportService } from '../../services/licence-report.service'; -import { ILicenseReport } from '@red/domain'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; -import { firstValueFrom } from 'rxjs'; -import dayjs from 'dayjs'; - -@Component({ - selector: 'redaction-license-information-screen', - templateUrl: './license-information-screen.component.html', - styleUrls: ['./license-information-screen.component.scss'], -}) -export class LicenseInformationScreenComponent implements OnInit { - readonly currentUser = this._userService.currentUser; - readonly buttonConfigs: readonly ButtonConfig[] = [ - { - label: _('license-info-screen.email-report'), - action: (): void => this.sendMail(), - type: IconButtonTypes.primary, - }, - ]; - - currentInfo: ILicenseReport = {}; - totalInfo: ILicenseReport = {}; - unlicensedInfo: ILicenseReport = {}; - totalLicensedNumberOfPages = 0; - analysisPercentageOfLicense = 100; - barChart: any[]; - lineChartSeries: any[] = []; - lineChartScheme: Color = { - name: 'Line chart scheme', - selectable: true, - group: ScaleType.Ordinal, - domain: ['#dd4d50', '#5ce594', '#0389ec'], - }; - comboBarScheme: Color = { - name: 'Combo bar scheme', - selectable: true, - group: ScaleType.Ordinal, - domain: ['#0389ec'], - }; - - constructor( - readonly configService: ConfigService, - private readonly _userService: UserService, - private readonly _loadingService: LoadingService, - readonly routerHistoryService: RouterHistoryService, - private readonly _translateService: TranslateService, - private readonly _licenseReportService: LicenseReportService, - ) { - _loadingService.start(); - } - - get currentYear(): number { - return new Date().getFullYear(); - } - - async ngOnInit() { - this.totalLicensedNumberOfPages = this.configService.values.LICENSE_PAGE_COUNT || 0; - const startDate = dayjs(this.configService.values.LICENSE_START, 'DD-MM-YYYY'); - const endDate = dayjs(this.configService.values.LICENSE_END, 'DD-MM-YYYY'); - - await this._setMonthlyStats(startDate, endDate); - - const currentConfig = { - startDate: startDate.toDate(), - endDate: endDate.toDate(), - }; - const promises = [ - firstValueFrom(this._licenseReportService.licenseReport(currentConfig)), - firstValueFrom(this._licenseReportService.licenseReport({})), - ]; - - if (endDate.isBefore(dayjs())) { - const unlicensedConfig = { - startDate: endDate.toDate(), - }; - promises.push(firstValueFrom(this._licenseReportService.licenseReport(unlicensedConfig))); - } - - Promise.all(promises).then(reports => { - [this.currentInfo, this.totalInfo, this.unlicensedInfo] = reports; - this._loadingService.stop(); - this.analysisPercentageOfLicense = - this.totalLicensedNumberOfPages > 0 - ? (this.currentInfo.numberOfAnalyzedPages / this.totalLicensedNumberOfPages) * 100 - : 100; - }); - } - - sendMail(): void { - const licenseCustomer = this.configService.values.LICENSE_CUSTOMER; - const subject = this._translateService.instant('license-info-screen.email.title', { - licenseCustomer, - }); - const lineBreak = '%0D%0A'; - const body = [ - this._translateService.instant('license-info-screen.email.body.analyzed', { - pages: this.currentInfo.numberOfAnalyzedPages, - }), - this._translateService.instant('license-info-screen.email.body.licensed', { - pages: this.totalLicensedNumberOfPages, - }), - ].join(lineBreak); - window.location.href = `mailto:${this.configService.values.LICENSE_EMAIL}?subject=${subject}&body=${body}`; - } - - private async _setMonthlyStats(startDate: dayjs.Dayjs, endDate: dayjs.Dayjs) { - const [startMonth, startYear] = [startDate.month(), startDate.year()]; - const [endMonth, endYear] = [endDate.month(), endDate.year()]; - - let m: number = startMonth; - let y: number = startYear; - const totalLicensedSeries = []; - const cumulativePagesSeries = []; - const promises = []; - - while (m <= endMonth && y <= endYear) { - totalLicensedSeries.push({ - name: `${dayjs.monthsShort()[m]} ${y}`, - value: this.totalLicensedNumberOfPages, - }); - - let nm = m + 1; - let ny = y; - if (nm === 12) { - nm = 0; - ny++; - } - - promises.push( - firstValueFrom( - this._licenseReportService.licenseReport({ - startDate: dayjs(`01-${m + 1}-${y}`, 'DD-M-YYYY').toDate(), - endDate: dayjs(`01-${nm + 1}-${ny}`, 'DD-M-YYYY').toDate(), - }), - ), - ); - - y = ny; - m = nm; - } - - const reports = await Promise.all(promises); - - m = startMonth; - y = startYear; - let cumulativePages = 0; - this.barChart = []; - for (const report of reports) { - cumulativePages += report.numberOfAnalyzedPages; - this.barChart.push({ - name: `${dayjs.monthsShort()[m]} ${y}`, - value: report.numberOfAnalyzedPages, - }); - cumulativePagesSeries.push({ - name: `${dayjs.monthsShort()[m]} ${y}`, - value: cumulativePages, - }); - m++; - if (m === 12) { - m = 0; - y++; - } - } - - this.lineChartSeries = [ - { - name: this._translateService.instant('license-info-screen.chart.licensed-total'), - series: totalLicensedSeries, - }, - { - name: this._translateService.instant('license-info-screen.chart.cumulative'), - series: cumulativePagesSeries, - }, - ]; - } -} diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.html b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.html similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.html rename to apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.html diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.scss rename to apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.scss diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.ts similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.ts rename to apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.ts diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-series-vertical.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/combo-chart/combo-series-vertical.component.ts rename to apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/index.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/index.ts similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/combo-chart/index.ts rename to apps/red-ui/src/app/modules/admin/screens/license/combo-chart/index.ts diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/models.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/models.ts similarity index 100% rename from apps/red-ui/src/app/modules/admin/components/combo-chart/models.ts rename to apps/red-ui/src/app/modules/admin/screens/license/combo-chart/models.ts diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.html b/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.html new file mode 100644 index 000000000..8da1b9488 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.html @@ -0,0 +1,18 @@ + diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.ts new file mode 100644 index 000000000..107353d82 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.ts @@ -0,0 +1,127 @@ +import { Component } from '@angular/core'; +import { ComboBarScheme, LICENSE_STORAGE_KEY, LineChartScheme } from '../utils/constants'; +import dayjs from 'dayjs'; +import { TranslateService } from '@ngx-translate/core'; +import { ILicenseReport } from '@red/domain'; +import { LicenseService } from '../services/license.service'; +import { IDateRange } from '../utils/date-range'; +import { ILicense } from '../utils/license'; +import { switchMap, tap } from 'rxjs/operators'; +import { ILineChartSeries } from '../combo-chart/models'; +import { LoadingService } from '@iqser/common-ui'; +import { generateDateRanges, isCurrentMonth, toDate } from '../utils/functions'; + +const monthNames = dayjs.monthsShort(); + +@Component({ + selector: 'redaction-license-chart', + templateUrl: './license-chart.component.html', + styleUrls: ['./license-chart.component.scss'], +}) +export class LicenseChartComponent { + readonly lineChartScheme = LineChartScheme; + readonly comboBarScheme = ComboBarScheme; + + lineChartSeries$ = this.#licenseChartSeries$; + barChart: any[]; + + constructor( + private readonly _translateService: TranslateService, + private readonly _licenseService: LicenseService, + private readonly _loadingService: LoadingService, + ) {} + + get #licenseChartSeries$() { + return this._licenseService.selectedLicense$.pipe( + tap(() => this._loadingService.start()), + switchMap(license => this.#setMonthlyStats(license)), + tap(() => this._loadingService.stop()), + ); + } + + async #setMonthlyStats(licence: ILicense): Promise { + const startDate = dayjs(licence.validFrom); + const endDate = dayjs(licence.validUntil); + const startMonth: number = startDate.month(); + const startYear: number = startDate.year(); + + const dateRanges = generateDateRanges(startMonth, startYear, endDate.month(), endDate.year()); + const reports = await this.#getReports(dateRanges); + + return [ + { + name: this._translateService.instant('license-info-screen.chart.licensed-total'), + series: this.#totalLicensedPagesSeries(dateRanges), + }, + { + name: this._translateService.instant('license-info-screen.chart.cumulative'), + series: this.#setBarChartAndGetCumulativePageSeries(startMonth, startYear, reports), + }, + ]; + } + + #setBarChartAndGetCumulativePageSeries(month: number, year: number, reports: ILicenseReport[]) { + let cumulativePages = 0; + const cumulativePagesSeries = []; + this.barChart = []; + + for (const report of reports) { + cumulativePages += report.numberOfAnalyzedPages; + + const name = `${monthNames[month]} ${year}`; + this.barChart.push({ + name, + value: report.numberOfAnalyzedPages, + }); + + cumulativePagesSeries.push({ + name, + value: cumulativePages, + }); + + month++; + if (month === 12) { + month = 0; + year++; + } + } + + return cumulativePagesSeries; + } + + #getReports(dateRanges: IDateRange[]) { + const reports = dateRanges.map(dateRange => { + const startMonth = dateRange.startMonth + 1; + const endMonth = dateRange.endMonth + 1; + + const key = `${startMonth}.${dateRange.startYear}-${endMonth}.${dateRange.endYear}`; + const existingReport = this._licenseService.storedReports[key]; + if (existingReport) { + return existingReport; + } + + const startDate = toDate(startMonth, dateRange.startYear); + const endDate = toDate(endMonth, dateRange.endYear); + const requestedReport = this._licenseService.getReport({ startDate, endDate }); + return requestedReport.then(report => this.#storeReportIfNotCurrentMonth(dateRange, report, key)); + }); + + return Promise.all(reports); + } + + #storeReportIfNotCurrentMonth(dateRange: IDateRange, report: ILicenseReport, key: string) { + if (!isCurrentMonth(dateRange.startMonth + 1, dateRange.startYear)) { + this._licenseService.storedReports[key] = report; + localStorage.setItem(LICENSE_STORAGE_KEY, JSON.stringify(this._licenseService.storedReports)); + } + + return report; + } + + #totalLicensedPagesSeries(dateRanges: IDateRange[]) { + return dateRanges.map(dateRange => ({ + name: `${monthNames[dateRange.startMonth]} ${dateRange.startYear}`, + value: this._licenseService.totalLicensedNumberOfPages, + })); + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html similarity index 69% rename from apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.html rename to apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html index 684b6c4dd..1d4e9834f 100644 --- a/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html @@ -1,8 +1,4 @@
-
- - -
-
-
-
{{ configService.values.LICENSE_CUSTOMER || '-' }}
-
- -
-
+
+
- {{ configService.values.LICENSE_START || '-' }} / - {{ configService.values.LICENSE_END || '-' }} +
+ +
+
+
{{ selectedLicense.licensedTo || '-' }}
+
+ +
+
+
+ {{ (selectedLicense.validFrom | date: 'dd-MM-YYYY') || '-' }} / + {{ (selectedLicense.validUntil | date: 'dd-MM-YYYY') || '-' }} +
+
+
+
{{ 'license-info-screen.licensed-page-count' | translate }}
{{ totalLicensedNumberOfPages }}
@@ -86,23 +91,7 @@
- + diff --git a/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.scss similarity index 100% rename from apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.scss rename to apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.scss diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts new file mode 100644 index 000000000..8f080de93 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit } from '@angular/core'; +import { ConfigService } from '@services/config.service'; +import { TranslateService } from '@ngx-translate/core'; +import { ButtonConfig, IconButtonTypes, LoadingService } from '@iqser/common-ui'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { UserService } from '@services/user.service'; +import { RouterHistoryService } from '@services/router-history.service'; +import { LicenseService } from '../services/license.service'; +import { ILicenseReport } from '@red/domain'; +import dayjs from 'dayjs'; +import { ILicense } from '../utils/license'; +import { UserPreferenceService } from '../../../../../services/user-preference.service'; + +@Component({ + templateUrl: './license-screen.component.html', + styleUrls: ['./license-screen.component.scss'], +}) +export class LicenseScreenComponent implements OnInit { + readonly currentYear = new Date().getFullYear(); + readonly currentUser = this._userService.currentUser; + readonly buttonConfigs: readonly ButtonConfig[] = [ + { + label: _('license-info-screen.email-report'), + action: (): void => this.sendMail(), + type: IconButtonTypes.primary, + }, + ]; + + currentInfo: ILicenseReport = {}; + totalInfo: ILicenseReport = {}; + unlicensedInfo: ILicenseReport = {}; + analysisPercentageOfLicense = 100; + totalLicensedNumberOfPages = this.licenseService.totalLicensedNumberOfPages; + + constructor( + readonly configService: ConfigService, + readonly userPreferenceService: UserPreferenceService, + readonly licenseService: LicenseService, + private readonly _userService: UserService, + private readonly _loadingService: LoadingService, + readonly routerHistoryService: RouterHistoryService, + private readonly _translateService: TranslateService, + ) { + _loadingService.start(); + } + + ngOnInit() { + return this.loadLicenceData(this.licenseService.selectedLicense); + } + + async loadLicenceData(license: ILicense) { + const startDate = dayjs(license.validFrom); + const endDate = dayjs(license.validUntil); + + const currentConfig = { + startDate: startDate.toDate(), + endDate: endDate.toDate(), + }; + const reports: Promise[] = [this.licenseService.getReport(currentConfig), this.licenseService.getReport({})]; + + if (endDate.isBefore(dayjs())) { + const unlicensedConfig = { + startDate: endDate.toDate(), + }; + reports.push(this.licenseService.getReport(unlicensedConfig)); + } + + [this.currentInfo, this.totalInfo, this.unlicensedInfo] = await Promise.all(reports); + this.analysisPercentageOfLicense = + this.totalLicensedNumberOfPages > 0 ? (this.currentInfo.numberOfAnalyzedPages / this.totalLicensedNumberOfPages) * 100 : 100; + } + + sendMail(): void { + const licenseCustomer = this.licenseService.selectedLicense.licensedTo; + const subject = this._translateService.instant('license-info-screen.email.title', { + licenseCustomer, + }); + const lineBreak = '%0D%0A'; + const body = [ + this._translateService.instant('license-info-screen.email.body.analyzed', { + pages: this.currentInfo.numberOfAnalyzedPages, + }), + this._translateService.instant('license-info-screen.email.body.licensed', { + pages: this.totalLicensedNumberOfPages, + }), + ].join(lineBreak); + const mail = this.licenseService.selectedLicense.licensedToEmail; + window.location.href = `mailto:${mail}?subject=${subject}&body=${body}`; + } + + licenceChanged(license: ILicense) { + this.totalLicensedNumberOfPages = this.licenseService.totalLicensedNumberOfPages; + return this.loadLicenceData(license); + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.html b/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.html new file mode 100644 index 000000000..8e0ee84ac --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.html @@ -0,0 +1,21 @@ +
+ + + + + + + + + +
+ + +
+ {{ license.name }} +
+
+ {{ getStatus(license.id) | translate | uppercase }} +
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.scss new file mode 100644 index 000000000..5d71f0eab --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.scss @@ -0,0 +1,15 @@ +.green { + background: var(--iqser-green-2); +} + +.space-between { + justify-content: space-between; +} + +.dot { + position: relative; +} + +.small-label { + font-weight: 600; +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.ts new file mode 100644 index 000000000..6c088558d --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.ts @@ -0,0 +1,31 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { LicenseService } from '../services/license.service'; +import { ILicense } from '../utils/license'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; + +const translations = { + active: _('license-info-screen.status.active'), + inactive: _('license-info-screen.status.inactive'), +} as const; + +@Component({ + selector: 'redaction-license-select', + templateUrl: './license-select.component.html', + styleUrls: ['./license-select.component.scss'], +}) +export class LicenseSelectComponent { + @Output() readonly valueChanges = new EventEmitter(); + + value = this.licenseService.getActiveLicense(); + + constructor(readonly licenseService: LicenseService) {} + + getStatus(id) { + return id === this.licenseService.activeLicenseId ? translations.active : translations.inactive; + } + + licenseChanged($event: ILicense) { + this.valueChanges.emit($event); + this.licenseService.selectedLicense$.next($event); + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license.module.ts b/apps/red-ui/src/app/modules/admin/screens/license/license.module.ts new file mode 100644 index 000000000..dbe83e1b0 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/license.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { LicenseScreenComponent } from './license-screen/license-screen.component'; +import { LicenseSelectComponent } from './license-select/license-select.component'; +import { LicenseChartComponent } from './license-chart/license-chart.component'; +import { LicenseService } from './services/license.service'; +import { RouterModule, Routes } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatSelectModule } from '@angular/material/select'; +import { IqserListingModule } from '@iqser/common-ui'; +import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { ComboChartComponent, ComboSeriesVerticalComponent } from './combo-chart'; +import { FormsModule } from '@angular/forms'; + +const routes: Routes = [ + { + path: '', + component: LicenseScreenComponent, + }, +]; + +@NgModule({ + declarations: [ + LicenseScreenComponent, + LicenseSelectComponent, + LicenseChartComponent, + ComboChartComponent, + ComboSeriesVerticalComponent, + ], + imports: [RouterModule.forChild(routes), TranslateModule, MatSelectModule, FormsModule, NgxChartsModule, IqserListingModule], + providers: [LicenseService], +}) +export class LicenseModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/services/license.service.ts b/apps/red-ui/src/app/modules/admin/screens/license/services/license.service.ts new file mode 100644 index 000000000..d3081f9da --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/services/license.service.ts @@ -0,0 +1,81 @@ +import { Injectable, Injector } from '@angular/core'; +import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; +import { ILicenseReport, ILicenseReportRequest } from '@red/domain'; +import { BehaviorSubject, firstValueFrom } from 'rxjs'; +import { ILicense, ILicenses, LICENSE_DATA } from '../utils/license'; +import { ConfigService } from '../../../../../services/config.service'; +import dayjs from 'dayjs'; +import { getStoredReports } from '../utils/functions'; + +@Injectable() +export class LicenseService extends GenericService { + storedReports = getStoredReports(); + readonly licenseData = this.#licenceData; + readonly activeLicenseId = this.licenseData.activeLicense; + readonly selectedLicense$ = new BehaviorSubject(this.getActiveLicense()); + + constructor(protected readonly _injector: Injector, private readonly _configService: ConfigService) { + super(_injector, 'report'); + } + + get selectedLicense() { + return this.selectedLicense$.value; + } + + get totalLicensedNumberOfPages() { + const processingPagesFeature = this.selectedLicense$.value.features.find(f => f.name === 'processingPages'); + return Number(processingPagesFeature.value ?? '0'); + } + + get #licenceData(): ILicenses { + return { + ...LICENSE_DATA, + licenses: [ + ...LICENSE_DATA.licenses, + { + id: 'guid-0', + name: this._configService.values.LICENSE_CUSTOMER, + product: 'RedactManager', + licensedTo: this._configService.values.LICENSE_CUSTOMER, + licensedToEmail: this._configService.values.LICENSE_EMAIL, + validFrom: dayjs(this._configService.values.LICENSE_START, 'DD-MM-YYYY').toISOString(), + validUntil: dayjs(this._configService.values.LICENSE_END, 'DD-MM-YYYY').toISOString(), + features: [ + { + name: 'pdftron', + type: 'STRING', + value: 'base64 encoded pdftron webviewer license key', + }, + { + name: 'processingPages', + type: 'NUMBER', + value: this._configService.values.LICENSE_PAGE_COUNT.toString(), + }, + ], + }, + ], + }; + } + + getActiveLicense() { + return this.licenseData.licenses.find(license => license.id === this.activeLicenseId); + } + + @Validate() + getReport$(@RequiredParam() body: ILicenseReportRequest, limit?: number, offset?: number) { + const queryParams: QueryParam[] = []; + if (limit) { + queryParams.push({ key: 'limit', value: limit }); + } + + if (offset) { + queryParams.push({ key: 'offset', value: offset }); + } + + return this._post(body, `${this._defaultModelPath}/license`, queryParams); + } + + getReport(body: ILicenseReportRequest, limit?: number, offset?: number) { + return firstValueFrom(this.getReport$(body, limit, offset)); + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/utils/constants.ts b/apps/red-ui/src/app/modules/admin/screens/license/utils/constants.ts new file mode 100644 index 000000000..a139c9732 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/utils/constants.ts @@ -0,0 +1,17 @@ +import { Color, ScaleType } from '@swimlane/ngx-charts'; + +export const ComboBarScheme: Color = { + name: 'Combo bar scheme', + selectable: true, + group: ScaleType.Ordinal, + domain: ['#0389ec'], +}; + +export const LineChartScheme: Color = { + name: 'Line chart scheme', + selectable: true, + group: ScaleType.Ordinal, + domain: ['#dd4d50', '#5ce594', '#0389ec'], +}; + +export const LICENSE_STORAGE_KEY = 'redaction-license-reports'; diff --git a/apps/red-ui/src/app/modules/admin/screens/license/utils/date-range.ts b/apps/red-ui/src/app/modules/admin/screens/license/utils/date-range.ts new file mode 100644 index 000000000..c18962cd6 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/utils/date-range.ts @@ -0,0 +1,6 @@ +export interface IDateRange { + readonly startMonth: number; + readonly startYear: number; + readonly endMonth: number; + readonly endYear: number; +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/utils/functions.ts b/apps/red-ui/src/app/modules/admin/screens/license/utils/functions.ts new file mode 100644 index 000000000..4e445c7ae --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/utils/functions.ts @@ -0,0 +1,41 @@ +import dayjs from 'dayjs'; +import { IDateRange } from './date-range'; +import { LICENSE_STORAGE_KEY } from './constants'; +import { ILicenseReport } from '@red/domain'; + +export function toDate(month: number, year: number) { + return dayjs(`01-${month}-${year}`, 'DD-M-YYYY').toDate(); +} + +export function generateDateRanges(month: number, year: number, endMonth: number, endYear: number) { + const dates: IDateRange[] = []; + + while (month <= endMonth && year <= endYear) { + let nextMonth = month + 1; + let nextYear = year; + if (nextMonth === 12) { + nextMonth = 0; + nextYear++; + } + + dates.push({ startMonth: month, startYear: year, endMonth: nextMonth, endYear: nextYear }); + + year = nextYear; + month = nextMonth; + } + + return dates; +} + +export function getStoredReports() { + const rawStoredReports = localStorage.getItem(LICENSE_STORAGE_KEY); + return JSON.parse(rawStoredReports ?? '{}') as Record; +} + +export function isCurrentMonth(month: number, year: number) { + const now = dayjs(); + const currentMonth = now.month() + 1; + const currentYear = now.year(); + + return month === currentMonth && year === currentYear; +} diff --git a/apps/red-ui/src/app/modules/admin/screens/license/utils/license.ts b/apps/red-ui/src/app/modules/admin/screens/license/utils/license.ts new file mode 100644 index 000000000..63cf5b4bf --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license/utils/license.ts @@ -0,0 +1,69 @@ +export interface ILicenseFeature { + readonly name: string; + readonly type: string; + readonly value: string; +} + +export interface ILicense { + readonly id: string; + readonly name: string; + readonly product: string; + readonly licensedTo: string; + readonly licensedToEmail: string; + readonly validFrom: string; + readonly validUntil: string; + readonly features: readonly ILicenseFeature[]; +} + +export interface ILicenses { + readonly activeLicense: string; + readonly licenses: readonly ILicense[]; +} + +export const LICENSE_DATA: ILicenses = { + activeLicense: 'guid-0', + licenses: [ + { + id: 'guid-1', + name: '1 Year comulative (2022)', + product: 'RedactManager', + licensedTo: 'Customer company name 1', + licensedToEmail: 'customer@example.com', + validFrom: '2022-01-01T00:00:00.000Z', + validUntil: '2022-12-31T23:59:59.999Z', + features: [ + { + name: 'pdftron', + type: 'STRING', + value: 'base64 encoded pdftron webviewer license key', + }, + { + name: 'processingPages', + type: 'NUMBER', + value: '200000', + }, + ], + }, + { + id: 'guid-2', + name: '2 Year comulative (2021)', + product: 'RedactManager', + licensedTo: 'Customer company name 2', + licensedToEmail: 'customer@example.com', + validFrom: '2021-01-01T00:00:00.000Z', + validUntil: '2021-12-31T23:59:59.999Z', + features: [ + { + name: 'pdftron', + type: 'STRING', + value: 'base64 encoded pdftron webviewer license key', + }, + { + name: 'processingPages', + type: 'NUMBER', + value: '100000', + }, + ], + }, + ], +}; diff --git a/apps/red-ui/src/app/modules/admin/services/licence-report.service.ts b/apps/red-ui/src/app/modules/admin/services/licence-report.service.ts deleted file mode 100644 index fa1be5312..000000000 --- a/apps/red-ui/src/app/modules/admin/services/licence-report.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Injectable, Injector } from '@angular/core'; -import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; -import { ILicenseReport, ILicenseReportRequest } from '@red/domain'; - -@Injectable() -export class LicenseReportService extends GenericService { - constructor(protected readonly _injector: Injector) { - super(_injector, 'report'); - } - - @Validate() - licenseReport(@RequiredParam() body: ILicenseReportRequest, limit?: number, offset?: number) { - const queryParams: QueryParam[] = []; - if (limit) { - queryParams.push({ key: 'limit', value: limit }); - } - - if (offset) { - queryParams.push({ key: 'offset', value: offset }); - } - - return this._post(body, `${this._defaultModelPath}/license`, queryParams); - } -} diff --git a/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts b/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts index 32bdf41f4..285cc8f30 100644 --- a/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/shared/shared-admin.module.ts @@ -4,13 +4,15 @@ import { SharedModule } from '@shared/shared.module'; import { DossierTemplateActionsComponent } from './components/dossier-template-actions/dossier-template-actions.component'; import { AddEditEntityComponent } from './components/add-edit-entity/add-edit-entity.component'; import { ColorPickerModule } from 'ngx-color-picker'; +import { RouterModule } from '@angular/router'; const components = [DossierTemplateActionsComponent, AddEditEntityComponent]; +const modules = [CommonModule, SharedModule]; + @NgModule({ declarations: [...components], - exports: [...components], - providers: [], - imports: [CommonModule, SharedModule, ColorPickerModule], + exports: [...components, ...modules], + imports: [...modules, RouterModule, ColorPickerModule], }) export class SharedAdminModule {} diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.html b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.html index d9c5384a8..e7f72dec8 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.html +++ b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.html @@ -23,7 +23,7 @@ [tooltip]="'dossier-details.edit-owner' | translate" class="ml-14" icon="iqser:edit" - iqserHelpMode="edit_dossier_owner" + iqserHelpMode="dashboard_in_dossier" tooltipPosition="below" > @@ -43,7 +43,7 @@ [strokeWidth]="15" [subtitle]="'dossier-overview.dossier-details.charts.documents-in-dossier' | translate" direction="row" - helpModeKey="filter_for_status" + helpModeKey="dashboard_in_dossier" > @@ -51,11 +51,7 @@ -
+
new NestedFilter(filter)); } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html index f2305bd2a..8fd005da3 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.html @@ -83,6 +83,8 @@ [type]="iconButtonTypes.dark" icon="iqser:trash" id="deleteDossier" + iqserHelpMode="edit_dossier_delete_dossier" + [dialogElement]="true" >
diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts index 56584737c..cd2d059e1 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts @@ -40,8 +40,7 @@ export class FileActionsComponent implements OnChanges { @Input() dossier: Dossier; @Input() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow'; @Input() maxWidth: number; - @Input() fileActionsHelpModeKey: 'document_features' | 'editor_document_features' = 'document_features'; - + @Input() fileActionsHelpModeKey: 'document_features_in_dossier' | 'document_features_in_editor' = 'document_features_in_dossier'; toggleTooltip?: string; assignTooltip?: string; buttonType?: CircleButtonType; diff --git a/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html b/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html index 9dda105cf..68a0473cf 100644 --- a/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html +++ b/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.html @@ -6,7 +6,7 @@ [scrollableParentView]="scrollableParentView" [tooltip]="(currentUser.isManager ? 'dossier-listing.edit.action' : 'dossier-listing.dossier-info.action') | translate" [type]="circleButtonTypes.dark" - iqserHelpMode="edit_dossier" + iqserHelpMode="edit_dossier_dossier_info" >
-
+
@@ -21,7 +20,6 @@ [tooltip]="'file-preview.highlights.remove' | translate" [type]="circleButtonTypes.dark" icon="iqser:trash" - iqserHelpMode="remove_highlights" tooltipPosition="above" >
diff --git a/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts index 03ed3c9e6..72fa76200 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts @@ -552,13 +552,19 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha return entry; } - private _loadDocument(blob: Blob, file: File) { + private async _loadDocument(blob: Blob, file: File) { const onError = () => { this._loadingService.stop(); this._errorService.set(DocLoadingError); this.stateService.reloadBlob(); }; - this.instance.UI.loadDocument(blob, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError }); + + const pdfNet = this.instance.Core.PDFNet; + + await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null); + const document = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer()); + await document.flattenAnnotations(false); + this.instance.UI.loadDocument(document, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError }); this._pageRotationService.clearRotationsHideActions(); } diff --git a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html index e42a8a9c1..1d761a465 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/user-management/user-management.component.html @@ -35,7 +35,7 @@ [tooltip]="assignTooltip$ | async" icon="iqser:edit" tooltipPosition="below" - iqserHelpMode="assign_reviewer" + iqserHelpMode="document_features_in_editor" > {{ 'file-preview.standard' | translate }} @@ -15,7 +15,7 @@ [disabled]="(canSwitchToDeltaView$ | async) === false" [matTooltip]="'file-preview.delta-tooltip' | translate" class="red-tab" - iqserHelpMode="delta_view" + iqserHelpMode="view" > {{ 'file-preview.delta' | translate }} @@ -26,7 +26,7 @@ [disabled]="(canSwitchToRedactedView$ | async) === false" [matTooltip]="'file-preview.redacted-tooltip' | translate" class="red-tab" - iqserHelpMode="preview_view" + iqserHelpMode="view" > {{ 'file-preview.redacted' | translate }} @@ -37,7 +37,7 @@ [disabled]="(canSwitchToHighlightsView$ | async) === false" [matTooltip]="'file-preview.text-highlights-tooltip' | translate" class="red-tab" - iqserHelpMode="highlights_view" + iqserHelpMode="view" > {{ 'file-preview.text-highlights' | translate }} diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.html b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.html index 355e6d228..4f2586b60 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.html +++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.html @@ -23,7 +23,7 @@ @@ -103,6 +103,8 @@ diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts index 5a9595bff..d8f9bbe6f 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts @@ -14,6 +14,8 @@ import { OnAttach, OnDetach, processFilters, + ScrollableParentView, + ScrollableParentViews, shareDistinctLast, } from '@iqser/common-ui'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; @@ -52,6 +54,17 @@ import { NGXLogger } from 'ngx-logger'; import { StampService } from './services/stamp.service'; import Annotation = Core.Annotations.Annotation; +const HelpModeKeys = { + redaction: 'redaction_text', + 'manual-redaction': 'redaction_text', + recommendation: 'recommendation', + skipped: 'skipped', + hint: 'hint_text', + 'hint-ocr': 'hint_picture', + 'hint-formula': 'picture', + 'hint-image': 'image', +}; + @Component({ templateUrl: './file-preview-screen.component.html', styleUrls: ['./file-preview-screen.component.scss'], @@ -486,6 +499,19 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni this.handleAnnotationSelected([]); } + get scrollableParentView(): ScrollableParentView { + return ScrollableParentViews.ANNOTATIONS_LIST; + } + + getActionsHelpModeKey(annotation: AnnotationWrapper): string { + const type = annotation?.typeLabel?.split('.')[1]; + const typeValue = annotation?.typeValue; + if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) { + return HelpModeKeys[`${type}-${typeValue}`]; + } + return HelpModeKeys[type]; + } + private _setExcludedPageStyles() { const file = this._filesMapService.get(this.dossierId, this.fileId); setTimeout(() => { diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts index bd202f432..8a3ee9bc8 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts @@ -501,19 +501,26 @@ export class AnnotationActionsService { } private _getFalsePositiveText(annotation: AnnotationWrapper) { - if (annotation.canBeMarkedAsFalsePositive) { - let text: string; - if (annotation.hasTextAfter) { - text = getFirstRelevantTextPart(annotation.textAfter, 'FORWARD'); - return text ? (annotation.value + text).trim() : annotation.value; - } - if (annotation.hasTextAfter) { - text = getFirstRelevantTextPart(annotation.textBefore, 'BACKWARD'); - return text ? (text + annotation.value).trim() : annotation.value; - } else { - return annotation.value; - } + if (!annotation.canBeMarkedAsFalsePositive) { + return; } + + if (annotation.isRecommendation) { + return annotation.value; + } + + let text: string; + if (annotation.hasTextAfter) { + text = getFirstRelevantTextPart(annotation.textAfter, 'FORWARD'); + return text ? (annotation.value + text).trim() : annotation.value; + } + + if (annotation.hasTextBefore) { + text = getFirstRelevantTextPart(annotation.textBefore, 'BACKWARD'); + return text ? (text + annotation.value).trim() : annotation.value; + } + + return annotation.value; } private _convertPath(path: string): string { diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html index 3ad717745..f9b9c24fb 100644 --- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html +++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.html @@ -5,13 +5,13 @@ [attr.aria-expanded]="btn.ariaExpanded && btn.ariaExpanded | async" [disabled]="btn.disabled" [icon]="btn.icon" - [iqserHelpMode]="helpModeKey" - [scrollableParentView]="scrollableParentView" [showDot]="btn.showDot" [tooltipClass]="btn.tooltipClass" [tooltipPosition]="tooltipPosition" [tooltip]="btn.tooltip | translate" [type]="btn.buttonType || buttonType" + [iqserHelpMode]="helpModeKey" + [scrollableParentView]="scrollableParentView" > @@ -19,11 +19,11 @@ *ngIf="btn.type === 'downloadBtn'" [dossier]="btn.dossier" [files]="btn.files" - [iqserHelpMode]="helpModeKey" - [scrollableParentView]="scrollableParentView" [tooltipClass]="btn.tooltipClass" [tooltipPosition]="tooltipPosition" [type]="buttonType" + [iqserHelpMode]="helpModeKey" + [scrollableParentView]="scrollableParentView" > @@ -33,10 +33,10 @@ (click)="$event.stopPropagation()" [checked]="btn.checked" [disabled]="btn.disabled" - [iqserHelpMode]="helpModeKey" [matTooltipPosition]="tooltipPosition" [matTooltip]="btn.tooltip | translate" [ngClass]="btn.class" + [iqserHelpMode]="helpModeKey" [scrollableParentView]="scrollableParentView" color="primary" > diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts index 9e59249c4..07ae9c81c 100644 --- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts @@ -17,7 +17,7 @@ export class ExpandableFileActionsComponent implements OnChanges { @Input() actions: Action[]; @Input() buttonType: CircleButtonType; @Input() tooltipPosition: IqserTooltipPosition; - @Input() helpModeKey: 'document_features' | 'editor_document_features'; + @Input() helpModeKey: 'document_features_in_dossier' | 'document_features_in_editor'; displayedButtons: Action[]; hiddenButtons: Action[]; @@ -30,7 +30,7 @@ export class ExpandableFileActionsComponent implements OnChanges { ) {} get scrollableParentView(): ScrollableParentView { - return this.helpModeKey === 'document_features' ? ScrollableParentViews.VIRTUAL_SCROLL : undefined; + return this.helpModeKey === 'document_features_in_dossier' ? ScrollableParentViews.VIRTUAL_SCROLL : undefined; } ngOnChanges(changes: SimpleChanges) { diff --git a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts index ffa553406..94ffb130e 100644 --- a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts @@ -26,7 +26,7 @@ export class SimpleDoughnutChartComponent implements OnChanges, OnInit { @Input() totalType: 'sum' | 'count' | 'simpleLabel' = 'sum'; @Input() counterText: string; @Input() filterKey = 'statusFilters'; - @Input() helpModeKey: 'filter_for_status'; + @Input() helpModeKey: 'dashboard_in_dossier'; filtersEnabled: boolean; chartData: any[] = []; diff --git a/apps/red-ui/src/app/translations/annotation-types-translations.ts b/apps/red-ui/src/app/translations/annotation-types-translations.ts index 9def2207e..289292d8f 100644 --- a/apps/red-ui/src/app/translations/annotation-types-translations.ts +++ b/apps/red-ui/src/app/translations/annotation-types-translations.ts @@ -1,7 +1,11 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { SuperType, SuperTypes } from '@models/file/super-types'; -export const annotationTypesTranslations: { [key in SuperType]: string } = { +export const SuggestionAddFalsePositive = 'suggestion-add-false-positive' as const; + +type TranslationKey = SuperType & typeof SuggestionAddFalsePositive; + +export const annotationTypesTranslations: Record = { [SuperTypes.TextHighlight]: _('annotation-type.text-highlight'), [SuperTypes.DeclinedSuggestion]: _('annotation-type.declined-suggestion'), [SuperTypes.Hint]: _('annotation-type.hint'), @@ -19,4 +23,5 @@ export const annotationTypesTranslations: { [key in SuperType]: string } = { [SuperTypes.SuggestionRemove]: _('annotation-type.suggestion-remove'), [SuperTypes.SuggestionRemoveDictionary]: _('annotation-type.suggestion-remove-dictionary'), [SuperTypes.SuggestionResize]: _('annotation-type.suggestion-resize'), + [SuggestionAddFalsePositive]: _('annotation-type.suggestion-add-false-positive'), } as const; diff --git a/apps/red-ui/src/assets/help-mode/links.json b/apps/red-ui/src/assets/help-mode/links.json index 6cd868047..b77c4b8c3 100644 --- a/apps/red-ui/src/assets/help-mode/links.json +++ b/apps/red-ui/src/assets/help-mode/links.json @@ -1,30 +1,12 @@ { - "assign_reviewer": { - "en": "/en/index-en.html?contextId=assign_reviewer", - "de": "", - "it": "", - "fr": "" - }, "bulk_select_annotations": { "en": "/en/index-en.html?contextId=bulk_select_annotations", "de": "", "it": "", "fr": "" }, - "delta_view": { - "en": "/en/index-en.html?contextId=delta_view", - "de": "", - "it": "", - "fr": "" - }, - "document_features": { - "en": "/en/index-en.html?contextId=document_features", - "de": "", - "it": "", - "fr": "" - }, - "edit_dossier": { - "en": "/en/index-en.html?contextId=edit_dossier", + "document_features_in_dossier": { + "en": "/en/index-en.html?contextId=document_features_in_dossier", "de": "", "it": "", "fr": "" @@ -41,14 +23,8 @@ "it": "", "fr": "" }, - "edit_dossier_owner": { - "en": "/en/index-en.html?contextId=edit_dossier_owner", - "de": "", - "it": "", - "fr": "" - }, - "redaction_edit_reason": { - "en": "/en/index-en.html?contextId=redaction_edit_reason", + "dashboard_in_dossier": { + "en": "/en/index-en.html?contextId=dashboard_in_dossier", "de": "", "it": "", "fr": "" @@ -65,12 +41,6 @@ "it": "", "fr": "" }, - "filter_for_editing_notes": { - "en": "/en/index-en.html?contextId=filter_for_editing_notes", - "de": "", - "it": "", - "fr": "" - }, "filter_for_status": { "en": "/en/index-en.html?contextId=filter_for_status", "de": "", @@ -101,104 +71,8 @@ "it": "", "fr": "" }, - "preview_view": { - "en": "/en/index-en.html?contextId=delta_view", - "de": "", - "it": "", - "fr": "" - }, - "highlights_view": { - "en": "/en/index-en.html?contextId=highlights_view", - "de": "", - "it": "", - "fr": "" - }, - "convert_highlights": { - "en": "/en/index-en.html?contextId=convert_highlights", - "de": "", - "it": "", - "fr": "" - }, - "remove_highlights": { - "en": "/en/index-en.html?contextId=remove_highlights", - "de": "", - "it": "", - "fr": "" - }, - "recommendation_accept_or_reject": { - "en": "/en/index-en.html?contextId=recommendation_accept_or_reject", - "de": "", - "it": "", - "fr": "" - }, - "redaction_false_positive": { - "en": "/en/index-en.html?contextId=redaction_false_positive", - "de": "", - "it": "", - "fr": "" - }, - "recommendation_false_positive": { - "en": "/en/index-en.html?contextId=recommendation_false_positive", - "de": "", - "it": "", - "fr": "" - }, - "skipped_remove_from_dictionary": { - "en": "/en/index-en.html?contextId=skipped_remove_from_dictionary", - "de": "", - "it": "", - "fr": "" - }, - "hint_remove_from_dictionary": { - "en": "/en/index-en.html?contextId=hint_remove_from_dictionary", - "de": "", - "it": "", - "fr": "" - }, - "recommendation_remove_from_dictionary": { - "en": "/en/index-en.html?contextId=recommendation_remove_from_dictionary", - "de": "", - "it": "", - "fr": "" - }, - "redaction_remove_only_here": { - "en": "/en/index-en.html?contextId=redaction_remove_only_here", - "de": "", - "it": "", - "fr": "" - }, - "hint_remove_only_here": { - "en": "/en/index-en.html?contextId=hint_remove_only_here", - "de": "", - "it": "", - "fr": "" - }, - "hint_recategorize": { - "en": "/en/index-en.html?contextId=hint_recategorize", - "de": "", - "it": "", - "fr": "" - }, - "hint_hide": { - "en": "/en/index-en.html?contextId=hint_hide", - "de": "", - "it": "", - "fr": "" - }, - "hint_force_hint": { - "en": "/en/index-en.html?contextId=hint_force_hint", - "de": "", - "it": "", - "fr": "" - }, - "redaction_recategorize": { - "en": "/en/index-en.html?contextId=redaction_recategorize", - "de": "", - "it": "", - "fr": "" - }, - "redaction_hide": { - "en": "/en/index-en.html?contextId=redaction_hide", + "view": { + "en": "/en/index-en.html?contextId=view", "de": "", "it": "", "fr": "" @@ -215,12 +89,6 @@ "it": "", "fr": "" }, - "standard_view": { - "en": "/en/index-en.html?contextId=standard_view", - "de": "", - "it": "", - "fr": "" - }, "workload_filter": { "en": "/en/index-en.html?contextId=workload_filter", "de": "", @@ -233,18 +101,6 @@ "it": "", "fr": "" }, - "delete_document_filter": { - "en": "/en/index-en.html?contextId=delete_document_filter", - "de": "", - "it": "", - "fr": "" - }, - "delete_dossier_filter": { - "en": "", - "de": "", - "it": "", - "fr": "" - }, "edit_dossier_in_dossier": { "en": "/en/index-en.html?contextId=edit_dossier_in_dossier", "de": "", @@ -257,32 +113,8 @@ "it": "", "fr": "" }, - "recommendation_resize": { - "en": "/en/index-en.html?contextId=recommendation_resize", - "de": "", - "it": "", - "fr": "" - }, - "hint_resize": { - "en": "/en/index-en.html?contextId=hint_resize", - "de": "", - "it": "", - "fr": "" - }, - "skipped_force_redaction": { - "en": "/en/index-en.html?contextId=skipped_force_redaction", - "de": "", - "it": "", - "fr": "" - }, - "editor_document_features": { - "en": "/en/index-en.html?contextId=editor_document_features", - "de": "", - "it": "", - "fr": "" - }, - "documents_quickfilter": { - "en": "/en/index-en.html?contextId=documents_quickfilter", + "document_features_in_editor": { + "en": "/en/index-en.html?contextId=document_features_in_editor", "de": "", "it": "", "fr": "" @@ -305,14 +137,8 @@ "it": "", "fr": "" }, - "dossiers_scroll_up_button": { - "en": "/en/index-en.html?contextId=dossiers_scroll_up_and_down", - "de": "", - "it": "", - "fr": "" - }, - "dossiers_scroll_down_button": { - "en": "/en/index-en.html?contextId=dossiers_scroll_up_and_down", + "scroll_dossier_list": { + "en": "/en/index-en.html?contextId=scroll_dossier_list", "de": "", "it": "", "fr": "" @@ -335,6 +161,18 @@ "it": "", "fr": "" }, + "edit_dossier_delete_dossier": { + "en": "/en/index-en.html?contextId=edit_dossier_delete_dossier", + "de": "", + "it": "", + "fr": "" + }, + "edit_dossier_archive_dossier": { + "en": "/en/index-en.html?contextId=edit_dossier_archive_dossier", + "de": "", + "it": "", + "fr": "" + }, "edit_dossier_download_package": { "en": "/en/index-en.html?contextId=edit_dossier_download_package", "de": "", @@ -389,26 +227,44 @@ "it": "", "fr": "" }, - "redaction_remove_from_dictionary": { - "en": "/en/index-en.html?contextId=redaction_remove_from_dictionary", + "redaction_text": { + "en": "/en/index-en.html?contextId=redaction_text", "de": "", "it": "", "fr": "" }, - "skipped_resize": { - "en": "/en/index-en.html?contextId=skipped_resize_redaction", + "recommendation": { + "en": "/en/index-en.html?contextId=recommendation", "de": "", "it": "", "fr": "" }, - "skipped_recategorize": { - "en": "/en/index-en.html?contextId=skipped_recategorize_redaction", + "skipped": { + "en": "/en/index-en.html?contextId=skipped", "de": "", "it": "", "fr": "" }, - "skipped_hide": { - "en": "/en/index-en.html?contextId=skipped_hide", + "hint_text": { + "en": "/en/index-en.html?contextId=hint_text", + "de": "", + "it": "", + "fr": "" + }, + "hint_picture": { + "en": "/en/index-en.html?contextId=hint_picture", + "de": "", + "it": "", + "fr": "" + }, + "picture": { + "en": "/en/index-en.html?contextId=picture", + "de": "", + "it": "", + "fr": "" + }, + "image": { + "en": "/en/index-en.html?contextId=image", "de": "", "it": "", "fr": "" diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json index ed05f1c84..9b8ce8cdb 100644 --- a/apps/red-ui/src/assets/i18n/de.json +++ b/apps/red-ui/src/assets/i18n/de.json @@ -344,6 +344,7 @@ "skipped": "Übersprungen", "suggestion-add": "Vorschlag für Schwärzung", "suggestion-add-dictionary": "Vorschlag für neuen Wörterbucheintrag", + "suggestion-add-false-positive": "", "suggestion-change-legal-basis": "Vorschlag für Änderung der Rechtsgrundlage", "suggestion-force-hint": "", "suggestion-force-redaction": "Vorschlag für erzwungene Schwärzung", @@ -1549,11 +1550,16 @@ }, "end-user-license-text": "Die Nutzung dieses Produkts unterliegt den Bedingungen der Endbenutzer-Lizenzvereinbarung für den RedactManager, sofern darin nichts anderweitig festgelegt.", "end-user-license-title": "Endbenutzer-Lizenzvereinbarung", + "license-title": "", "licensed-page-count": "Anzahl der lizenzierten Seiten", "licensed-to": "Lizenziert für", "licensing-details": "Lizenzdetails", "licensing-period": "Laufzeit der Lizenz", "ocr-analyzed-pages": "Mit OCR konvertierte Seiten", + "status": { + "active": "Aktiv", + "inactive": "" + }, "total-analyzed": "Seit {date} insgesamt analysierte Seiten", "unlicensed-analyzed": "Über Lizenz hinaus analysierte Seiten", "usage-details": "Nutzungsdetails" diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 7bb13a438..2df6a0b00 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -344,6 +344,7 @@ "skipped": "Skipped", "suggestion-add": "Suggested redaction", "suggestion-add-dictionary": "Suggested dictionary add", + "suggestion-add-false-positive": "Suggested add to false positive", "suggestion-change-legal-basis": "Suggested change legal basis", "suggestion-force-hint": "Suggestion force hint", "suggestion-force-redaction": "Suggestion force redaction", @@ -1549,11 +1550,16 @@ }, "end-user-license-text": "The use of this product is subject to the terms of the Redaction End User Agreement, unless otherwise specified therein.", "end-user-license-title": "End User License Agreement", + "license-title": "License Title", "licensed-page-count": "Number of licensed pages", "licensed-to": "Licensed to", "licensing-details": "Licensing Details", "licensing-period": "Licensing Period", "ocr-analyzed-pages": "OCR Analyzed Pages", + "status": { + "active": "Active", + "inactive": "Inactive" + }, "total-analyzed": "Total Analyzed Pages Since {date}", "unlicensed-analyzed": "Unlicensed Analyzed Pages", "usage-details": "Usage Details" diff --git a/apps/red-ui/src/assets/styles/red-components.scss b/apps/red-ui/src/assets/styles/red-components.scss index e9f484584..78d3c5e9e 100644 --- a/apps/red-ui/src/assets/styles/red-components.scss +++ b/apps/red-ui/src/assets/styles/red-components.scss @@ -98,3 +98,7 @@ } } } + +.align-center { + align-items: center; +} diff --git a/docker/common/nginx/nginx.conf b/docker/common/nginx/nginx.conf index 752051c54..73661a583 100644 --- a/docker/common/nginx/nginx.conf +++ b/docker/common/nginx/nginx.conf @@ -6,7 +6,7 @@ server { root /usr/share/nginx/html; # SSL stuff for cloudflare proxy-ing - ignores SSL certificate and uses SNI - add_header Content-Security-Policy "default-src 'self'; script-src 'self' data: 'unsafe-eval'; script-src-elem 'self' data: blob: 'unsafe-inline'; script-src-attr 'self' data:; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' blob: data: 'unsafe-eval' 'unsafe-inline'; script-src-elem 'self' data: blob: 'unsafe-inline'; script-src-attr 'self' data:; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"; proxy_ssl_verify off; proxy_read_timeout 1m; diff --git a/package.json b/package.json index 2a159797d..08771f626 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.451.0", + "version": "3.464.0", "private": true, "license": "MIT", "scripts": { diff --git a/paligo-styles/homepage.scss b/paligo-styles/homepage.scss index 7256c0b92..cfbe08a30 100644 --- a/paligo-styles/homepage.scss +++ b/paligo-styles/homepage.scss @@ -11,31 +11,33 @@ .publication-icon { background-color: variables.$red-1; } + + width: 280px; } -.portal-contents { +.featured-content-label { + margin-top: 24px; + text-align: center; +} + +.featured-content { margin-top: 24px; margin-bottom: 0; .inner { margin: 0; - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 24px; - - &::before { - content: none; - } + justify-content: center; } -} -.publication-contents { - @include mixin.card; -} + .publication-contents { + @include mixin.card; + width: 250px; + margin: 0 15px !important; -@media only screen and (max-width: 768px) { - .portal-contents .inner { - grid-template-columns: 1fr; + li a { + font-size: inherit; + line-height: inherit; + } } } diff --git a/paligo-theme.tar.gz b/paligo-theme.tar.gz index 279150e4a..ec2a79ac5 100644 Binary files a/paligo-theme.tar.gz and b/paligo-theme.tar.gz differ diff --git a/paligo-theme/paligo-styles/redacto-theme.css b/paligo-theme/paligo-styles/redacto-theme.css index 537362864..84db8fc87 100644 --- a/paligo-theme/paligo-styles/redacto-theme.css +++ b/paligo-theme/paligo-styles/redacto-theme.css @@ -59,6 +59,7 @@ } .portal-single-publication { background-color: transparent; + width: 280px; } .portal-single-publication > a { border-radius: 4px; @@ -67,78 +68,78 @@ background-color: #dd4d50; } -.portal-contents { +.featured-content-label { + margin-top: 24px; + text-align: center; +} + +.featured-content { margin-top: 24px; margin-bottom: 0; } -.portal-contents .inner { +.featured-content .inner { margin: 0; - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 24px; + justify-content: center; } -.portal-contents .inner::before { - content: none; -} - -.publication-contents { +.featured-content .publication-contents { padding: 24px 40px; border: 1px solid #e2e4e9; width: 100%; margin: 0; background-color: #fff; border-radius: 4px; + width: 250px; + margin: 0 15px !important; } -.publication-contents h4.featured-title, -.publication-contents .section-toc-title { +.featured-content .publication-contents h4.featured-title, +.featured-content .publication-contents .section-toc-title { margin: 0; } -.publication-contents h4.featured-title a, -.publication-contents .section-toc-title a { +.featured-content .publication-contents h4.featured-title a, +.featured-content .publication-contents .section-toc-title a { color: #283241; } -.publication-contents h4.featured-title a:hover, -.publication-contents .section-toc-title a:hover { +.featured-content .publication-contents h4.featured-title a:hover, +.featured-content .publication-contents .section-toc-title a:hover { color: #283241; text-decoration: underline; } -.publication-contents .section-toc-title { +.featured-content .publication-contents .section-toc-title { font-size: 28px; font-weight: 300; line-height: 36px; } -.publication-contents ul { +.featured-content .publication-contents ul { margin: 0; padding: 0; } -.publication-contents li { +.featured-content .publication-contents li { margin: 4px 0; } -.publication-contents li:first-child { +.featured-content .publication-contents li:first-child { margin-top: 20px; } -.publication-contents li:last-child { +.featured-content .publication-contents li:last-child { margin-bottom: 40px; } -.publication-contents li a { +.featured-content .publication-contents li a { color: #dd4d50; font-size: 16px; line-height: 24px; } -.publication-contents li a:hover { +.featured-content .publication-contents li a:hover { color: #dd4d50; text-decoration: underline; } -.publication-contents h4 span, -.publication-contents li::before { +.featured-content .publication-contents h4 span, +.featured-content .publication-contents li::before { display: none; } - -@media only screen and (max-width: 768px) { - .portal-contents .inner { - grid-template-columns: 1fr; - } +.featured-content .publication-contents li a { + font-size: inherit; + line-height: inherit; } + /* Einleitung */ .cat-panel-1:before { content: '\f277';