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;
+}