RED-6830: License information with graphjs
This commit is contained in:
parent
76b5755d5e
commit
c8745f048d
@ -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,
|
||||
|
||||
@ -1,116 +0,0 @@
|
||||
<ngx-charts-chart
|
||||
(legendLabelActivate)="onActivate($event)"
|
||||
(legendLabelClick)="onClick($event)"
|
||||
(legendLabelDeactivate)="onDeactivate($event)"
|
||||
[activeEntries]="activeEntries"
|
||||
[animations]="animations"
|
||||
[legendOptions]="legendOptions"
|
||||
[showLegend]="legend"
|
||||
[view]="[width + legendSpacing, height]"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg:g [attr.transform]="transform" class="bar-chart chart">
|
||||
<svg:g
|
||||
(dimensionsChanged)="updateXAxisHeight($event)"
|
||||
*ngIf="xAxis"
|
||||
[dims]="dims"
|
||||
[labelText]="xAxisLabel"
|
||||
[rotateTicks]="true"
|
||||
[showLabel]="showXAxisLabel"
|
||||
[tickFormatting]="xAxisTickFormatting"
|
||||
[xScale]="xScale"
|
||||
ngx-charts-x-axis
|
||||
></svg:g>
|
||||
<svg:g
|
||||
(dimensionsChanged)="updateYAxisWidth($event)"
|
||||
*ngIf="yAxis"
|
||||
[dims]="dims"
|
||||
[labelText]="yAxisLabel"
|
||||
[showGridLines]="showGridLines"
|
||||
[showLabel]="showYAxisLabel"
|
||||
[tickFormatting]="yAxisTickFormatting"
|
||||
[yOrient]="yOrientLeft"
|
||||
[yScale]="yScale"
|
||||
[valuesMaxLength]="getValuesMaxLength(1)"
|
||||
red-ngx-charts-y-axis
|
||||
></svg:g>
|
||||
<svg:g
|
||||
(dimensionsChanged)="updateYAxisWidth($event)"
|
||||
*ngIf="yAxis"
|
||||
[dims]="dims"
|
||||
[labelText]="yAxisLabelRight"
|
||||
[showGridLines]="showGridLines"
|
||||
[showLabel]="showRightYAxisLabel"
|
||||
[tickFormatting]="yRightAxisTickFormatting"
|
||||
[yOrient]="yOrientRight"
|
||||
[yScale]="yScaleLine"
|
||||
[valuesMaxLength]="getValuesMaxLength(0)"
|
||||
red-ngx-charts-y-axis
|
||||
></svg:g>
|
||||
<svg:g
|
||||
(activate)="onActivate($event)"
|
||||
(bandwidth)="updateLineWidth($event)"
|
||||
(deactivate)="onDeactivate($event)"
|
||||
[activeEntries]="activeEntries"
|
||||
[animations]="animations"
|
||||
[colors]="colors"
|
||||
[dims]="dims"
|
||||
[gradient]="gradient"
|
||||
[noBarWhenZero]="noBarWhenZero"
|
||||
[seriesLine]="lineChart"
|
||||
[series]="results"
|
||||
[tooltipDisabled]="true"
|
||||
[xScale]="xScale"
|
||||
[yScale]="yScale"
|
||||
ngx-combo-charts-series-vertical
|
||||
></svg:g>
|
||||
</svg:g>
|
||||
|
||||
<svg:g [attr.transform]="transform" class="line-chart chart">
|
||||
<svg:g>
|
||||
<svg:g *ngFor="let series of lineChart; trackBy: trackBy">
|
||||
<svg:g
|
||||
[activeEntries]="activeEntries"
|
||||
[animations]="animations"
|
||||
[colors]="colorsLine"
|
||||
[curve]="curve"
|
||||
[data]="series"
|
||||
[rangeFillOpacity]="rangeFillOpacity"
|
||||
[scaleType]="scaleType"
|
||||
[xScale]="xScaleLine"
|
||||
[yScale]="yScaleLine"
|
||||
ngx-charts-line-series
|
||||
></svg:g>
|
||||
</svg:g>
|
||||
|
||||
<svg:g
|
||||
(hover)="updateHoveredVertical($event)"
|
||||
*ngIf="!tooltipDisabled"
|
||||
[colors]="colorsLine"
|
||||
[dims]="dims"
|
||||
[results]="combinedSeries"
|
||||
[tooltipDisabled]="tooltipDisabled"
|
||||
[xScale]="xScaleLine"
|
||||
[xSet]="xSet"
|
||||
[yScale]="yScaleLine"
|
||||
ngx-charts-tooltip-area
|
||||
></svg:g>
|
||||
|
||||
<svg:g *ngFor="let series of lineChart">
|
||||
<svg:g
|
||||
(activate)="onActivate($event)"
|
||||
(deactivate)="onDeactivate($event)"
|
||||
[activeEntries]="activeEntries"
|
||||
[colors]="colorsLine"
|
||||
[data]="series"
|
||||
[scaleType]="scaleType"
|
||||
[tooltipDisabled]="tooltipDisabled"
|
||||
[visibleValue]="hoveredVertical"
|
||||
[xScale]="xScaleLine"
|
||||
[yScale]="yScaleLine"
|
||||
ngx-charts-circle-series
|
||||
></svg:g>
|
||||
</svg:g>
|
||||
</svg:g>
|
||||
</svg:g>
|
||||
</ngx-charts-chart>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 });
|
||||
}
|
||||
}
|
||||
@ -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: `
|
||||
<svg:g
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
ngx-charts-bar
|
||||
*ngFor="let bar of bars; trackBy: trackBy"
|
||||
[@animationState]="'active'"
|
||||
[width]="bar.width"
|
||||
[height]="bar.height"
|
||||
[x]="bar.x"
|
||||
[y]="bar.y"
|
||||
[fill]="bar.color"
|
||||
[stops]="bar.gradientStops"
|
||||
[data]="bar.data"
|
||||
[orientation]="orientations.Vertical"
|
||||
[roundEdges]="bar.roundEdges"
|
||||
[gradient]="gradient"
|
||||
[isActive]="isActive(bar.data)"
|
||||
[animations]="animations"
|
||||
[noBarWhenZero]="noBarWhenZero"
|
||||
(activate)="activate.emit($event)"
|
||||
(deactivate)="deactivate.emit($event)"
|
||||
ngx-tooltip
|
||||
[tooltipDisabled]="tooltipDisabled"
|
||||
[tooltipPlacement]="tooltipPlacements.Top"
|
||||
[tooltipType]="tooltipTypes.tooltip"
|
||||
[tooltipTitle]="bar.tooltipText"
|
||||
></svg:g>
|
||||
`,
|
||||
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 = `
|
||||
<span class="tooltip-label">${tooltipLabel}</span>
|
||||
<span class="tooltip-val">
|
||||
Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}%
|
||||
</span>
|
||||
`;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './combo-chart.component';
|
||||
export * from './combo-series-vertical.component';
|
||||
export * from './y-axis.component';
|
||||
@ -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: `
|
||||
<svg:g xmlns:svg="http://www.w3.org/2000/svg" [attr.class]="yAxisClassName" [attr.transform]="transform">
|
||||
<svg:g
|
||||
ngx-charts-y-axis-ticks
|
||||
*ngIf="yScale"
|
||||
[trimTicks]="trimTicks"
|
||||
[maxTickLength]="maxTickLength"
|
||||
[tickFormatting]="tickFormatting"
|
||||
[tickArguments]="tickArguments"
|
||||
[tickValues]="ticks"
|
||||
[tickStroke]="tickStroke"
|
||||
[scale]="yScale"
|
||||
[orient]="yOrient"
|
||||
[showGridLines]="showGridLines"
|
||||
[gridLineWidth]="dims.width"
|
||||
[referenceLines]="referenceLines"
|
||||
[showRefLines]="showRefLines"
|
||||
[showRefLabels]="showRefLabels"
|
||||
[height]="dims.height"
|
||||
(dimensionsChanged)="emitTicksWidth($event)"
|
||||
/>
|
||||
<svg:g
|
||||
ngx-charts-axis-label
|
||||
*ngIf="showLabel"
|
||||
[label]="labelText"
|
||||
[offset]="labelOffset"
|
||||
[orient]="yOrient"
|
||||
[height]="dims.height"
|
||||
[width]="dims.width"
|
||||
></svg:g>
|
||||
</svg:g>
|
||||
`,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
<div class="canvas-container">
|
||||
<canvas [data]="chartData" [options]="chartOptions" [type]="'line'" baseChart></canvas>
|
||||
</div>
|
||||
@ -0,0 +1,8 @@
|
||||
:host {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
position: relative;
|
||||
width: 1000px;
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<div class="section-title all-caps-label" translate="license-info-screen.storage-details"></div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.active-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseReport.activeFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.archived-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseReport.archivedFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.trash-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseReport.trashFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.all-documents"></div>
|
||||
<div>
|
||||
{{ licenseService.currentLicenseReport.totalFilesUploadedBytes | size }}
|
||||
<ng-container *ngIf="uploadedBytesCapacityPercentage !== -1">
|
||||
({{ uploadedBytesCapacityPercentage | number : '1.0-2' }}%)
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="donut-chart-wrapper pl-20">
|
||||
<redaction-donut-chart
|
||||
*ngIf="uploadedBytesCapacityPercentage !== -1"
|
||||
[config]="donutChartConfig"
|
||||
[direction]="'row'"
|
||||
[radius]="80"
|
||||
[strokeWidth]="15"
|
||||
[subtitles]="['license-info-screen.storage.storage-capacity' | translate]"
|
||||
[totalType]="'sum'"
|
||||
[valueFormatter]="size"
|
||||
></redaction-donut-chart>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<redaction-chart
|
||||
*ngIf="data$ | async as data"
|
||||
[datasets]="data.datasets"
|
||||
[labels]="data.labels"
|
||||
[ticksCallback]="formatSize"
|
||||
></redaction-chart>
|
||||
</div>
|
||||
@ -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;
|
||||
}
|
||||
@ -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',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
<div class="section-title all-caps-label" translate="license-info-screen.usage-details"></div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.current-analyzed"></div>
|
||||
<div>
|
||||
{{ licenseService.analyzedPagesInCurrentLicensingPeriod }}
|
||||
({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.ocr-analyzed-pages"></div>
|
||||
<div>{{ licenseService.currentLicenseReport.numberOfOcrPages }}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!!licenseService.unlicensedPages" class="row">
|
||||
<div translate="license-info-screen.unlicensed-analyzed"></div>
|
||||
<div>{{ licenseService.unlicensedPages }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div [innerHTML]="'license-info-screen.total-analyzed' | translate"></div>
|
||||
|
||||
<div>{{ licenseService.allLicensesReport.numberOfAnalyzedPages }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div [innerHTML]="'license-info-screen.total-ocr-analyzed' | translate"></div>
|
||||
<div>{{ licenseService.allLicensesReport.numberOfOcrPages }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<redaction-chart
|
||||
*ngIf="data$ | async as data"
|
||||
[datasets]="data.datasets"
|
||||
[labels]="data.labels"
|
||||
[secondaryAxis]="true"
|
||||
[yAxisLabelRight]="'license-info-screen.chart.total-pages' | translate"
|
||||
[yAxisLabel]="'license-info-screen.chart.pages-per-month' | translate"
|
||||
></redaction-chart>
|
||||
</div>
|
||||
@ -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;
|
||||
}
|
||||
@ -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),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
<combo-chart-component
|
||||
*ngIf="lineChartSeries$ | async as lineChartSeries"
|
||||
[animations]="true"
|
||||
[colorSchemeLine]="lineChartScheme"
|
||||
[legendTitle]="'license-info-screen.chart.legend' | translate"
|
||||
[legend]="true"
|
||||
[lineChart]="lineChartSeries"
|
||||
[results]="barChart"
|
||||
[scheme]="comboBarScheme"
|
||||
[showGridLines]="true"
|
||||
[showRightYAxisLabel]="true"
|
||||
[showYAxisLabel]="true"
|
||||
[view]="[1000, 300]"
|
||||
[xAxis]="true"
|
||||
[yAxisLabelRight]="'license-info-screen.chart.total-pages' | translate"
|
||||
[yAxisLabel]="'license-info-screen.chart.pages-per-month' | translate"
|
||||
[yAxis]="true"
|
||||
></combo-chart-component>
|
||||
@ -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<Series[]> {
|
||||
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<Series[]> {
|
||||
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<IDateRange>, reports: List<ILicenseReport>): 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<ILicenseReport>) {
|
||||
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<IDateRange>, 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<IDateRange>) {
|
||||
return dateRanges.map(dateRange => ({
|
||||
name: verboseDate(dateRange),
|
||||
value: this._licenseService.totalLicensedNumberOfPages,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -61,65 +61,8 @@
|
||||
<div>{{ licenseService.totalLicensedNumberOfPages }}</div>
|
||||
</div>
|
||||
|
||||
<!---------- STORAGE DETAILS -->
|
||||
|
||||
<div class="section-title all-caps-label" translate="license-info-screen.storage-details"></div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.active-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseInfo.activeFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.archived-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseInfo.archivedFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.trash-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseInfo.trashFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.storage.all-documents"></div>
|
||||
<div>{{ licenseService.currentLicenseInfo.totalFilesUploadedBytes | size }}</div>
|
||||
</div>
|
||||
|
||||
<!---------- USAGE DETAILS -->
|
||||
|
||||
<div class="section-title all-caps-label" translate="license-info-screen.usage-details"></div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.current-analyzed"></div>
|
||||
<div>
|
||||
{{ licenseService.analyzedPagesInCurrentLicensingPeriod }}
|
||||
({{ analysisPercentageOfLicense$ | async | number : '1.0-2' }}%)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div translate="license-info-screen.ocr-analyzed-pages"></div>
|
||||
<div>{{ licenseService.currentLicenseInfo.numberOfOcrPages }}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!!licenseService.unlicensedPages" class="row">
|
||||
<div translate="license-info-screen.unlicensed-analyzed"></div>
|
||||
<div>{{ licenseService.unlicensedPages }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div [innerHTML]="'license-info-screen.total-analyzed' | translate"></div>
|
||||
|
||||
<div>{{ licenseService.allLicensesInfo.numberOfAnalyzedPages }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div [innerHTML]="'license-info-screen.total-ocr-analyzed' | translate"></div>
|
||||
<div>{{ licenseService.allLicensesInfo.numberOfOcrPages }}</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<redaction-license-chart></redaction-license-chart>
|
||||
</div>
|
||||
<red-license-storage></red-license-storage>
|
||||
<red-license-usage></red-license-usage>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)));
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</svg>
|
||||
|
||||
<div [style]="'height: ' + size + 'px; width: ' + size + 'px; padding: ' + (strokeWidth + 5) + 'px;'" class="text-container">
|
||||
<div class="heading-xl">{{ displayedDataTotal }}</div>
|
||||
<div class="heading-xl">{{ getFormattedValue(displayedDataTotal) }}</div>
|
||||
<div *ngIf="subtitles.length === 1" class="mt-5">{{ subtitles[0] }}</div>
|
||||
|
||||
<mat-select
|
||||
|
||||
@ -23,6 +23,7 @@ export class DonutChartComponent implements OnChanges, OnInit {
|
||||
@Input() counterText: string;
|
||||
@Input() filterKey;
|
||||
@Input() helpModeKey;
|
||||
@Input() valueFormatter?: (value: number) => string;
|
||||
|
||||
@Output() readonly subtitleChanged = new EventEmitter<number>();
|
||||
|
||||
@ -32,10 +33,6 @@ export class DonutChartComponent implements OnChanges, OnInit {
|
||||
size = 0;
|
||||
filters$: Observable<INestedFilter[]>;
|
||||
|
||||
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 {
|
||||
|
||||
@ -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<string, ILicenseReport>;
|
||||
}
|
||||
|
||||
const defaultOnError: ILicenses = {
|
||||
activeLicense: 'err',
|
||||
licenses: [
|
||||
@ -45,15 +39,15 @@ const defaultOnError: ILicenses = {
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LicenseService extends GenericService<ILicenseReport> {
|
||||
storedReports = getStoredReports();
|
||||
readonly licenseData$: Observable<ILicenses>;
|
||||
readonly selectedLicense$: Observable<ILicense>;
|
||||
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<ILicenses | undefined>(undefined);
|
||||
readonly #selectedLicense$ = new BehaviorSubject<ILicense | undefined>(undefined);
|
||||
@ -81,15 +75,13 @@ export class LicenseService extends GenericService<ILicenseReport> {
|
||||
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<ILicenseReport> {
|
||||
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<ILicenseReport> {
|
||||
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() {
|
||||
|
||||
@ -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 = '';
|
||||
|
||||
@ -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": "",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "",
|
||||
|
||||
@ -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}",
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit aa4516286eeeaab7e69adadb4f58193e8f2b64ed
|
||||
Subproject commit 2d6ee6655c26debca43f3bf248f8521e793117f4
|
||||
@ -1,8 +1,4 @@
|
||||
import { List } from '@iqser/common-ui';
|
||||
|
||||
export interface ILicenseReportRequest {
|
||||
dossierIds?: List;
|
||||
endDate?: Date | string;
|
||||
requestId?: string;
|
||||
startDate?: Date | string;
|
||||
}
|
||||
|
||||
@ -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[];
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
189
yarn.lock
189
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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user