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 44ecafc01..4162dac19 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 @@ -17,6 +17,7 @@ import { DigitalSignatureScreenComponent } from './screens/digital-signature/dig import { AuditScreenComponent } from './screens/audit/audit-screen.component'; import { RouterModule } from '@angular/router'; import { SmtpConfigScreenComponent } from './screens/smtp-config/smtp-config-screen.component'; +import { ReportsScreenComponent } from './screens/reports/reports-screen.component'; const routes = [ { path: '', redirectTo: 'dossier-templates', pathMatch: 'full' }, @@ -81,6 +82,14 @@ const routes = [ routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard] } }, + { + path: 'reports', + component: ReportsScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard] + } + }, { path: 'default-colors', component: DefaultColorsScreenComponent, diff --git a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts b/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts index ad1ca3ca7..0c84c8ac6 100644 --- a/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts +++ b/apps/red-ui/src/app/modules/admin/admin-side-nav/admin-side-nav.component.ts @@ -33,7 +33,8 @@ export class AdminSideNavComponent { { screen: 'rules', onlyDevMode: true, label: 'rule-editor' }, { screen: 'default-colors' }, { screen: 'watermark' }, - { screen: 'file-attributes', onlyAdmin: true } + { screen: 'file-attributes', onlyAdmin: true }, + { screen: 'reports', onlyAdmin: true, onlyDevMode: true } ] }; 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 0099a80d0..47817f819 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -33,6 +33,7 @@ import { FileAttributesCsvImportDialogComponent } from './dialogs/file-attribute 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 { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'; +import { ReportsScreenComponent } from './screens/reports/reports-screen.component'; const dialogs = [ AddEditDossierTemplateDialogComponent, @@ -75,7 +76,7 @@ const components = [ ]; @NgModule({ - declarations: [...components], + declarations: [...components, ReportsScreenComponent], providers: [AdminDialogService], imports: [ CommonModule, diff --git a/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.html new file mode 100644 index 000000000..31ff344e5 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.html @@ -0,0 +1,86 @@ +
+ + +
+
+ + + +
+
+ +
+ +
+
+
+
+ +
+
Placeholders
+
Description
+ +
{{ getPlaceholderDisplayValue(placeholder) }}
+
+ What is and how to use it in your document. The readable content of a page + when looking at its layout must be nice. +
+
+
+
+ +
+
+
+ +
+ +
+ +
+
{{ template.fileName }}
+ +
+ + +
+
+
+
+
+ + + + diff --git a/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.scss new file mode 100644 index 000000000..73858e2ef --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.scss @@ -0,0 +1,113 @@ +@import '../../../../../assets/styles/red-variables'; +@import '../../../../../assets/styles/red-mixins'; + +.page-header .actions { + display: flex; + justify-content: flex-end; +} + +.content-container, +.right-container { + flex: 1; + padding: 30px; + overflow: auto; + @include scroll-bar; +} + +.right-container { + .header { + display: flex; + justify-content: space-between; + align-items: center; + padding-left: 8px; + margin-bottom: 8px; + } + + .template { + padding: 8px 10px; + background-color: $grey-6; + border-radius: 4px; + transition: background-color 0.2s; + position: relative; + + &:not(:last-child) { + margin-bottom: 2px; + } + + .name { + max-width: calc(100% - 100px); + @include line-clamp(1); + } + + .actions { + right: 16px; + position: absolute; + top: 6px; + display: none; + align-items: center; + + > *:not(:last-child) { + margin-right: 12px; + } + } + + &:hover { + background-color: $grey-4; + + .actions { + display: flex; + } + } + } + + .template.upload-button { + display: flex; + justify-content: center; + cursor: pointer; + } +} + +.content-container { + > .heading-xl, + > .description, + > .document-setup { + margin-bottom: 24px; + + > .all-caps-label { + margin-bottom: 8px; + } + } + + .placeholders { + width: calc(100% + 60px); + margin-left: -30px; + display: grid; + grid-template-columns: 1fr 2fr; + + .all-caps-label { + border-top: 1px solid $separator; + border-bottom: 1px solid $separator; + padding: 8px 32px; + + &:nth-child(2) { + padding: 8px 16px; + } + } + + .placeholder, + .description { + padding: 16px 16px; + border-bottom: 1px solid $separator; + } + + .placeholder { + font-weight: 600; + text-transform: capitalize; + margin-left: 16px; + } + + .description { + margin-right: 16px; + } + } +} diff --git a/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.ts new file mode 100644 index 000000000..459673a86 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen.component.ts @@ -0,0 +1,80 @@ +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { AppStateService } from '../../../../state/app-state.service'; +import { ReportTemplate, ReportTemplateControllerService } from '@redaction/red-ui-http'; +import { download } from '../../../../utils/file-download-utils'; +import { AdminDialogService } from '../../services/admin-dialog.service'; + +@Component({ + selector: 'redaction-reports-screen', + templateUrl: './reports-screen.component.html', + styleUrls: ['./reports-screen.component.scss'] +}) +export class ReportsScreenComponent implements OnInit { + viewReady = false; + placeholders: string[] = [ + 'report', + 'predefined placeholder 1', + 'signature 01', + 'new attribute' + ]; + availableTemplates: ReportTemplate[]; + + @ViewChild('fileInput') private _fileInput: ElementRef; + + constructor( + private readonly _activatedRoute: ActivatedRoute, + private readonly _appStateService: AppStateService, + private readonly _reportTemplateService: ReportTemplateControllerService, + private readonly _dialogService: AdminDialogService + ) { + this._appStateService.activateDossierTemplate( + _activatedRoute.snapshot.params.dossierTemplateId + ); + } + + getPlaceholderDisplayValue(placeholder: string): string { + return `{{${placeholder}}}`; + } + + async ngOnInit() { + await this._loadReportTemplates(); + this.viewReady = true; + } + + async uploadFile($event) { + this.viewReady = false; + const file = $event.target.files[0]; + + await this._reportTemplateService + .uploadTemplateForm(this._appStateService.activeDossierTemplateId, file) + .toPromise(); + + this._fileInput.nativeElement.value = null; + await this._loadReportTemplates(); + this.viewReady = true; + } + + async download(template: ReportTemplate) { + const data = await this._reportTemplateService + .downloadReportTemplate(template.dossierTemplateId, template.templateId, 'response') + .toPromise(); + download(data, template.fileName); + } + + deleteTemplate(template: ReportTemplate) { + this._dialogService.openDeleteReportTemplateDialog( + template.templateId, + template.dossierTemplateId, + async () => { + await this._loadReportTemplates(); + } + ); + } + + private async _loadReportTemplates() { + this.availableTemplates = await this._reportTemplateService + .getAvailableReportTemplates(this._appStateService.activeDossierTemplateId) + .toPromise(); + } +} diff --git a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts index 57219f61a..4b7e51e44 100644 --- a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts +++ b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts @@ -7,6 +7,7 @@ import { DossierTemplateModel, FileAttributeConfig, FileAttributesConfig, + ReportTemplateControllerService, SMTPConfigurationModel, TypeValue, User @@ -41,6 +42,7 @@ export class AdminDialogService { constructor( private readonly _dialog: MatDialog, private readonly _dossierTemplateControllerService: DossierTemplateControllerService, + private readonly _reportTemplateService: ReportTemplateControllerService, private readonly _dictionaryControllerService: DictionaryControllerService ) {} @@ -61,6 +63,23 @@ export class AdminDialogService { return ref; } + openDeleteReportTemplateDialog( + templateId: string, + dossierTemplateId: string, + cb?: Function + ): MatDialogRef { + const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig); + ref.afterClosed().subscribe(async result => { + if (result) { + await this._reportTemplateService + .deleteTemplate(dossierTemplateId, templateId) + .toPromise(); + if (cb) cb(); + } + }); + return ref; + } + openDeleteDossierTemplateDialog( dossierTemplate: DossierTemplateModel, cb?: () => void diff --git a/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts b/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts index 0f91f7d62..217215bf6 100644 --- a/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges } from '@angular/core'; import { UserService } from '@services/user.service'; import { User } from '@redaction/red-ui-http'; import { TranslateService } from '@ngx-translate/core'; diff --git a/apps/red-ui/src/app/utils/file-download-utils.ts b/apps/red-ui/src/app/utils/file-download-utils.ts index 697509c6a..8f793588c 100644 --- a/apps/red-ui/src/app/utils/file-download-utils.ts +++ b/apps/red-ui/src/app/utils/file-download-utils.ts @@ -11,6 +11,5 @@ export function download(event: HttpResponse, altName?: string) { } catch (e) { console.log('[REDACTION] Failed to parse content-disposition: ', contentDisposition); } - console.log('save'); saveAs(event.body, fileName ? fileName : altName); } diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 84b4c1d60..5627cc5e7 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -1005,7 +1005,16 @@ }, "title": "Watermark" }, + "reports-screen": { + "title": "Reports", + "description": "A short text explaining how to create report documents. It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.", + "document-setup-heading": "Document Setup", + "document-setup-description": "A short text explaining what placeholders are and how to use them in your report template. It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.", + "report-documents": "Report Documents", + "upload-document": "Upload a Document" + }, "dictionaries": "Dictionaries", + "reports": "Reports", "user-management": "User Management", "license-information": "License Information", "notifications": { diff --git a/libs/red-ui-http/src/lib/api/reportTemplateController.service.ts b/libs/red-ui-http/src/lib/api/reportTemplateController.service.ts index 8e4b4eaf5..2dbfea216 100644 --- a/libs/red-ui-http/src/lib/api/reportTemplateController.service.ts +++ b/libs/red-ui-http/src/lib/api/reportTemplateController.service.ts @@ -187,13 +187,14 @@ export class ReportTemplateControllerService { headers = headers.set('Accept', httpHeaderAcceptSelected); } - return this.httpClient.request( + return this.httpClient.request( 'get', `${this.basePath}/templateUpload/${encodeURIComponent( String(dossierTemplateId) )}/${encodeURIComponent(String(templateId))}`, { withCredentials: this.configuration.withCredentials, + responseType: 'blob', headers: headers, observe: observe, reportProgress: reportProgress diff --git a/libs/red-ui-http/src/lib/model/dossierTemplateModel.ts b/libs/red-ui-http/src/lib/model/dossierTemplateModel.ts index 2d671b22b..ea2ccecea 100644 --- a/libs/red-ui-http/src/lib/model/dossierTemplateModel.ts +++ b/libs/red-ui-http/src/lib/model/dossierTemplateModel.ts @@ -74,13 +74,11 @@ export namespace DossierTemplateModel { export type ReportTypesEnum = | 'EXCEL_MULTI_FILE' | 'EXCEL_SINGLE_FILE' - | 'WORD_SINGLE_FILE' | 'WORD_SINGLE_FILE_APPENDIX_A1_TEMPLATE' | 'WORD_SINGLE_FILE_APPENDIX_A2_TEMPLATE'; export const ReportTypesEnum = { EXCELMULTIFILE: 'EXCEL_MULTI_FILE' as ReportTypesEnum, EXCELSINGLEFILE: 'EXCEL_SINGLE_FILE' as ReportTypesEnum, - WORDSINGLEFILE: 'WORD_SINGLE_FILE' as ReportTypesEnum, WORDSINGLEFILEAPPENDIXA1TEMPLATE: 'WORD_SINGLE_FILE_APPENDIX_A1_TEMPLATE' as ReportTypesEnum, WORDSINGLEFILEAPPENDIXA2TEMPLATE: 'WORD_SINGLE_FILE_APPENDIX_A2_TEMPLATE' as ReportTypesEnum