From f9021361b07f79741473709f71218d9caff7319b Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Thu, 5 May 2022 17:20:45 +0300 Subject: [PATCH] RED-3765: wip license view --- .../src/app/modules/admin/admin.module.ts | 5 +- .../screens/license-information/constants.ts | 15 ++ .../screens/license-information/licence.ts | 69 ++++++ .../license-information-screen.component.html | 13 +- .../license-information-screen.component.ts | 216 ++++++++++-------- .../select-licence.component.html | 21 ++ .../select-licence.component.scss | 15 ++ .../select-licence.component.ts | 26 +++ .../admin/services/licence-report.service.ts | 17 +- apps/red-ui/src/assets/i18n/de.json | 5 + apps/red-ui/src/assets/i18n/en.json | 5 + .../src/assets/styles/red-components.scss | 4 + 12 files changed, 310 insertions(+), 101 deletions(-) create mode 100644 apps/red-ui/src/app/modules/admin/screens/license-information/constants.ts create mode 100644 apps/red-ui/src/app/modules/admin/screens/license-information/licence.ts create mode 100644 apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.html create mode 100644 apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.scss create mode 100644 apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.ts 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..19db479bd 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -33,7 +33,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 +48,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 { SelectLicenceComponent } from './screens/license-information/select-licence/select-licence.component'; const dialogs = [ AddEditDossierTemplateDialogComponent, @@ -103,8 +103,9 @@ const components = [ AddEditDossierStateDialogComponent, ConfirmDeleteDossierStateDialogComponent, TrashTableItemComponent, + SelectLicenceComponent, ], - providers: [AdminDialogService, AuditService, DigitalSignatureService, LicenseReportService, RulesService, SmtpConfigService], + providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, SmtpConfigService], imports: [CommonModule, SharedModule, AdminRoutingModule, SharedAdminModule, NgxChartsModule, ColorPickerModule, A11yModule], }) export class AdminModule {} diff --git a/apps/red-ui/src/app/modules/admin/screens/license-information/constants.ts b/apps/red-ui/src/app/modules/admin/screens/license-information/constants.ts new file mode 100644 index 000000000..766035b64 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license-information/constants.ts @@ -0,0 +1,15 @@ +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'], +}; diff --git a/apps/red-ui/src/app/modules/admin/screens/license-information/licence.ts b/apps/red-ui/src/app/modules/admin/screens/license-information/licence.ts new file mode 100644 index 000000000..51da73235 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license-information/licence.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-1', + 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/screens/license-information/license-information-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/license-information/license-information-screen.component.html index 684b6c4dd..947c16001 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-information/license-information-screen.component.html @@ -38,16 +38,23 @@
+
+
+
+ +
+
+
-
{{ configService.values.LICENSE_CUSTOMER || '-' }}
+
{{ selectedLicense.licensedTo || '-' }}
- {{ configService.values.LICENSE_START || '-' }} / - {{ configService.values.LICENSE_END || '-' }} + {{ (selectedLicense.validFrom | date: 'dd-MM-YYYY') || '-' }} / + {{ (selectedLicense.validUntil | date: 'dd-MM-YYYY') || '-' }}
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 index ae953b5d7..57703c432 100644 --- 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 @@ -5,18 +5,38 @@ 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 { LicenseService } 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'; +import { ComboBarScheme, LineChartScheme } from './constants'; +import { ILicense } from './licence'; + +const monthNames = dayjs.monthsShort(); + +function toDate(month: number, year: number) { + return dayjs(`01-${month}-${year}`, 'DD-M-YYYY').toDate(); +} + +interface IMonthYear { + readonly month: number; + readonly year: number; +} + +interface IDateRange { + readonly startDate: IMonthYear; + readonly endDate: IMonthYear; +} @Component({ selector: 'redaction-license-information-screen', templateUrl: './license-information-screen.component.html', styleUrls: ['./license-information-screen.component.scss'], + providers: [LicenseService], }) export class LicenseInformationScreenComponent implements OnInit { + readonly lineChartScheme = LineChartScheme; + readonly comboBarScheme = ComboBarScheme; + readonly currentYear = new Date().getFullYear(); readonly currentUser = this._userService.currentUser; readonly buttonConfigs: readonly ButtonConfig[] = [ { @@ -29,40 +49,35 @@ export class LicenseInformationScreenComponent implements OnInit { 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'], - }; + selectedLicense = this.licenseService.getActiveLicense(); + totalLicensedNumberOfPages = this.#processingPages; constructor( readonly configService: ConfigService, + readonly licenseService: LicenseService, 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(); + get #processingPages() { + const processingPagesFeature = this.selectedLicense.features.find(f => f.name === 'processingPages'); + return Number(processingPagesFeature.value ?? '0'); } - async ngOnInit() { - this.totalLicensedNumberOfPages = this.configService.values.LICENSE_PAGE_COUNT || 0; + ngOnInit() { + return this.loadLicenceData(); + } + + async loadLicenceData() { + // const startDate = dayjs(this.selectedLicense.validFrom, 'DD-MM-YYYY'); + // const endDate = dayjs(this.selectedLicense.validUntil, 'DD-MM-YYYY'); const startDate = dayjs(this.configService.values.LICENSE_START, 'DD-MM-YYYY'); const endDate = dayjs(this.configService.values.LICENSE_END, 'DD-MM-YYYY'); @@ -72,30 +87,23 @@ export class LicenseInformationScreenComponent implements OnInit { startDate: startDate.toDate(), endDate: endDate.toDate(), }; - const promises = [ - firstValueFrom(this._licenseReportService.licenseReport(currentConfig)), - firstValueFrom(this._licenseReportService.licenseReport({})), - ]; + const reports: Promise[] = [this.licenseService.getReport(currentConfig), this.licenseService.getReport({})]; if (endDate.isBefore(dayjs())) { const unlicensedConfig = { startDate: endDate.toDate(), }; - promises.push(firstValueFrom(this._licenseReportService.licenseReport(unlicensedConfig))); + reports.push(this.licenseService.getReport(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; - }); + [this.currentInfo, this.totalInfo, this.unlicensedInfo] = await Promise.all(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 licenseCustomer = this.selectedLicense.licensedTo; const subject = this._translateService.instant('license-info-screen.email.title', { licenseCustomer, }); @@ -108,77 +116,97 @@ export class LicenseInformationScreenComponent implements OnInit { pages: this.totalLicensedNumberOfPages, }), ].join(lineBreak); - window.location.href = `mailto:${this.configService.values.LICENSE_EMAIL}?subject=${subject}&body=${body}`; + window.location.href = `mailto:${this.selectedLicense.licensedToEmail}?subject=${subject}&body=${body}`; + } + + licenceChanged(license: ILicense) { + this.selectedLicense = license; + this.totalLicensedNumberOfPages = this.#processingPages; + return this.loadLicenceData(); } private async _setMonthlyStats(startDate: dayjs.Dayjs, endDate: dayjs.Dayjs) { - const [startMonth, startYear] = [startDate.month(), startDate.year()]; - const [endMonth, endYear] = [endDate.month(), endDate.year()]; + const startMonth: number = startDate.month(); + const startYear: number = startDate.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++; - } - } + const dateRanges = this.#generateDateRanges(startMonth, startYear, endDate.month(), endDate.year()); + const reports = await this.#getReports(dateRanges); this.lineChartSeries = [ { name: this._translateService.instant('license-info-screen.chart.licensed-total'), - series: totalLicensedSeries, + series: this.#totalLicensedPagesSeries(dateRanges), }, { name: this._translateService.instant('license-info-screen.chart.cumulative'), - series: cumulativePagesSeries, + 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 startDate = toDate(dateRange.startDate.month + 1, dateRange.startDate.year); + const endDate = toDate(dateRange.endDate.month + 1, dateRange.endDate.year); + return this.licenseService.getReport({ startDate, endDate }); + }); + + return Promise.all(reports); + } + + #totalLicensedPagesSeries(dateRanges: IDateRange[]) { + return dateRanges.map(dateRange => ({ + name: `${monthNames[dateRange.startDate.month]} ${dateRange.startDate.year}`, + value: this.totalLicensedNumberOfPages, + })); + } + + #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({ startDate: { month, year }, endDate: { month: nextMonth, year: nextYear } }); + + year = nextYear; + month = nextMonth; + } + + return dates; + } } diff --git a/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.html b/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.html new file mode 100644 index 000000000..e697dddfa --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.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-information/select-licence/select-licence.component.scss b/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.scss new file mode 100644 index 000000000..5d71f0eab --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.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-information/select-licence/select-licence.component.ts b/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.ts new file mode 100644 index 000000000..a033f39f1 --- /dev/null +++ b/apps/red-ui/src/app/modules/admin/screens/license-information/select-licence/select-licence.component.ts @@ -0,0 +1,26 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { LicenseService } from '../../../services/licence-report.service'; +import { ILicense } from '../licence'; +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-select-licence', + templateUrl: './select-licence.component.html', + styleUrls: ['./select-licence.component.scss'], +}) +export class SelectLicenceComponent { + @Output() readonly valueChanges = new EventEmitter(); + + value = this.licenseService.getActiveLicense(); + + constructor(readonly licenseService: LicenseService) {} + + getStatus(id) { + return id === this.licenseService.activeLicenseId ? translations.active : translations.inactive; + } +} 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 index fa1be5312..1e80657d7 100644 --- 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 @@ -1,15 +1,24 @@ import { Injectable, Injector } from '@angular/core'; import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui'; import { ILicenseReport, ILicenseReportRequest } from '@red/domain'; +import { firstValueFrom } from 'rxjs'; +import { LICENSE_DATA } from '../screens/license-information/licence'; @Injectable() -export class LicenseReportService extends GenericService { +export class LicenseService extends GenericService { + readonly licenseData = LICENSE_DATA; + readonly activeLicenseId = this.licenseData.activeLicense; + constructor(protected readonly _injector: Injector) { super(_injector, 'report'); } + getActiveLicense() { + return this.licenseData.licenses.find(license => license.id === this.activeLicenseId); + } + @Validate() - licenseReport(@RequiredParam() body: ILicenseReportRequest, limit?: number, offset?: number) { + getReport$(@RequiredParam() body: ILicenseReportRequest, limit?: number, offset?: number) { const queryParams: QueryParam[] = []; if (limit) { queryParams.push({ key: 'limit', value: limit }); @@ -21,4 +30,8 @@ export class LicenseReportService extends GenericService { 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/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json index 9ecf220fb..607e2a984 100644 --- a/apps/red-ui/src/assets/i18n/de.json +++ b/apps/red-ui/src/assets/i18n/de.json @@ -1532,11 +1532,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 498a5d53d..d532ecb08 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -1532,11 +1532,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; +}