diff --git a/apps/red-ui/project.json b/apps/red-ui/project.json
index 213866f34..c0f01719a 100644
--- a/apps/red-ui/project.json
+++ b/apps/red-ui/project.json
@@ -63,7 +63,7 @@
"stylePreprocessorOptions": {
"includePaths": ["./apps/red-ui/src/assets/styles", "./libs/common-ui/src/assets/styles"]
},
- "scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js"],
+ "scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js", "node_modules/chart.js/dist/chart.js"],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.html b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.html
deleted file mode 100644
index b1f569bc3..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.scss
deleted file mode 100644
index c2ef9aba1..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.scss
+++ /dev/null
@@ -1,103 +0,0 @@
-.ngx-charts {
- float: left;
- overflow: visible;
-
- .circle,
- .bar,
- .arc {
- cursor: pointer;
- }
-
- .bar,
- .cell,
- .arc,
- .card {
- &.active,
- &:hover {
- opacity: 0.8;
- transition: opacity 100ms ease-in-out;
- }
-
- &:focus {
- outline: none;
- }
-
- &.hidden {
- display: none;
- }
- }
-
- g {
- &:focus {
- outline: none;
- }
- }
-
- .line-series,
- .line-series-range,
- .area-series {
- &.inactive {
- transition: opacity 100ms ease-in-out;
- opacity: 0.2;
- }
- }
-
- .line-highlight {
- display: none;
-
- &.active {
- display: block;
- }
- }
-
- .area {
- opacity: 0.6;
- }
-
- .circle {
- &:hover {
- cursor: pointer;
- }
- }
-
- .label {
- font-size: 12px;
- font-weight: normal;
- }
-
- .tooltip-anchor {
- fill: var(--iqser-text);
- }
-
- .gridline-path {
- stroke: #ddd;
- stroke-width: 1;
- fill: none;
- }
-
- .grid-panel {
- rect {
- fill: none;
- }
-
- &.odd {
- rect {
- fill: rgba(0, 0, 0, 0.05);
- }
- }
- }
-
- fill: var(--iqser-text);
-}
-
-.chart-legend .legend-labels {
- background: var(--iqser-alt-background) !important;
-
- .legend-label .legend-label-text {
- color: var(--iqser-text) !important;
-
- &:hover {
- color: rgba(var(--iqser-text-rgb), 0.8) !important;
- }
- }
-}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.ts
deleted file mode 100644
index 58db4f019..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-chart.component.ts
+++ /dev/null
@@ -1,384 +0,0 @@
-import { Component, EventEmitter, HostListener, Input, Output, ViewEncapsulation } from '@angular/core';
-
-import { curveLinear } from 'd3-shape';
-import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
-import {
- BaseChartComponent,
- calculateViewDimensions,
- Color,
- ColorHelper,
- LegendPosition,
- Orientation,
- ScaleType,
- Series,
- StringOrNumberOrDate,
- ViewDimensions,
-} from '@swimlane/ngx-charts';
-
-@Component({
- // eslint-disable-next-line @angular-eslint/component-selector
- selector: 'combo-chart-component',
- templateUrl: './combo-chart.component.html',
- styleUrls: ['./combo-chart.component.scss'],
- encapsulation: ViewEncapsulation.None,
-})
-export class ComboChartComponent extends BaseChartComponent {
- @Input() curve: any = curveLinear;
- @Input() legend = false;
- @Input() legendTitle = 'Legend';
- @Input() legendPosition: LegendPosition = LegendPosition.Right;
- @Input() xAxis;
- @Input() yAxis;
- @Input() showXAxisLabel;
- @Input() showYAxisLabel;
- @Input() showRightYAxisLabel;
- @Input() xAxisLabel;
- @Input() yAxisLabel;
- @Input() yAxisLabelRight;
- @Input() tooltipDisabled = false;
- @Input() gradient: boolean;
- @Input() showGridLines = true;
- @Input() activeEntries: any[] = [];
- @Input() schemeType: ScaleType;
- @Input() yAxisTickFormatting: any;
- @Input() yRightAxisTickFormatting: any;
- @Input() roundDomains = false;
- @Input() colorSchemeLine: Color;
- @Input() autoScale;
- @Input() lineChart: Series[];
- @Input() yLeftAxisScaleFactor: any;
- @Input() yRightAxisScaleFactor: any;
- @Input() rangeFillOpacity: number;
- @Input() animations = true;
- @Input() noBarWhenZero = true;
- @Output() activate = new EventEmitter<{ value; entries: unknown[] }>();
- @Output() deactivate = new EventEmitter<{ value; entries: unknown[] }>();
- dims: ViewDimensions;
- xScale: any;
- yScale: any;
- xDomain: string[] | number[];
- yDomain: string[] | number[];
- transform: string;
- colors: ColorHelper;
- colorsLine: ColorHelper;
- margin = [10, 20, 10, 20];
- xAxisHeight = 0;
- yAxisWidth = 0;
- legendOptions: any;
- scaleType: ScaleType = ScaleType.Linear;
- xScaleLine;
- yScaleLine;
- xDomainLine;
- yDomainLine;
- seriesDomain;
- combinedSeries: Series[];
- xSet;
- filteredDomain;
- hoveredVertical;
- yOrientLeft: Orientation = Orientation.Left;
- yOrientRight: Orientation = Orientation.Right;
- legendSpacing = 0;
- bandwidth: number;
- barPadding = 8;
-
- @Input() xAxisTickFormatting: any;
-
- trackBy(_index, item): string {
- return item.name;
- }
-
- update(): void {
- super.update();
- this.dims = calculateViewDimensions({
- width: this.width,
- height: this.height,
- margins: this.margin,
- showXAxis: this.xAxis,
- showYAxis: this.yAxis,
- xAxisHeight: this.xAxisHeight,
- yAxisWidth: this.yAxisWidth,
- showXLabel: this.showXAxisLabel,
- showYLabel: this.showYAxisLabel,
- showLegend: this.legend,
- legendType: this.schemeType,
- legendPosition: this.legendPosition,
- });
-
- if (!this.yAxis) {
- this.legendSpacing = 0;
- } else if (this.showYAxisLabel && this.yAxis) {
- this.legendSpacing = 150;
- } else {
- this.legendSpacing = 40;
- }
- this.xScale = this.getXScale();
- this.yScale = this.getYScale();
-
- // line chart
- this.xDomainLine = this.getXDomainLine();
- if (this.filteredDomain) {
- this.xDomainLine = this.filteredDomain;
- }
-
- this.yDomainLine = this.getYDomainLine();
- this.seriesDomain = this.getSeriesDomain();
-
- this.scaleLines();
-
- this.setColors();
- this.legendOptions = this.getLegendOptions();
-
- this.transform = `translate(${this.dims.xOffset} , ${this.margin[0]})`;
- }
-
- deactivateAll() {
- this.activeEntries = [...this.activeEntries];
- for (const entry of this.activeEntries) {
- this.deactivate.emit({ value: entry, entries: [] });
- }
- this.activeEntries = [];
- }
-
- @HostListener('mouseleave')
- hideCircles(): void {
- this.hoveredVertical = null;
- this.deactivateAll();
- }
-
- updateHoveredVertical(item): void {
- this.hoveredVertical = item.value;
- this.deactivateAll();
- }
-
- scaleLines() {
- this.xScaleLine = this.getXScaleLine(this.xDomainLine, this.dims.width);
- this.yScaleLine = this.getYScaleLine(this.yDomainLine, this.dims.height);
- }
-
- getSeriesDomain(): any[] {
- this.combinedSeries = this.lineChart.slice(0);
- this.combinedSeries.push({
- name: this.yAxisLabel,
- series: this.results,
- });
- return this.combinedSeries.map(d => d.name);
- }
-
- isDate(value): value is Date {
- return value instanceof Date;
- }
-
- getScaleType(values): ScaleType {
- let date = true;
- let num = true;
-
- for (const value of values) {
- if (!this.isDate(value)) {
- date = false;
- }
-
- if (typeof value !== 'number') {
- num = false;
- }
- }
-
- if (date) {
- return ScaleType.Time;
- }
- if (num) {
- return ScaleType.Linear;
- }
- return ScaleType.Ordinal;
- }
-
- getXDomainLine(): any[] {
- let values: StringOrNumberOrDate[] = [];
-
- for (const results of this.lineChart) {
- for (const d of results.series) {
- if (!values.includes(d.name)) {
- values.push(d.name);
- }
- }
- }
-
- this.scaleType = this.getScaleType(values);
- let domain = [];
-
- if (this.scaleType === 'time') {
- const min = Math.min(...(values as number[]));
- const max = Math.max(...(values as number[]));
- domain = [min, max];
- } else if (this.scaleType === 'linear') {
- values = values.map(v => Number(v));
- const min = Math.min(...(values as number[]));
- const max = Math.max(...(values as number[]));
- domain = [min, max];
- } else {
- domain = values;
- }
-
- this.xSet = values;
- return domain;
- }
-
- getYDomainLine(): any[] {
- const domain: number[] = [];
-
- for (const results of this.lineChart) {
- for (const d of results.series) {
- if (domain.indexOf(d.value) < 0) {
- domain.push(d.value);
- }
- if (d.min !== undefined) {
- if (domain.indexOf(d.min) < 0) {
- domain.push(d.min);
- }
- }
- if (d.max !== undefined) {
- if (domain.indexOf(d.max) < 0) {
- domain.push(d.max);
- }
- }
- }
- }
-
- let min = Math.min(...domain);
- const max = Math.max(...domain);
- if (this.yRightAxisScaleFactor) {
- const minMax = this.yRightAxisScaleFactor(min, max);
- return [Math.min(0, minMax.min as number), minMax.max];
- }
- min = Math.min(0, min);
- return [min, max];
- }
-
- getXScaleLine(domain, width: number): any {
- let scale;
- if (this.bandwidth === undefined) {
- this.bandwidth = width - this.barPadding;
- }
- const offset = Math.floor((width + this.barPadding - (this.bandwidth + this.barPadding) * domain.length) / 2);
-
- if (this.scaleType === 'time') {
- scale = scaleTime().range([0, width]).domain(domain);
- } else if (this.scaleType === 'linear') {
- scale = scaleLinear().range([0, width]).domain(domain);
-
- if (this.roundDomains) {
- scale = scale.nice();
- }
- } else if (this.scaleType === 'ordinal') {
- scale = scalePoint()
- .range([offset + this.bandwidth / 2, width - offset - this.bandwidth / 2])
- .domain(domain);
- }
-
- return scale;
- }
-
- getYScaleLine(domain, height): any {
- const scale = scaleLinear().range([height, 0]).domain(domain);
- return this.roundDomains ? scale.nice() : scale;
- }
-
- getXScale(): any {
- this.xDomain = this.getXDomain();
- const spacing = this.xDomain.length / (this.dims.width / this.barPadding + 1);
- return scaleBand().range([0, this.dims.width]).paddingInner(spacing).domain(this.xDomain);
- }
-
- getYScale(): any {
- this.yDomain = this.getYDomain();
- const scale = scaleLinear().range([this.dims.height, 0]).domain(this.yDomain);
- return this.roundDomains ? scale.nice() : scale;
- }
-
- getXDomain(): any[] {
- return this.results.map(d => d.name);
- }
-
- getYDomain() {
- const values: number[] = this.results.map(d => d.value);
- const min = Math.min(0, ...values);
- const max = Math.max(...values);
- if (this.yLeftAxisScaleFactor) {
- const minMax = this.yLeftAxisScaleFactor(min, max);
- return [Math.min(0, minMax.min as number), minMax.max];
- }
- return [min, max];
- }
-
- onClick(data) {
- this.select.emit(data);
- }
-
- setColors(): void {
- let domain: number[] | string[];
- if (this.schemeType === 'ordinal') {
- domain = this.xDomain;
- } else {
- domain = this.yDomain;
- }
- this.colors = new ColorHelper(this.scheme, this.schemeType, domain, this.customColors);
- this.colorsLine = new ColorHelper(this.colorSchemeLine, this.schemeType, domain, this.customColors);
- }
-
- getLegendOptions() {
- const opts = {
- scaleType: this.schemeType,
- colors: undefined,
- domain: [],
- title: undefined,
- position: this.legendPosition,
- };
- if (opts.scaleType === 'ordinal') {
- opts.domain = this.seriesDomain;
- opts.colors = this.colorsLine;
- opts.title = this.legendTitle;
- } else {
- opts.domain = this.seriesDomain;
- opts.colors = this.colors.scale;
- }
- return opts;
- }
-
- getValuesMaxLength(index: number): number {
- const values = this.lineChart[index].series.map(s => s.value);
- return Math.max(...values).toString().length;
- }
-
- updateLineWidth(width): void {
- this.bandwidth = width;
- this.scaleLines();
- }
-
- updateYAxisWidth({ width }: { width: number }): void {
- this.yAxisWidth = width + 20;
- this.update();
- }
-
- updateXAxisHeight({ height }): void {
- this.xAxisHeight = height;
- this.update();
- }
-
- onActivate(item) {
- const idx = this.activeEntries.findIndex(d => d.name === item.name && d.value === item.value && d.series === item.series);
- if (idx > -1) {
- return;
- }
-
- this.activeEntries = [item, ...this.activeEntries];
- this.activate.emit({ value: item, entries: this.activeEntries });
- }
-
- onDeactivate(item) {
- const idx = this.activeEntries.findIndex(d => d.name === item.name && d.value === item.value && d.series === item.series);
-
- this.activeEntries.splice(idx, 1);
- this.activeEntries = [...this.activeEntries];
-
- this.deactivate.emit({ value: item, entries: this.activeEntries });
- }
-}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts
deleted file mode 100644
index ecc1f2f17..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/combo-series-vertical.component.ts
+++ /dev/null
@@ -1,200 +0,0 @@
-import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
-import { animate, style, transition, trigger } from '@angular/animations';
-import { Bar, BarOrientation, formatLabel, PlacementTypes, StyleTypes } from '@swimlane/ngx-charts';
-
-@Component({
- // eslint-disable-next-line @angular-eslint/component-selector
- selector: 'g[ngx-combo-charts-series-vertical]',
- template: `
-
- `,
- changeDetection: ChangeDetectionStrategy.OnPush,
- animations: [
- trigger('animationState', [
- transition('* => void', [
- style({
- opacity: 1,
- transform: '*',
- }),
- animate(500, style({ opacity: 0, transform: 'scale(0)' })),
- ]),
- ]),
- ],
-})
-export class ComboSeriesVerticalComponent implements OnChanges {
- @Input() dims;
- @Input() type = 'standard';
- @Input() series;
- @Input() seriesLine;
- @Input() xScale;
- @Input() yScale;
- @Input() colors;
- @Input() tooltipDisabled = false;
- @Input() gradient: boolean;
- @Input() activeEntries: any[];
- @Input() seriesName: string;
- @Input() animations = true;
- @Input() noBarWhenZero = true;
-
- @Output() activate = new EventEmitter();
- @Output() deactivate = new EventEmitter();
- @Output() bandwidth = new EventEmitter();
-
- bars: Bar[];
- x: any;
- y: any;
- readonly tooltipTypes = StyleTypes;
- readonly tooltipPlacements = PlacementTypes;
- readonly orientations = BarOrientation;
-
- ngOnChanges(): void {
- this.update();
- }
-
- update(): void {
- let width;
- if (this.series.length) {
- width = this.xScale.bandwidth();
- this.bandwidth.emit(width);
- }
-
- let d0 = 0;
- let total;
- if (this.type === 'normalized') {
- total = this.series.map(d => d.value).reduce((sum: number, d: number) => sum + d, 0);
- }
-
- this.bars = this.series.map((d, index) => {
- let value: number = d.value;
- const label = d.name;
- const formattedLabel = formatLabel(label);
- const roundEdges = this.type === 'standard';
-
- const bar: Bar = {
- value,
- label,
- roundEdges,
- data: d,
- width,
- formattedLabel,
- height: 0,
- x: 0,
- y: 0,
- ariaLabel: label,
- tooltipText: label,
- color: undefined,
- gradientStops: undefined,
- };
-
- let offset0 = d0;
- let offset1 = offset0 + value;
-
- if (this.type === 'standard') {
- bar.height = Math.abs(this.yScale(value) - this.yScale(0));
- bar.x = this.xScale(label);
-
- if (value < 0) {
- bar.y = this.yScale(0);
- } else {
- bar.y = this.yScale(value);
- }
- } else if (this.type === 'stacked') {
- d0 += value;
-
- bar.height = this.yScale(offset0) - this.yScale(offset1);
- bar.x = 0;
- bar.y = this.yScale(offset1);
- // bar.offset0 = offset0;
- // bar.offset1 = offset1;
- } else if (this.type === 'normalized') {
- d0 += value;
-
- if (total > 0) {
- offset0 = (offset0 * 100) / total;
- offset1 = (offset1 * 100) / total;
- } else {
- offset0 = 0;
- offset1 = 0;
- }
-
- bar.height = this.yScale(offset0) - this.yScale(offset1);
- bar.x = 0;
- bar.y = this.yScale(offset1);
- // bar.offset0 = offset0;
- // bar.offset1 = offset1;
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- value = (offset1 - offset0).toFixed(2) + '%';
- }
-
- if (this.colors.scaleType === 'ordinal') {
- bar.color = this.colors.getColor(label);
- } else {
- if (this.type === 'standard') {
- bar.color = this.colors.getColor(value);
- bar.gradientStops = this.colors.getLinearGradientStops(value);
- } else {
- bar.color = this.colors.getColor(offset1);
- bar.gradientStops = this.colors.getLinearGradientStops(offset1, offset0);
- }
- }
-
- let tooltipLabel = formattedLabel;
- if (this.seriesName) {
- tooltipLabel = `${this.seriesName} • ${formattedLabel}`;
- }
-
- this.getSeriesTooltips(this.seriesLine, index);
- const lineValue: string = this.seriesLine[0].series[index].value;
- bar.tooltipText = `
- ${tooltipLabel}
-
- Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}%
-
- `;
-
- return bar;
- });
- }
-
- getSeriesTooltips(seriesLine, index) {
- return seriesLine.map(d => d.series[index]);
- }
-
- isActive(entry): boolean {
- if (!this.activeEntries) {
- return false;
- }
- const item = this.activeEntries.find(d => entry.name === d.name && entry.series === d.series);
- return item !== undefined;
- }
-
- trackBy(_index: number, bar: Bar): string {
- return bar.label;
- }
-}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/index.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/index.ts
deleted file mode 100644
index 07ef678f5..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './combo-chart.component';
-export * from './combo-series-vertical.component';
-export * from './y-axis.component';
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/y-axis.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/y-axis.component.ts
deleted file mode 100644
index 3509f511f..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/combo-chart/y-axis.component.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core';
-import { Orientation, ViewDimensions, YAxisTicksComponent } from '@swimlane/ngx-charts';
-
-@Component({
- selector: 'g[red-ngx-charts-y-axis]',
- template: `
-
-
-
-
- `,
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class YAxisComponent implements OnChanges {
- @Input() yScale;
- @Input() dims: ViewDimensions;
- @Input() trimTicks: boolean;
- @Input() maxTickLength: number;
- @Input() tickFormatting;
- @Input() ticks: any[];
- @Input() showGridLines = false;
- @Input() showLabel: boolean;
- @Input() labelText: string;
- @Input() yAxisTickCount: any;
- @Input() yOrient: Orientation = Orientation.Left;
- @Input() referenceLines;
- @Input() showRefLines: boolean;
- @Input() showRefLabels: boolean;
- @Input() yAxisOffset = 0;
- @Input() valuesMaxLength = 0;
- @Output() dimensionsChanged = new EventEmitter();
-
- yAxisClassName = 'y axis';
- tickArguments: number[];
- offset: number;
- transform: string;
- labelOffset = 15;
- fill = 'none';
- stroke = '#CCC';
- tickStroke = '#CCC';
- strokeWidth = 1;
- padding = 5;
-
- @ViewChild(YAxisTicksComponent) ticksComponent: YAxisTicksComponent;
-
- ngOnChanges(changes: SimpleChanges): void {
- this.update();
- }
-
- update(): void {
- this.offset = -(this.yAxisOffset + this.padding);
- if (this.yOrient === Orientation.Right) {
- this.labelOffset = 65 + (this.valuesMaxLength + this.valuesMaxLength / 4) * 5;
- this.transform = `translate(${this.offset + this.dims.width} , 0)`;
- } else {
- this.transform = `translate(${this.offset} , 0)`;
- }
-
- if (this.yAxisTickCount !== undefined) {
- this.tickArguments = [this.yAxisTickCount];
- }
- }
-
- emitTicksWidth({ width }): void {
- if (width !== this.labelOffset && this.yOrient === Orientation.Right) {
- this.labelOffset = width + this.labelOffset + 300;
- setTimeout(() => {
- this.dimensionsChanged.emit({ width });
- }, 0);
- } else if (width !== this.labelOffset) {
- this.labelOffset = width + (this.valuesMaxLength + this.valuesMaxLength / 4) * 5;
- setTimeout(() => {
- this.dimensionsChanged.emit({ width });
- }, 0);
- }
- }
-}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.html b/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.html
new file mode 100644
index 000000000..0e417290e
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.scss
new file mode 100644
index 000000000..b59b59cec
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.scss
@@ -0,0 +1,8 @@
+:host {
+ margin: 0 auto;
+}
+
+.canvas-container {
+ position: relative;
+ width: 1000px;
+}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.ts
new file mode 100644
index 000000000..0faf36246
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/chart/chart.component.ts
@@ -0,0 +1,72 @@
+import { Component, Input, OnChanges } from '@angular/core';
+import { ChartConfiguration, ChartDataset } from 'chart.js';
+
+@Component({
+ selector: 'redaction-chart',
+ templateUrl: './chart.component.html',
+ styleUrls: ['./chart.component.scss'],
+})
+export class ChartComponent implements OnChanges {
+ @Input({ required: true }) datasets: ChartDataset[];
+ @Input({ required: true }) labels: string[];
+ @Input() ticksCallback?: (value: number) => string;
+ @Input() secondaryAxis = false;
+ @Input() yAxisLabel?: string;
+ @Input() yAxisLabelRight?: string;
+
+ chartData: ChartConfiguration['data'];
+ chartOptions: ChartConfiguration<'line'>['options'] = {};
+
+ constructor() {
+ this.#setChartOptions();
+ }
+
+ ngOnChanges() {
+ this.chartData = {
+ labels: this.labels,
+ datasets: this.datasets,
+ };
+ this.#setChartOptions();
+ }
+
+ #setChartOptions(): void {
+ this.chartOptions = {
+ scales: {
+ y: {
+ stacked: true,
+ ticks: { callback: this.ticksCallback ? (value: number) => this.ticksCallback(value) : undefined },
+ title: {
+ display: !!this.yAxisLabel,
+ text: this.yAxisLabel,
+ padding: { bottom: 20 },
+ },
+ },
+ y1: {
+ display: this.secondaryAxis,
+ position: 'right',
+ title: {
+ display: !!this.yAxisLabelRight,
+ text: this.yAxisLabelRight,
+ padding: { bottom: 20 },
+ },
+ },
+ },
+ plugins: {
+ legend: { position: 'right' },
+ tooltip: {
+ callbacks: {
+ label: this.ticksCallback ? item => `${item.dataset.label}: ${this.ticksCallback(item.parsed.y)}` : undefined,
+ },
+ },
+ },
+ layout: {
+ padding: {
+ top: 50,
+ bottom: 50,
+ },
+ },
+ datasets: { bar: { barPercentage: 0.7 } },
+ aspectRatio: 2.5,
+ };
+ }
+}
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/components/license-select/license-select.component.html
similarity index 100%
rename from apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.html
rename to apps/red-ui/src/app/modules/admin/screens/license/components/license-select/license-select.component.html
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/components/license-select/license-select.component.scss
similarity index 100%
rename from apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.scss
rename to apps/red-ui/src/app/modules/admin/screens/license/components/license-select/license-select.component.scss
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/components/license-select/license-select.component.ts
similarity index 100%
rename from apps/red-ui/src/app/modules/admin/screens/license/license-select/license-select.component.ts
rename to apps/red-ui/src/app/modules/admin/screens/license/components/license-select/license-select.component.ts
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.html b/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.html
new file mode 100644
index 000000000..87bd0b86b
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.html
@@ -0,0 +1,47 @@
+
+
+
+
+
{{ licenseService.currentLicenseReport.activeFilesUploadedBytes | size }}
+
+
+
+
+
{{ licenseService.currentLicenseReport.archivedFilesUploadedBytes | size }}
+
+
+
+
{{ licenseService.currentLicenseReport.trashFilesUploadedBytes | size }}
+
+
+
+
+
+ {{ licenseService.currentLicenseReport.totalFilesUploadedBytes | size }}
+
+ ({{ uploadedBytesCapacityPercentage | number : '1.0-2' }}%)
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.scss
new file mode 100644
index 000000000..c0a071ff5
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.scss
@@ -0,0 +1,38 @@
+:host {
+ display: contents;
+}
+
+.donut-chart-wrapper {
+ grid-row: 11 / span 4;
+ grid-column: 3;
+ width: fit-content;
+}
+
+.row {
+ display: contents;
+
+ > div {
+ padding: 8px 20px;
+
+ &:first-of-type {
+ font-weight: 600;
+ }
+ }
+
+ &:hover {
+ > div {
+ background-color: var(--iqser-alt-background);
+ }
+ }
+}
+
+.section-title {
+ grid-column: span 3;
+ padding: 20px 20px 8px;
+ margin-bottom: 8px;
+ border-bottom: 1px solid var(--iqser-separator);
+}
+
+redaction-chart {
+ grid-column: span 3;
+}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.ts
new file mode 100644
index 000000000..cce4f1eef
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/license-storage/license-storage.component.ts
@@ -0,0 +1,91 @@
+import { Component } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { size } from '@iqser/common-ui';
+import { LicenseService } from '@services/license.service';
+import { map, tap } from 'rxjs/operators';
+import type { DonutChartConfig, ILicenseReport } from '@red/domain';
+import { ChartDataset } from 'chart.js';
+import { ChartBlue, ChartGreen, ChartGrey, ChartRed } from '../../utils/constants';
+import { getLabelsFromMonthlyData, getLineConfig } from '../../utils/functions';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+
+@Component({
+ selector: 'red-license-storage',
+ templateUrl: './license-storage.component.html',
+ styleUrls: ['./license-storage.component.scss'],
+})
+export class LicenseStorageComponent {
+ readonly formatSize = size;
+ uploadedBytesCapacityPercentage = -1;
+ donutChartConfig: DonutChartConfig[] = [];
+ readonly data$ = this.licenseService.licenseData$.pipe(
+ map(() => this.licenseService.currentLicenseReport),
+ tap(license => {
+ this.uploadedBytesCapacityPercentage = this.#getUploadedBytesCapacityPercentage(license);
+ this.donutChartConfig = this.#getDonutChartConfig(license);
+ }),
+ map(license => ({
+ datasets: this.#getDatasets(license),
+ labels: getLabelsFromMonthlyData(license.monthlyData),
+ })),
+ );
+ readonly size = size;
+
+ constructor(readonly licenseService: LicenseService, private readonly _translateService: TranslateService) {}
+
+ #getUploadedBytesCapacityPercentage(license: ILicenseReport): number {
+ return this.licenseService.uploadedBytesCapacity
+ ? (license.totalFilesUploadedBytes / this.licenseService.uploadedBytesCapacity) * 100
+ : -1;
+ }
+
+ #getDonutChartConfig(license: ILicenseReport): DonutChartConfig[] {
+ return [
+ {
+ value: license.activeFilesUploadedBytes,
+ color: ChartGreen,
+ label: this._translateService.instant(_('license-info-screen.storage.active-documents')),
+ },
+ {
+ value: license.archivedFilesUploadedBytes,
+ color: ChartBlue,
+ label: this._translateService.instant(_('license-info-screen.storage.archived-documents')),
+ },
+ {
+ value: license.trashFilesUploadedBytes,
+ color: ChartRed,
+ label: this._translateService.instant(_('license-info-screen.storage.trash-documents')),
+ },
+ {
+ value: this.licenseService.uploadedBytesCapacity - license.totalFilesUploadedBytes,
+ color: ChartGrey,
+ label: this._translateService.instant(_('license-info-screen.storage.unused')),
+ },
+ ];
+ }
+
+ #getDatasets(license: ILicenseReport): ChartDataset[] {
+ const monthlyData = license.monthlyData;
+
+ return [
+ {
+ data: monthlyData.flatMap(d => d.activeFilesUploadedBytes),
+ label: this._translateService.instant('license-info-screen.storage.active-documents'),
+ ...getLineConfig(ChartGreen, 'origin'),
+ stack: 'storage',
+ },
+ {
+ data: monthlyData.flatMap(d => d.archivedFilesUploadedBytes),
+ label: this._translateService.instant('license-info-screen.storage.archived-documents'),
+ ...getLineConfig(ChartBlue, '-1'),
+ stack: 'storage',
+ },
+ {
+ data: monthlyData.flatMap(d => d.trashFilesUploadedBytes),
+ label: this._translateService.instant('license-info-screen.storage.trash-documents'),
+ ...getLineConfig(ChartRed, '-1'),
+ stack: 'storage',
+ },
+ ];
+ }
+}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.html b/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.html
new file mode 100644
index 000000000..dec058ea6
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+ {{ licenseService.analyzedPagesInCurrentLicensingPeriod }}
+ ({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%)
+
+
+
+
+
+
{{ licenseService.currentLicenseReport.numberOfOcrPages }}
+
+
+
+
+
{{ licenseService.unlicensedPages }}
+
+
+
+
+
+
{{ licenseService.allLicensesReport.numberOfAnalyzedPages }}
+
+
+
+
+
{{ licenseService.allLicensesReport.numberOfOcrPages }}
+
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.scss
new file mode 100644
index 000000000..2a35a4526
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.scss
@@ -0,0 +1,36 @@
+:host {
+ display: contents;
+}
+
+.row {
+ display: contents;
+
+ > div {
+ padding: 8px 20px;
+
+ &:first-of-type {
+ font-weight: 600;
+ }
+
+ &:nth-child(2) {
+ grid-column: span 2;
+ }
+ }
+
+ &:hover {
+ > div {
+ background-color: var(--iqser-alt-background);
+ }
+ }
+}
+
+.section-title {
+ grid-column: span 3;
+ padding: 20px 20px 8px;
+ margin-bottom: 8px;
+ border-bottom: 1px solid var(--iqser-separator);
+}
+
+redaction-chart {
+ grid-column: span 3;
+}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.ts b/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.ts
new file mode 100644
index 000000000..8f1524d3c
--- /dev/null
+++ b/apps/red-ui/src/app/modules/admin/screens/license/components/license-usage/license-usage.component.ts
@@ -0,0 +1,63 @@
+import { Component } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { LicenseService } from '@services/license.service';
+import { map } from 'rxjs/operators';
+import type { ILicenseReport } from '@red/domain';
+import { ChartDataset } from 'chart.js';
+import { ChartBlue, ChartGreen, ChartRed } from '../../utils/constants';
+import { getLabelsFromMonthlyData, getLineConfig } from '../../utils/functions';
+
+@Component({
+ selector: 'red-license-usage',
+ templateUrl: './license-usage.component.html',
+ styleUrls: ['./license-usage.component.scss'],
+})
+export class LicenseUsageComponent {
+ readonly analysisPercentageOfLicense$ = this.licenseService.selectedLicense$.pipe(map(() => this.getAnalysisPercentageOfLicense()));
+ readonly data$ = this.licenseService.selectedLicense$.pipe(
+ map(() => this.licenseService.currentLicenseReport),
+ map(license => ({
+ datasets: this.#getDatasets(license),
+ labels: getLabelsFromMonthlyData(license.monthlyData),
+ })),
+ );
+
+ constructor(readonly licenseService: LicenseService, private readonly _translateService: TranslateService) {}
+
+ getAnalysisPercentageOfLicense() {
+ const totalLicensedNumberOfPages = this.licenseService.totalLicensedNumberOfPages;
+ const numberOfAnalyzedPages = this.licenseService.analyzedPagesInCurrentLicensingPeriod;
+ return totalLicensedNumberOfPages > 0 ? (numberOfAnalyzedPages / totalLicensedNumberOfPages) * 100 : 100;
+ }
+
+ #getDatasets(license: ILicenseReport): ChartDataset[] {
+ const monthlyData = license.monthlyData;
+ return [
+ {
+ data: monthlyData.flatMap(d => d.numberOfAnalyzedPages),
+ label: 'Pages per Month',
+ type: 'bar',
+ backgroundColor: ChartBlue,
+ order: 2,
+ },
+ {
+ data: monthlyData.flatMap(() => 200000),
+ label: 'Total Pages',
+ ...getLineConfig(ChartRed, false),
+ yAxisID: 'y1',
+ order: 1,
+ },
+ {
+ data: monthlyData.map(
+ (month, monthIndex) =>
+ month.numberOfAnalyzedPages +
+ monthlyData.slice(0, monthIndex).reduce((acc, curr) => acc + curr.numberOfAnalyzedPages, 0),
+ ),
+ label: 'Cumulative Pages',
+ yAxisID: 'y1',
+ order: 1,
+ ...getLineConfig(ChartGreen, false),
+ },
+ ];
+ }
+}
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
deleted file mode 100644
index 8da1b9488..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.html
+++ /dev/null
@@ -1,18 +0,0 @@
-
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
deleted file mode 100644
index 23d20b594..000000000
--- a/apps/red-ui/src/app/modules/admin/screens/license/license-chart/license-chart.component.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { Component } from '@angular/core';
-import { ComboBarScheme, LICENSE_STORAGE_KEY, LineChartScheme } from '../utils/constants';
-import dayjs from 'dayjs';
-import { IDateRange, ILicense, ILicenseReport } from '@red/domain';
-import { LicenseService } from '@services/license.service';
-import { switchMap, tap } from 'rxjs/operators';
-import { List, LoadingService } from '@iqser/common-ui';
-import { isCurrentMonth, toDate, verboseDate } from '../utils/functions';
-import { TranslateService } from '@ngx-translate/core';
-import { Observable } from 'rxjs';
-import { Series } from '@swimlane/ngx-charts';
-
-@Component({
- selector: 'redaction-license-chart',
- templateUrl: './license-chart.component.html',
-})
-export class LicenseChartComponent {
- readonly lineChartScheme = LineChartScheme;
- readonly comboBarScheme = ComboBarScheme;
-
- readonly lineChartSeries$ = this.#licenseChartSeries$;
- barChart = [];
-
- get #licenseChartSeries$(): Observable {
- return this._licenseService.selectedLicense$.pipe(
- tap(() => this._loadingService.start()),
- switchMap(license => this.#getLicenseData(license)),
- tap(() => this._loadingService.stop()),
- );
- }
-
- constructor(
- private readonly _translateService: TranslateService,
- private readonly _licenseService: LicenseService,
- private readonly _loadingService: LoadingService,
- ) {}
-
- async #getLicenseData(license: ILicense): Promise {
- const startDate = dayjs(license.validFrom);
- const endDate = dayjs(license.validUntil);
- const startDay: number = startDate.date();
- const startMonth: number = startDate.month();
- const startYear: number = startDate.year();
-
- const dateRanges = [];
-
- for (let dt = startDate; dt <= endDate; dt = dt.add(1, 'month')) {
- const end = dt.add(1, 'month');
- dateRanges.push({ startMonth: dt.month(), startYear: dt.year(), endMonth: end.month(), endYear: end.year() });
- }
-
- if (dateRanges.length > 0) {
- dateRanges[0].startDay = startDay;
- }
- const reports = await this.#getReports(dateRanges, license.id);
-
- return this.#mapRangesToReports(startMonth, startYear, dateRanges, reports);
- }
-
- #mapRangesToReports(month: number, year: number, dateRanges: List, reports: List): Series[] {
- return [
- {
- name: this._translateService.instant('license-info-screen.chart.total-pages'),
- series: this.#totalLicensedPagesSeries(dateRanges),
- },
- {
- name: this._translateService.instant('license-info-screen.chart.cumulative'),
- series: this.#setBar(month, year, reports),
- },
- ];
- }
-
- #setBar(month: number, year: number, reports: List) {
- let cumulativePages = 0;
- const cumulativePagesSeries = [];
- this.barChart = [];
- const monthNames = dayjs.monthsShort();
-
- 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++;
- }
- }
-
- if (cumulativePages !== this._licenseService.currentLicenseInfo.numberOfAnalyzedPages) {
- this._licenseService.wipeStoredReportsAndReloadSelectedLicenseData();
- }
-
- return cumulativePagesSeries;
- }
-
- #getReports(dateRanges: List, id: string) {
- const reports = dateRanges.map(range => {
- const startMonth = range.startMonth + 1;
- const endMonth = range.endMonth + 1;
-
- const key = `${id}-${startMonth}.${range.startYear}-${endMonth}.${range.endYear}`;
- const existingReport = this._licenseService.storedReports[key];
- if (existingReport) {
- return existingReport;
- }
-
- const startDate = toDate(startMonth, range.startYear, range.startDay);
- const endDate = toDate(endMonth, range.endYear);
- const requestedReport = this._licenseService.getReport({ startDate, endDate });
- return requestedReport.then(report => this.#storeReportIfNotCurrentMonth(range, 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: List) {
- return dateRanges.map(dateRange => ({
- name: verboseDate(dateRange),
- value: this._licenseService.totalLicensedNumberOfPages,
- }));
- }
-}
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html
index 386d0f278..7a642fc4b 100644
--- a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html
+++ b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.html
@@ -61,65 +61,8 @@
{{ licenseService.totalLicensedNumberOfPages }}
-
-
-
-
-
-
-
{{ licenseService.currentLicenseInfo.activeFilesUploadedBytes | size }}
-
-
-
-
-
{{ licenseService.currentLicenseInfo.archivedFilesUploadedBytes | size }}
-
-
-
-
{{ licenseService.currentLicenseInfo.trashFilesUploadedBytes | size }}
-
-
-
-
-
{{ licenseService.currentLicenseInfo.totalFilesUploadedBytes | size }}
-
-
-
-
-
-
-
-
-
- {{ licenseService.analyzedPagesInCurrentLicensingPeriod }}
- ({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%)
-
-
-
-
-
-
{{ licenseService.currentLicenseInfo.numberOfOcrPages }}
-
-
-
-
-
{{ licenseService.unlicensedPages }}
-
-
-
-
-
-
{{ licenseService.allLicensesInfo.numberOfAnalyzedPages }}
-
-
-
-
-
{{ licenseService.allLicensesInfo.numberOfOcrPages }}
-
-
-
-
-
+
+
diff --git a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.scss
index a48358949..0320d1c0b 100644
--- a/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.scss
+++ b/apps/red-ui/src/app/modules/admin/screens/license/license-screen/license-screen.component.scss
@@ -11,7 +11,7 @@
.grid-container {
width: calc(100% - 40px);
display: grid;
- grid-template-columns: 1fr 2fr;
+ grid-template-columns: 1fr 2fr 2fr;
margin: 20px;
.row {
@@ -23,6 +23,10 @@
&:first-of-type {
font-weight: 600;
}
+
+ &:nth-child(2) {
+ grid-column: span 2;
+ }
}
&:hover {
@@ -33,15 +37,14 @@
}
.section-title {
- grid-column: span 2;
+ grid-column: span 3;
padding: 20px 20px 8px;
margin-bottom: 8px;
border-bottom: 1px solid var(--iqser-separator);
}
- }
- redaction-license-chart {
- margin: 50px 0;
- grid-column: span 2;
+ redaction-chart {
+ grid-column: span 3;
+ }
}
}
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
index c4609aac6..c09292794 100644
--- 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
@@ -1,11 +1,10 @@
import { Component } from '@angular/core';
import { ConfigService } from '@services/config.service';
import { TranslateService } from '@ngx-translate/core';
-import { ButtonConfig, getCurrentUser, IconButtonTypes, IqserPermissionsService, LoadingService } from '@iqser/common-ui';
+import { ButtonConfig, getCurrentUser, IconButtonTypes, IqserPermissionsService } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { RouterHistoryService } from '@services/router-history.service';
import { LicenseService } from '@services/license.service';
-import { map } from 'rxjs/operators';
import { Roles } from '@users/roles';
import type { User } from '@red/domain';
@@ -27,24 +26,13 @@ export class LicenseScreenComponent {
},
];
- readonly analysisPercentageOfLicense$ = this.licenseService.selectedLicense$.pipe(map(() => this.getAnalysisPercentageOfLicense()));
-
constructor(
readonly configService: ConfigService,
readonly licenseService: LicenseService,
readonly permissionsService: IqserPermissionsService,
- private readonly _loadingService: LoadingService,
readonly routerHistoryService: RouterHistoryService,
private readonly _translateService: TranslateService,
- ) {
- _loadingService.start();
- }
-
- getAnalysisPercentageOfLicense() {
- const totalLicensedNumberOfPages = this.licenseService.totalLicensedNumberOfPages;
- const numberOfAnalyzedPages = this.licenseService.analyzedPagesInCurrentLicensingPeriod;
- return totalLicensedNumberOfPages > 0 ? (numberOfAnalyzedPages / totalLicensedNumberOfPages) * 100 : 100;
- }
+ ) {}
sendMail(): void {
const licenseCustomer = this.licenseService.selectedLicense.licensedTo;
@@ -54,7 +42,7 @@ export class LicenseScreenComponent {
const lineBreak = '%0D%0A';
const body = [
this._translateService.instant('license-info-screen.email.body.analyzed', {
- pages: this.licenseService.currentLicenseInfo.numberOfAnalyzedPages,
+ pages: this.licenseService.currentLicenseReport.numberOfAnalyzedPages,
}),
this._translateService.instant('license-info-screen.email.body.licensed', {
pages: this.licenseService.totalLicensedNumberOfPages,
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
index 5b9bfc13c..6ac357a00 100644
--- 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
@@ -1,16 +1,18 @@
import { inject, 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 { LicenseSelectComponent } from './components/license-select/license-select.component';
import { RouterModule, Routes } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { MatSelectModule } from '@angular/material/select';
import { IqserHelpModeModule, IqserListingModule, SizePipe } from '@iqser/common-ui';
-import { NgxChartsModule } from '@swimlane/ngx-charts';
-import { ComboChartComponent, ComboSeriesVerticalComponent, YAxisComponent } from './combo-chart';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { LicenseService } from '@services/license.service';
+import { ChartComponent } from './components/chart/chart.component';
+import { NgChartsModule } from 'ng2-charts';
+import { LicenseStorageComponent } from './components/license-storage/license-storage.component';
+import { LicenseUsageComponent } from './components/license-usage/license-usage.component';
+import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
const routes: Routes = [
{
@@ -23,24 +25,18 @@ const routes: Routes = [
];
@NgModule({
- declarations: [
- LicenseScreenComponent,
- LicenseSelectComponent,
- LicenseChartComponent,
- ComboChartComponent,
- ComboSeriesVerticalComponent,
- YAxisComponent,
- ],
+ declarations: [LicenseScreenComponent, LicenseSelectComponent, ChartComponent, LicenseStorageComponent, LicenseUsageComponent],
imports: [
RouterModule.forChild(routes),
CommonModule,
TranslateModule,
MatSelectModule,
FormsModule,
- NgxChartsModule,
IqserListingModule,
IqserHelpModeModule,
SizePipe,
+ NgChartsModule,
+ DonutChartComponent,
],
})
export class LicenseModule {}
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
index a139c9732..24c1bde59 100644
--- 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
@@ -1,17 +1,4 @@
-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';
+export const ChartRed = '#dd4d50';
+export const ChartGreen = '#5ce594';
+export const ChartBlue = '#0389ec';
+export const ChartGrey = '#ccced3'; // grey-5
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
index 0dd196237..efbbea5d3 100644
--- 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
@@ -1,18 +1,32 @@
-import dayjs from 'dayjs';
-import { IDateRange } from '@red/domain';
-
-export function toDate(month: number, year: number, day: number = 1) {
- return dayjs(`${day}-${month}-${year}`, 'D-M-YYYY').toDate();
-}
-
-export function isCurrentMonth(month: number, year: number) {
- const now = dayjs();
- const currentMonth = now.month() + 1;
- const currentYear = now.year();
-
- return month === currentMonth && year === currentYear;
-}
+import dayjs, { Dayjs } from 'dayjs';
+import { FillTarget } from 'chart.js';
+import { hexToRgba } from '@utils/functions';
+import { ILicenseData } from '@red/domain';
+import { ComplexFillTarget } from 'chart.js/dist/types';
const monthNames = dayjs.monthsShort();
-export const verboseDate = (range: IDateRange) => `${monthNames[range.startMonth]} ${range.startYear}`;
+export const verboseDate = (date: Dayjs) => `${monthNames[date.month()]} ${date.year()}`;
+
+export const getLineConfig: (
+ color: string,
+ target: FillTarget,
+) => {
+ type: 'line';
+ borderColor: string;
+ backgroundColor: string;
+ pointBackgroundColor: string;
+ fill: ComplexFillTarget;
+} = (color, target) => ({
+ type: 'line',
+ borderColor: hexToRgba(color, 1),
+ backgroundColor: hexToRgba(color, 1),
+ pointBackgroundColor: hexToRgba(color, 1),
+ fill: {
+ target: target ?? '-1',
+ above: hexToRgba(color, 0.3),
+ below: hexToRgba(color, 0.3),
+ },
+});
+
+export const getLabelsFromMonthlyData = (monthlyData: ILicenseData[]) => monthlyData.map(data => verboseDate(dayjs(data.startDate)));
diff --git a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html
index 5d08fd33b..2de1a0ff6 100644
--- a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html
+++ b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.html
@@ -18,7 +18,7 @@
-
{{ displayedDataTotal }}
+
{{ getFormattedValue(displayedDataTotal) }}
{{ subtitles[0] }}
string;
@Output() readonly subtitleChanged = new EventEmitter();
@@ -32,10 +33,6 @@ export class DonutChartComponent implements OnChanges, OnInit {
size = 0;
filters$: Observable;
- constructor(@Optional() readonly filterService: FilterService) {
- // TODO: move this component to a separate module, split into smaller components, improve filters
- }
-
get circumference(): number {
return 2 * Math.PI * this.radius;
}
@@ -48,6 +45,10 @@ export class DonutChartComponent implements OnChanges, OnInit {
return this.totalType === 'sum' ? this.dataTotal : this.config.length;
}
+ constructor(@Optional() readonly filterService: FilterService) {
+ // TODO: move this component to a separate module, split into smaller components, improve filters
+ }
+
ngOnInit() {
const filterModels$ = this.filterService?.getFilterModels$(this.filterKey).pipe(
map(filters => filters ?? []),
@@ -71,6 +72,10 @@ export class DonutChartComponent implements OnChanges, OnInit {
);
}
+ getFormattedValue(value: number): string {
+ return this.valueFormatter ? this.valueFormatter(value) : value.toString();
+ }
+
calculateChartData() {
let angleOffset = -90;
this.chartData = this.config.map(dataVal => {
@@ -101,8 +106,8 @@ export class DonutChartComponent implements OnChanges, OnInit {
return this.totalType === 'simpleLabel'
? `${label}`
: this.totalType === 'sum'
- ? `${value} ${label}`
- : `${label} (${value} ${this.counterText})`;
+ ? `${this.getFormattedValue(value)} ${label}`
+ : `${label} (${this.getFormattedValue(value)} ${this.counterText})`;
}
selectValue(key: string): void {
diff --git a/apps/red-ui/src/app/services/license.service.ts b/apps/red-ui/src/app/services/license.service.ts
index f56db7792..7c1dd0d14 100644
--- a/apps/red-ui/src/app/services/license.service.ts
+++ b/apps/red-ui/src/app/services/license.service.ts
@@ -1,17 +1,11 @@
import { Injectable } from '@angular/core';
-import { GenericService, QueryParam } from '@iqser/common-ui';
+import { GenericService } from '@iqser/common-ui';
import { ILicense, ILicenseReport, ILicenseReportRequest, ILicenses } from '@red/domain';
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
import { catchError, filter, tap } from 'rxjs/operators';
-import { LICENSE_STORAGE_KEY } from '../modules/admin/screens/license/utils/constants';
import dayjs from 'dayjs';
import { NGXLogger } from 'ngx-logger';
-export function getStoredReports() {
- const rawStoredReports = localStorage.getItem(LICENSE_STORAGE_KEY);
- return JSON.parse(rawStoredReports ?? '{}') as Record;
-}
-
const defaultOnError: ILicenses = {
activeLicense: 'err',
licenses: [
@@ -45,15 +39,15 @@ const defaultOnError: ILicenses = {
providedIn: 'root',
})
export class LicenseService extends GenericService {
- storedReports = getStoredReports();
readonly licenseData$: Observable;
readonly selectedLicense$: Observable;
activeLicenseId: string;
totalLicensedNumberOfPages = 0;
- currentLicenseInfo: ILicenseReport = {};
- allLicensesInfo: ILicenseReport = {};
+ currentLicenseReport: ILicenseReport = {};
+ allLicensesReport: ILicenseReport = {};
unlicensedPages = 0;
analyzedPagesInCurrentLicensingPeriod = 0;
+ uploadedBytesCapacity = 0;
protected readonly _defaultModelPath = 'report';
readonly #licenseData$ = new BehaviorSubject(undefined);
readonly #selectedLicense$ = new BehaviorSubject(undefined);
@@ -81,15 +75,13 @@ export class LicenseService extends GenericService {
this.licenseData$ = this.#licenseData$.pipe(
filter(licenses => !!licenses),
tap(data => (this.activeLicenseId = data.activeLicense)),
+ tap(() => {
+ const uploadedBytesCapacity = this.activeLicense.features.find(f => f.name === 'uploadedBytesCapacity')?.value;
+ this.uploadedBytesCapacity = uploadedBytesCapacity ? parseInt(uploadedBytesCapacity, 10) : 0;
+ }),
);
}
- wipeStoredReportsAndReloadSelectedLicenseData() {
- this._logger.info('[LICENSE] Wiping stored reports and reloading license data');
- this.storedReports = {};
- this.setSelectedLicense(this.selectedLicense);
- }
-
async loadLicenseData(license: ILicense = this.selectedLicense) {
this.totalLicensedNumberOfPages = this.getTotalLicensedNumberOfPages(license);
@@ -109,14 +101,14 @@ export class LicenseService extends GenericService {
const configs = [currentLicenseConfig, allLicensesConfig];
const reports = configs.map(config => this.getReport(config));
- [this.currentLicenseInfo, this.allLicensesInfo] = await Promise.all(reports);
+ [this.currentLicenseReport, this.allLicensesReport] = await Promise.all(reports);
- if (this.currentLicenseInfo.numberOfAnalyzedPages > this.totalLicensedNumberOfPages) {
- this.unlicensedPages = this.currentLicenseInfo.numberOfAnalyzedPages - this.totalLicensedNumberOfPages;
+ if (this.currentLicenseReport.numberOfAnalyzedPages > this.totalLicensedNumberOfPages) {
+ this.unlicensedPages = this.currentLicenseReport.numberOfAnalyzedPages - this.totalLicensedNumberOfPages;
} else {
this.unlicensedPages = 0;
}
- this.analyzedPagesInCurrentLicensingPeriod = this.currentLicenseInfo.numberOfAnalyzedPages;
+ this.analyzedPagesInCurrentLicensingPeriod = this.currentLicenseReport.numberOfAnalyzedPages;
}
getTotalLicensedNumberOfPages(license: ILicense) {
@@ -128,17 +120,8 @@ export class LicenseService extends GenericService {
this.setSelectedLicense(this.activeLicense);
}
- getReport(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 firstValueFrom(this._post(body, `${this._defaultModelPath}/license`, queryParams));
+ getReport(body: ILicenseReportRequest) {
+ return firstValueFrom(this._post(body, `${this._defaultModelPath}/license`));
}
async loadLicenses() {
diff --git a/apps/red-ui/src/app/utils/functions.ts b/apps/red-ui/src/app/utils/functions.ts
index 02e0dffa1..ed720c96f 100644
--- a/apps/red-ui/src/app/utils/functions.ts
+++ b/apps/red-ui/src/app/utils/functions.ts
@@ -14,6 +14,11 @@ export function hexToRgb(hex: string) {
: null;
}
+export function hexToRgba(hex: string, alpha: number) {
+ const rgb = hexToRgb(hex);
+ return rgb ? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})` : null;
+}
+
export function getFirstRelevantTextPart(text: string, direction: 'FORWARD' | 'BACKWARD') {
let spaceCount = 0;
let accumulator = '';
diff --git a/apps/red-ui/src/assets/i18n/redact/de.json b/apps/red-ui/src/assets/i18n/redact/de.json
index dfea3dcef..294073cf1 100644
--- a/apps/red-ui/src/assets/i18n/redact/de.json
+++ b/apps/red-ui/src/assets/i18n/redact/de.json
@@ -1681,7 +1681,9 @@
"active-documents": "",
"all-documents": "",
"archived-documents": "",
- "trash-documents": ""
+ "storage-capacity": "",
+ "trash-documents": "",
+ "unused": ""
},
"total-analyzed": "Seit {date} insgesamt analysierte Seiten",
"total-ocr-analyzed": "",
diff --git a/apps/red-ui/src/assets/i18n/redact/en.json b/apps/red-ui/src/assets/i18n/redact/en.json
index 6f0d29933..0c2d1092d 100644
--- a/apps/red-ui/src/assets/i18n/redact/en.json
+++ b/apps/red-ui/src/assets/i18n/redact/en.json
@@ -1679,9 +1679,11 @@
"storage-details": "Storage Details",
"storage": {
"active-documents": "Active Documents",
- "all-documents": "All Documents",
+ "all-documents": "Total Storage Used",
"archived-documents": "Archived Documents",
- "trash-documents": "Documents in Trash"
+ "storage-capacity": "Storage Capacity",
+ "trash-documents": "Documents in Trash",
+ "unused": "Unused Storage"
},
"total-analyzed": "Total Analyzed Pages",
"total-ocr-analyzed": "Total OCR Processed Pages",
diff --git a/apps/red-ui/src/assets/i18n/scm/de.json b/apps/red-ui/src/assets/i18n/scm/de.json
index ba2394f36..33d99d99a 100644
--- a/apps/red-ui/src/assets/i18n/scm/de.json
+++ b/apps/red-ui/src/assets/i18n/scm/de.json
@@ -1681,7 +1681,9 @@
"active-documents": "",
"all-documents": "",
"archived-documents": "",
- "trash-documents": ""
+ "storage-capacity": "",
+ "trash-documents": "",
+ "unused": ""
},
"total-analyzed": "Seit {date} insgesamt analysierte Seiten",
"total-ocr-analyzed": "",
diff --git a/apps/red-ui/src/assets/i18n/scm/en.json b/apps/red-ui/src/assets/i18n/scm/en.json
index 5d5dd741c..2fe18f605 100644
--- a/apps/red-ui/src/assets/i18n/scm/en.json
+++ b/apps/red-ui/src/assets/i18n/scm/en.json
@@ -1679,9 +1679,11 @@
"storage-details": "Storage Details",
"storage": {
"active-documents": "Active Documents",
- "all-documents": "All Documents",
+ "all-documents": "Total Storage Used",
"archived-documents": "Archived Documents",
- "trash-documents": "Documents in Trash"
+ "storage-capacity": "Storage Capacity",
+ "trash-documents": "Documents in Trash",
+ "unused": "Unused Storage"
},
"total-analyzed": "Total Analyzed Pages Since {date}",
"total-ocr-analyzed": "Total OCR Processed Pages Since {date}",
diff --git a/libs/common-ui b/libs/common-ui
index aa4516286..2d6ee6655 160000
--- a/libs/common-ui
+++ b/libs/common-ui
@@ -1 +1 @@
-Subproject commit aa4516286eeeaab7e69adadb4f58193e8f2b64ed
+Subproject commit 2d6ee6655c26debca43f3bf248f8521e793117f4
diff --git a/libs/red-domain/src/lib/reports/license-report.request.ts b/libs/red-domain/src/lib/reports/license-report.request.ts
index 796fb114a..99330d5bd 100644
--- a/libs/red-domain/src/lib/reports/license-report.request.ts
+++ b/libs/red-domain/src/lib/reports/license-report.request.ts
@@ -1,8 +1,4 @@
-import { List } from '@iqser/common-ui';
-
export interface ILicenseReportRequest {
- dossierIds?: List;
endDate?: Date | string;
- requestId?: string;
startDate?: Date | string;
}
diff --git a/libs/red-domain/src/lib/reports/license-report.ts b/libs/red-domain/src/lib/reports/license-report.ts
index ab145d2c0..969ff4dcb 100644
--- a/libs/red-domain/src/lib/reports/license-report.ts
+++ b/libs/red-domain/src/lib/reports/license-report.ts
@@ -1,4 +1,4 @@
-interface ILicenseData {
+export interface ILicenseData {
activeFilesUploadedBytes?: number;
archivedFilesUploadedBytes?: number;
totalFilesUploadedBytes?: number;
@@ -16,6 +16,5 @@ export interface ILicenseReport extends ILicenseData {
numberOfDossiers?: number;
numberOfOcrFiles?: number;
offset?: number;
- requestId?: string;
monthlyData?: ILicenseData[];
}
diff --git a/package.json b/package.json
index 8fd5f5910..e084fdb84 100644
--- a/package.json
+++ b/package.json
@@ -37,8 +37,9 @@
"@messageformat/core": "^3.1.0",
"@ngx-translate/core": "15.0.0",
"@ngx-translate/http-loader": "8.0.0",
+ "@nx/angular": "16.3.2",
"@pdftron/webviewer": "10.1.1",
- "@swimlane/ngx-charts": "20.4.1",
+ "chart.js": "^4.3.0",
"dayjs": "^1.11.5",
"file-saver": "^2.0.5",
"jwt-decode": "^3.1.2",
@@ -46,6 +47,7 @@
"keycloak-js": "21.1.1",
"lodash-es": "^4.17.21",
"monaco-editor": "0.39.0",
+ "ng2-charts": "^4.1.1",
"ngx-color-picker": "^14.0.0",
"ngx-logger": "^5.0.11",
"ngx-toastr": "17.0.2",
@@ -57,8 +59,7 @@
"scroll-into-view-if-needed": "^3.0.6",
"streamsaver": "^2.0.5",
"tslib": "2.5.3",
- "zone.js": "0.13.1",
- "@nx/angular": "16.3.2"
+ "zone.js": "0.13.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "16.1.0",
diff --git a/yarn.lock b/yarn.lock
index 32b342a21..5f7b26dfa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2233,6 +2233,11 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@kurkle/color@^0.3.0":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
+ integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
+
"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@@ -3568,26 +3573,6 @@
dependencies:
"@sinonjs/commons" "^3.0.0"
-"@swimlane/ngx-charts@20.4.1":
- version "20.4.1"
- resolved "https://registry.yarnpkg.com/@swimlane/ngx-charts/-/ngx-charts-20.4.1.tgz#42f3d63c1326cfe347d62d1f626840d6c1511276"
- integrity sha512-DyTQe0fcqLDoLEZca45gkdjxP8iLH7kh4pCkr+TCFIkmgEdfQ5DpavNBOOVO0qd5J5uV/tbtSnkYWSx8JkbFpg==
- dependencies:
- d3-array "^3.1.1"
- d3-brush "^3.0.0"
- d3-color "^3.1.0"
- d3-ease "^3.0.1"
- d3-format "^3.1.0"
- d3-hierarchy "^3.1.0"
- d3-interpolate "^3.0.1"
- d3-scale "^4.0.2"
- d3-selection "^3.0.0"
- d3-shape "^3.2.0"
- d3-time-format "^3.0.0"
- d3-transition "^3.0.1"
- rfdc "^1.3.0"
- tslib "^2.0.0"
-
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
@@ -5051,6 +5036,13 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+chart.js@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.3.0.tgz#ac363030ab3fec572850d2d872956f32a46326a1"
+ integrity sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==
+ dependencies:
+ "@kurkle/color" "^0.3.0"
+
chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.5.1, chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -5625,143 +5617,6 @@ cuint@^0.2.2:
resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b"
integrity sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==
-d3-array@2:
- version "2.12.1"
- resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81"
- integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==
- dependencies:
- internmap "^1.0.0"
-
-"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.1:
- version "3.2.4"
- resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
- integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
- dependencies:
- internmap "1 - 2"
-
-d3-brush@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
- integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
- dependencies:
- d3-dispatch "1 - 3"
- d3-drag "2 - 3"
- d3-interpolate "1 - 3"
- d3-selection "3"
- d3-transition "3"
-
-"d3-color@1 - 3", d3-color@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
- integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
-
-"d3-dispatch@1 - 3":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
- integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
-
-"d3-drag@2 - 3":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
- integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
- dependencies:
- d3-dispatch "1 - 3"
- d3-selection "3"
-
-"d3-ease@1 - 3", d3-ease@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
- integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
-
-"d3-format@1 - 3", d3-format@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
- integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
-
-d3-hierarchy@^3.1.0:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6"
- integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
-
-"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
- integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
- dependencies:
- d3-color "1 - 3"
-
-d3-path@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526"
- integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
-
-d3-scale@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
- integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
- dependencies:
- d3-array "2.10.0 - 3"
- d3-format "1 - 3"
- d3-interpolate "1.2.0 - 3"
- d3-time "2.1.1 - 3"
- d3-time-format "2 - 4"
-
-d3-selection@3, d3-selection@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
- integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
-
-d3-shape@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5"
- integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
- dependencies:
- d3-path "^3.1.0"
-
-"d3-time-format@2 - 4":
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
- integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
- dependencies:
- d3-time "1 - 3"
-
-d3-time-format@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6"
- integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==
- dependencies:
- d3-time "1 - 2"
-
-"d3-time@1 - 2":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682"
- integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==
- dependencies:
- d3-array "2"
-
-"d3-time@1 - 3", "d3-time@2.1.1 - 3":
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7"
- integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
- dependencies:
- d3-array "2 - 3"
-
-"d3-timer@1 - 3":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
- integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
-
-d3-transition@3, d3-transition@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
- integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
- dependencies:
- d3-color "1 - 3"
- d3-dispatch "1 - 3"
- d3-ease "1 - 3"
- d3-interpolate "1 - 3"
- d3-timer "1 - 3"
-
data-urls@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
@@ -7516,16 +7371,6 @@ internal-slot@^1.0.4:
has "^1.0.3"
side-channel "^1.0.4"
-"internmap@1 - 2":
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
- integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
-
-internmap@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
- integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
-
ip@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
@@ -8571,7 +8416,7 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
-lodash-es@^4.17.21:
+lodash-es@^4.17.15, lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
@@ -9052,6 +8897,14 @@ neo-async@^2.6.2:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+ng2-charts@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ng2-charts/-/ng2-charts-4.1.1.tgz#699bee539030b093caf54ab6111c6dea9e8e7ed3"
+ integrity sha512-iHwXDbmX86lfeH8VRcsaW2tJATsuAZo4kvvC/Yk2l35zOHjevja1qBvO6BAibiDazi9r9aS6ZRJOqWPsz1pP2w==
+ dependencies:
+ lodash-es "^4.17.15"
+ tslib "^2.3.0"
+
ngx-color-picker@^14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/ngx-color-picker/-/ngx-color-picker-14.0.0.tgz#4587f517ac5683a705d4e55cd0939afa91faa853"