ui: move barchart to echarts
This commit is contained in:
parent
81d99bd985
commit
8e1f50e4a3
6 changed files with 290 additions and 164 deletions
|
|
@ -12,7 +12,6 @@ echarts.use([BarChart]);
|
|||
|
||||
interface BarChartProps extends DataProps {
|
||||
label?: string;
|
||||
horizontal?: boolean;
|
||||
}
|
||||
|
||||
function ORBarChart(props: BarChartProps) {
|
||||
|
|
@ -26,14 +25,14 @@ function ORBarChart(props: BarChartProps) {
|
|||
obs.observe(chartRef.current);
|
||||
|
||||
const categories = buildCategories(props.data);
|
||||
const { datasets, series } = buildBarDatasetsAndSeries(props, props.horizontal ?? false);
|
||||
const { datasets, series } = buildBarDatasetsAndSeries(props);
|
||||
|
||||
initWindowStorages(chartUuid.current, categories, props.data.chart, props.compData?.chart ?? []);
|
||||
series.forEach((s: any) => {
|
||||
(window as any).__seriesColorMap[chartUuid.current][s.name] = s.itemStyle?.color ?? '#999';
|
||||
const ds = datasets.find((d) => d.id === s.datasetId);
|
||||
if (!ds) return;
|
||||
const yDim = props.horizontal ? s.encode.x : s.encode.y;
|
||||
const yDim = s.encode.y;
|
||||
const yDimIndex = ds.dimensions.indexOf(yDim);
|
||||
if (yDimIndex < 0) return;
|
||||
|
||||
|
|
@ -46,12 +45,12 @@ function ORBarChart(props: BarChartProps) {
|
|||
|
||||
|
||||
const xAxis: any = {
|
||||
type: props.horizontal ? 'value' : 'category',
|
||||
data: props.horizontal ? undefined : categories,
|
||||
type: 'category',
|
||||
data: categories,
|
||||
};
|
||||
const yAxis: any = {
|
||||
type: props.horizontal ? 'category' : 'value',
|
||||
data: props.horizontal ? categories : undefined,
|
||||
type: 'value',
|
||||
data: undefined,
|
||||
name: props.label ?? 'Number of Sessions',
|
||||
nameLocation: 'middle',
|
||||
nameGap: 35,
|
||||
|
|
@ -82,7 +81,7 @@ function ORBarChart(props: BarChartProps) {
|
|||
delete (window as any).__timestampMap[chartUuid.current];
|
||||
delete (window as any).__timestampCompMap[chartUuid.current];
|
||||
};
|
||||
}, [props.data, props.compData, props.horizontal]);
|
||||
}, [props.data, props.compData]);
|
||||
|
||||
return <div ref={chartRef} style={{ width: '100%', height: 240 }} />;
|
||||
}
|
||||
|
|
|
|||
93
frontend/app/components/Charts/ColumnChart.tsx
Normal file
93
frontend/app/components/Charts/ColumnChart.tsx
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import React from 'react';
|
||||
import { defaultOptions, echarts } from './init';
|
||||
import { BarChart } from 'echarts/charts';
|
||||
import { customTooltipFormatter } from './utils';
|
||||
import { buildColumnChart } from './barUtils'
|
||||
|
||||
echarts.use([BarChart]);
|
||||
|
||||
interface DataItem {
|
||||
time: string;
|
||||
timestamp: number;
|
||||
[seriesName: string]: number | string;
|
||||
}
|
||||
|
||||
export interface DataProps {
|
||||
data: {
|
||||
chart: DataItem[];
|
||||
namesMap: string[];
|
||||
};
|
||||
compData?: {
|
||||
chart: DataItem[];
|
||||
namesMap: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface ColumnChartProps extends DataProps {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
function ColumnChart(props: ColumnChartProps) {
|
||||
const { data, compData, label } = props;
|
||||
const chartRef = React.useRef<HTMLDivElement>(null);
|
||||
const chartUuid = React.useRef<string>(
|
||||
Math.random().toString(36).substring(7)
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!chartRef.current) return;
|
||||
const chart = echarts.init(chartRef.current);
|
||||
(window as any).__seriesValueMap = (window as any).__seriesValueMap ?? {};
|
||||
(window as any).__seriesValueMap[chartUuid.current] = {};
|
||||
(window as any).__seriesColorMap = (window as any).__seriesColorMap ?? {};
|
||||
(window as any).__seriesColorMap[chartUuid.current] = {};
|
||||
|
||||
const { yAxisData, series } = buildColumnChart(chartUuid.current, data, compData);
|
||||
|
||||
chart.setOption({
|
||||
...defaultOptions,
|
||||
tooltip: {
|
||||
...defaultOptions.tooltip,
|
||||
formatter: customTooltipFormatter(chartUuid.current),
|
||||
},
|
||||
legend: {
|
||||
data: series
|
||||
.filter((s: any) => !s._hideInLegend)
|
||||
.map((s: any) => s.name),
|
||||
},
|
||||
grid: {
|
||||
...defaultOptions.grid,
|
||||
left: 40,
|
||||
right: 30,
|
||||
top: 40,
|
||||
bottom: 30,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01],
|
||||
name: label ?? 'Total',
|
||||
nameLocation: 'middle',
|
||||
nameGap: 35,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: yAxisData,
|
||||
},
|
||||
series,
|
||||
});
|
||||
|
||||
const obs = new ResizeObserver(() => chart.resize());
|
||||
obs.observe(chartRef.current);
|
||||
|
||||
return () => {
|
||||
chart.dispose();
|
||||
obs.disconnect();
|
||||
delete (window as any).__seriesValueMap[chartUuid.current];
|
||||
delete (window as any).__seriesColorMap[chartUuid.current];
|
||||
};
|
||||
}, [data, compData, label]);
|
||||
|
||||
return <div ref={chartRef} style={{ width: '100%', height: 240 }} />;
|
||||
}
|
||||
|
||||
export default ColumnChart;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { DataProps } from './utils';
|
||||
import type { DataProps, DataItem } from './utils';
|
||||
import { createDataset, assignColorsByBaseName } from './utils';
|
||||
|
||||
export function createBarSeries(
|
||||
|
|
@ -6,16 +6,13 @@ export function createBarSeries(
|
|||
datasetId: string,
|
||||
dashed: boolean,
|
||||
hideFromLegend: boolean,
|
||||
horizontal: boolean
|
||||
) {
|
||||
return data.namesMap.filter(Boolean).map((fullName) => {
|
||||
const baseName = fullName.replace(/^Previous\s+/, '');
|
||||
|
||||
const encode = horizontal
|
||||
? { x: fullName, y: 'idx' }
|
||||
: { x: 'idx', y: fullName };
|
||||
const encode = { x: 'idx', y: fullName };
|
||||
|
||||
const borderRadius = horizontal ? [0, 6, 6, 0] : [6, 6, 0, 0];
|
||||
const borderRadius = [6, 6, 0, 0];
|
||||
const decal = dashed ? { symbol: 'line', symbolSize: 10, rotation: 1 } : { symbol: 'none' };
|
||||
return {
|
||||
name: fullName,
|
||||
|
|
@ -31,15 +28,15 @@ export function createBarSeries(
|
|||
});
|
||||
}
|
||||
|
||||
export function buildBarDatasetsAndSeries(props: DataProps, horizontal = false) {
|
||||
export function buildBarDatasetsAndSeries(props: DataProps) {
|
||||
const mainDataset = createDataset('current', props.data);
|
||||
const mainSeries = createBarSeries(props.data, 'current', false, false, horizontal);
|
||||
const mainSeries = createBarSeries(props.data, 'current', false, false);
|
||||
|
||||
let compDataset: Record<string, any> | null = null;
|
||||
let compSeries: Record<string, any>[] = [];
|
||||
if (props.compData && props.compData.chart?.length) {
|
||||
compDataset = createDataset('previous', props.compData);
|
||||
compSeries = createBarSeries(props.compData, 'previous', true, true, horizontal);
|
||||
compSeries = createBarSeries(props.compData, 'previous', true, true);
|
||||
}
|
||||
|
||||
const datasets = compDataset ? [mainDataset, compDataset] : [mainDataset];
|
||||
|
|
@ -50,3 +47,95 @@ export function buildBarDatasetsAndSeries(props: DataProps, horizontal = false)
|
|||
return { datasets, series };
|
||||
}
|
||||
|
||||
|
||||
function sumSeries(chart: DataItem[], seriesName: string): number {
|
||||
return chart.reduce((acc, row) => acc + (Number(row[seriesName]) || 0), 0);
|
||||
}
|
||||
|
||||
export function buildColumnChart(chartUuid: string, data: DataProps['data'], compData: DataProps['compData'],) {
|
||||
const baseNamesSet = new Set<string>();
|
||||
|
||||
data.namesMap.filter(Boolean).forEach((fullName) => {
|
||||
const baseName = fullName.replace(/^Previous\s+/, '');
|
||||
baseNamesSet.add(baseName);
|
||||
});
|
||||
|
||||
if (compData && compData.chart?.length) {
|
||||
compData.namesMap.filter(Boolean).forEach((fullName) => {
|
||||
const baseName = fullName.replace(/^Previous\s+/, '');
|
||||
baseNamesSet.add(baseName);
|
||||
});
|
||||
}
|
||||
|
||||
const baseNames = Array.from(baseNamesSet); // e.g. ["Series 1","Series 2"]
|
||||
|
||||
const yAxisData = baseNames;
|
||||
|
||||
const series: any[] = [];
|
||||
|
||||
data.namesMap.filter(Boolean).forEach((fullName) => {
|
||||
const baseName = fullName.replace(/^Previous\s+/, '');
|
||||
const idx = baseNames.indexOf(baseName);
|
||||
|
||||
const val = sumSeries(data.chart, fullName);
|
||||
const dataArr = new Array(baseNames.length).fill(0);
|
||||
dataArr[idx] = val;
|
||||
(window as any).__seriesValueMap[chartUuid][
|
||||
`Previous ${fullName}`
|
||||
] = val;
|
||||
series.push({
|
||||
name: fullName,
|
||||
type: 'bar',
|
||||
barWidth: 16,
|
||||
data: dataArr,
|
||||
_hideInLegend: false,
|
||||
_baseName: baseName,
|
||||
itemStyle: {
|
||||
borderRadius: [0, 6, 6, 0],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (compData && compData.chart?.length) {
|
||||
compData.namesMap.filter(Boolean).forEach((fullName) => {
|
||||
const baseName = fullName.replace(/^Previous\s+/, '');
|
||||
const idx = baseNames.indexOf(baseName);
|
||||
const val = sumSeries(compData.chart, fullName);
|
||||
|
||||
const dataArr = new Array(baseNames.length).fill(0);
|
||||
dataArr[idx] = val;
|
||||
(window as any).__seriesValueMap[chartUuid][baseName] = val;
|
||||
series.push({
|
||||
name: fullName,
|
||||
type: 'bar',
|
||||
barWidth: 16,
|
||||
barGap: '1%',
|
||||
data: dataArr,
|
||||
_hideInLegend: true,
|
||||
_baseName: baseName,
|
||||
itemStyle: {
|
||||
borderRadius: [0, 6, 6, 0],
|
||||
decal: {
|
||||
show: true,
|
||||
symbol: 'line',
|
||||
symbolSize: 6,
|
||||
rotation: 1,
|
||||
dashArrayX: 4,
|
||||
dashArrayY: 4,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
assignColorsByBaseName(series);
|
||||
series.forEach((s) => {
|
||||
(window as any).__seriesColorMap[chartUuid][s.name] =
|
||||
s.itemStyle.color;
|
||||
});
|
||||
|
||||
return {
|
||||
yAxisData,
|
||||
series,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
import { formatTimeOrDate } from "App/date";
|
||||
import { formatTimeOrDate } from 'App/date';
|
||||
|
||||
export const colors = ['#394EFF', '#3EAAAF', '#9276da', '#ceba64', "#bc6f9d", '#966fbc', '#64ce86', '#e06da3', '#6dabe0'];
|
||||
export const colors = [
|
||||
'#394EFF',
|
||||
'#3EAAAF',
|
||||
'#9276da',
|
||||
'#ceba64',
|
||||
'#bc6f9d',
|
||||
'#966fbc',
|
||||
'#64ce86',
|
||||
'#e06da3',
|
||||
'#6dabe0',
|
||||
];
|
||||
//export const colors = ['#6774E2', '#929ACD', '#3EAAAF', '#565D97', '#8F9F9F', '#376F72'];
|
||||
// const colorsTeal = ['#1E889A', '#239DB2', '#28B2C9', '#36C0D7', '#65CFE1'];
|
||||
// const colorsx = ['#256669', '#38999e', '#3eaaaf', '#51b3b7', '#78c4c7', '#9fd5d7', '#c5e6e7'].reverse();
|
||||
|
|
@ -39,7 +49,6 @@ export function assignColorsByBaseName(series: any[]) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the hovered “current” or “previous” line + the matching partner (if it exists).
|
||||
*/
|
||||
|
|
@ -49,23 +58,80 @@ export function customTooltipFormatter(uuid: string) {
|
|||
// { seriesName, dataIndex, data, marker, color, encode, ... }
|
||||
if (!params) return '';
|
||||
const { seriesName, dataIndex } = params;
|
||||
const isPrevious = /^Previous\s+/.test(seriesName);
|
||||
const baseName = seriesName.replace(/^Previous\s+/, '');
|
||||
const partnerName = isPrevious ? baseName : `Previous ${baseName}`;
|
||||
|
||||
if (!Array.isArray(params.data)) {
|
||||
const partnerValue = (window as any).__seriesValueMap?.[uuid]?.[
|
||||
seriesName
|
||||
];
|
||||
let str = `
|
||||
<div class="flex flex-col gap-1 bg-white shadow border rounded p-2 z-50">
|
||||
<div class="flex gap-2 items-center">
|
||||
<div style="
|
||||
border-radius: 99px;
|
||||
background: ${params.color};
|
||||
width: 1rem;
|
||||
height: 1rem;">
|
||||
</div>
|
||||
<div class="font-medium text-black">${seriesName}</div>
|
||||
</div>
|
||||
|
||||
<div style="border-left: 2px solid ${
|
||||
params.color
|
||||
};" class="flex flex-col px-2 ml-2">
|
||||
<div class="text-neutral-600 text-sm">
|
||||
Total:
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="font-medium text-black">${params.value}</div>
|
||||
${buildCompareTag(params.value, partnerValue)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
if (partnerValue !== undefined) {
|
||||
const partnerColor =
|
||||
(window as any).__seriesColorMap?.[uuid]?.[partnerName] || '#999';
|
||||
str += `<div class="flex gap-2 items-center mt-2">
|
||||
<div style="
|
||||
border-radius: 99px;
|
||||
background: ${partnerColor};
|
||||
width: 1rem;
|
||||
height: 1rem;">
|
||||
</div>
|
||||
<div class="font-medium">${partnerName}</div>
|
||||
</div>
|
||||
<div style="border-left: 2px dashed ${partnerColor};" class="flex flex-col px-2 ml-2">
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="font-medium">${partnerValue ?? '—'}</div>
|
||||
${buildCompareTag(partnerValue, params.value)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
str += '</div>';
|
||||
|
||||
return str;
|
||||
}
|
||||
// 'value' of the hovered point
|
||||
const yKey = params.encode.y[0]; // "Series 1"
|
||||
const value = params.data?.[yKey];
|
||||
|
||||
const isPrevious = /^Previous\s+/.test(seriesName);
|
||||
const baseName = seriesName.replace(/^Previous\s+/, '');
|
||||
const timestamp = (window as any).__timestampMap?.[uuid]?.[dataIndex];
|
||||
const comparisonTimestamp = (window as any).__timestampCompMap?.[uuid]?.[dataIndex];
|
||||
const comparisonTimestamp = (window as any).__timestampCompMap?.[uuid]?.[
|
||||
dataIndex
|
||||
];
|
||||
|
||||
// Get partner’s value from some global map
|
||||
const partnerName = isPrevious ? baseName : `Previous ${baseName}`;
|
||||
const partnerVal = (window as any).__seriesValueMap?.[uuid]?.[partnerName]?.[dataIndex];
|
||||
|
||||
const partnerVal = (window as any).__seriesValueMap?.[uuid]?.[
|
||||
partnerName
|
||||
]?.[dataIndex];
|
||||
|
||||
const categoryLabel = (window as any).__categoryMap[uuid]
|
||||
? (window as any).__categoryMap[uuid][dataIndex]
|
||||
: dataIndex;
|
||||
? (window as any).__categoryMap[uuid][dataIndex]
|
||||
: dataIndex;
|
||||
|
||||
const firstTs = isPrevious ? comparisonTimestamp : timestamp;
|
||||
const secondTs = isPrevious ? timestamp : comparisonTimestamp;
|
||||
|
|
@ -81,7 +147,9 @@ export function customTooltipFormatter(uuid: string) {
|
|||
<div class="font-medium text-black">${seriesName}</div>
|
||||
</div>
|
||||
|
||||
<div style="border-left: 2px solid ${params.color};" class="flex flex-col px-2 ml-2">
|
||||
<div style="border-left: 2px solid ${
|
||||
params.color
|
||||
};" class="flex flex-col px-2 ml-2">
|
||||
<div class="text-neutral-600 text-sm">
|
||||
${firstTs ? formatTimeOrDate(firstTs) : categoryLabel}
|
||||
</div>
|
||||
|
|
@ -93,7 +161,8 @@ export function customTooltipFormatter(uuid: string) {
|
|||
`;
|
||||
|
||||
if (partnerVal !== undefined) {
|
||||
const partnerColor = (window as any).__seriesColorMap?.[uuid]?.[partnerName] || '#999';
|
||||
const partnerColor =
|
||||
(window as any).__seriesColorMap?.[uuid]?.[partnerName] || '#999';
|
||||
tooltipContent += `
|
||||
<div class="flex gap-2 items-center mt-2">
|
||||
<div style="
|
||||
|
|
@ -118,7 +187,7 @@ export function customTooltipFormatter(uuid: string) {
|
|||
|
||||
tooltipContent += '</div>';
|
||||
return tooltipContent;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -156,7 +225,6 @@ function buildCompareTag(val: number, prevVal: number): string {
|
|||
`;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build category labels (["Sun", "Mon", ...]) from the "current" data only
|
||||
*/
|
||||
|
|
@ -169,15 +237,13 @@ export function buildCategories(data: DataProps['data']): string[] {
|
|||
* The `idx` dimension aligns with xAxis = "category"
|
||||
* (which is dates in our case)
|
||||
*/
|
||||
export function createDataset(
|
||||
id: string,
|
||||
data: DataProps['data']
|
||||
) {
|
||||
export function createDataset(id: string, data: DataProps['data']) {
|
||||
const dimensions = ['idx', ...data.namesMap];
|
||||
const source = data.chart.map((item, idx) => {
|
||||
const row: (number | undefined)[] = [idx];
|
||||
data.namesMap.forEach((name) => {
|
||||
const val = typeof item[name] === 'number' ? (item[name] as number) : undefined;
|
||||
const val =
|
||||
typeof item[name] === 'number' ? (item[name] as number) : undefined;
|
||||
row.push(val);
|
||||
});
|
||||
return row;
|
||||
|
|
@ -210,9 +276,9 @@ export function createSeries(
|
|||
_hideInLegend: hideFromLegend,
|
||||
itemStyle: { opacity: 1 },
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: { opacity: 1 },
|
||||
lineStyle: { opacity: 1 },
|
||||
focus: 'series',
|
||||
itemStyle: { opacity: 1 },
|
||||
lineStyle: { opacity: 1 },
|
||||
},
|
||||
blur: {
|
||||
itemStyle: { opacity: 0.2 },
|
||||
|
|
@ -240,8 +306,7 @@ export function buildDatasetsAndSeries(props: DataProps) {
|
|||
return { datasets, series };
|
||||
}
|
||||
|
||||
|
||||
interface DataItem {
|
||||
export interface DataItem {
|
||||
time: string;
|
||||
timestamp: number;
|
||||
[seriesName: string]: number | string;
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
data: { chart: any[], namesMap: string[] };
|
||||
compData: { chart: any[], namesMap: string[] } | null;
|
||||
params: any;
|
||||
colors: any;
|
||||
onClick?: (event, index) => void;
|
||||
yaxis?: any;
|
||||
label?: string;
|
||||
hideLegend?: boolean;
|
||||
}
|
||||
|
||||
function ProgressBarChart(props: Props) {
|
||||
const {
|
||||
data = { chart: [], namesMap: [] },
|
||||
compData = { chart: [], namesMap: [] },
|
||||
colors,
|
||||
onClick = () => null,
|
||||
label = 'Number of Sessions',
|
||||
} = props;
|
||||
|
||||
const getTotalForSeries = (series: string, isComp: boolean) => {
|
||||
if (isComp) {
|
||||
if (!compData) return 0;
|
||||
return compData.chart.reduce((acc, curr) => acc + curr[series], 0);
|
||||
}
|
||||
return data.chart.reduce((acc, curr) => acc + curr[series], 0);
|
||||
}
|
||||
|
||||
const formattedNumber = (num: number) => {
|
||||
return Intl.NumberFormat().format(num);
|
||||
}
|
||||
|
||||
// Group the data into pairs (original + comparison)
|
||||
const groupedData: Array<{ original: any, comparison: any }> = [];
|
||||
for (let i = 0; i < data.namesMap.length; i++) {
|
||||
if (!data.namesMap[i]) continue;
|
||||
|
||||
const original = {
|
||||
name: data.namesMap[i],
|
||||
value: getTotalForSeries(data.namesMap[i], false),
|
||||
isComp: false,
|
||||
index: i
|
||||
};
|
||||
|
||||
const comparison = compData && compData.namesMap[i] ? {
|
||||
name: compData.namesMap[i],
|
||||
value: getTotalForSeries(compData.namesMap[i], true),
|
||||
isComp: true,
|
||||
index: i
|
||||
} : null;
|
||||
|
||||
groupedData.push({ original, comparison });
|
||||
}
|
||||
|
||||
// Find highest value among all data points
|
||||
const highest = groupedData.reduce((acc, curr) => {
|
||||
const maxInGroup = Math.max(
|
||||
curr.original.value,
|
||||
curr.comparison ? curr.comparison.value : 0
|
||||
);
|
||||
return Math.max(acc, maxInGroup);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-3 ps-3 justify-center" style={{ height: 240 }}>
|
||||
{groupedData.map((group, i) => (
|
||||
<div key={i} className={`flex flex-col ${i < groupedData.length - 1 && group.comparison ? 'border-b border-dashed border-[0,0,0,.15] pb-3' : ''}`}>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center" style={{ flex: 1 }}>
|
||||
<div
|
||||
className="w-4 h-4 rounded-full mr-2"
|
||||
style={{ backgroundColor: colors[group.original.index] }}
|
||||
/>
|
||||
<span>{group.original.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2" style={{ flex: 4 }}>
|
||||
<div
|
||||
style={{
|
||||
height: 8,
|
||||
borderRadius: 8,
|
||||
backgroundColor: colors[group.original.index],
|
||||
width: `${(group.original.value/highest)*100}%`
|
||||
}}
|
||||
/>
|
||||
<div>{formattedNumber(group.original.value)}</div>
|
||||
</div>
|
||||
<div style={{ flex: 1 }} />
|
||||
</div>
|
||||
{group.comparison && (
|
||||
<div className="flex items-center">
|
||||
<div className="invisible flex items-center" style={{ flex: 1 }}>
|
||||
<div
|
||||
className="w-4 h-4 rounded-full mr-2"
|
||||
style={{ backgroundColor: colors[group.comparison.index] }}
|
||||
/>
|
||||
<span>{group.comparison.name}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2" style={{ flex: 4 }}>
|
||||
<div
|
||||
style={{
|
||||
height: 8,
|
||||
borderRadius: 8,
|
||||
backgroundImage: `repeating-linear-gradient(45deg, #ffffff 0px, #ffffff 1.5px, ${colors[group.comparison.index]} 1.5px, ${colors[group.comparison.index]} 4.5px)`,
|
||||
backgroundSize: '20px 20px',
|
||||
width: `${(group.comparison.value/highest)*100}%`
|
||||
}}
|
||||
/>
|
||||
<div>{formattedNumber(group.comparison.value)}</div>
|
||||
</div>
|
||||
<div style={{ flex: 1 }} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProgressBarChart;
|
||||
|
|
@ -2,13 +2,13 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||
import LineChart from 'App/components/Charts/LineChart'
|
||||
import BarChart from 'App/components/Charts/BarChart'
|
||||
import PieChart from 'App/components/Charts/PieChart'
|
||||
import ColumnChart from 'App/components/Charts/ColumnChart'
|
||||
import CustomMetricPercentage from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage';
|
||||
import { Styles } from 'App/components/Dashboard/Widgets/common';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Icon, Loader } from 'UI';
|
||||
import { useStore } from 'App/mstore';
|
||||
import FunnelTable from "../../../Funnels/FunnelWidget/FunnelTable";
|
||||
import ProgressBarChart from '../../Widgets/CustomMetricsWidgets/ProgressBarChart';
|
||||
import BugNumChart from '../../Widgets/CustomMetricsWidgets/BigNumChart';
|
||||
import WidgetDatatable from '../WidgetDatatable/WidgetDatatable';
|
||||
import WidgetPredefinedChart from '../WidgetPredefinedChart';
|
||||
|
|
@ -335,8 +335,9 @@ function WidgetChart(props: Props) {
|
|||
|
||||
if (viewType === 'progressChart') {
|
||||
return (
|
||||
<ProgressBarChart
|
||||
<ColumnChart
|
||||
inGrid={!props.isPreview}
|
||||
horizontal
|
||||
data={chartData}
|
||||
compData={compData}
|
||||
params={params}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue