@pagamio/frontend-commons-lib
Version:
Pagamio library for Frontend reusable components like the form engine and table container
132 lines (131 loc) • 10.5 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import ReactECharts from 'echarts-for-react';
import { useCallback, useMemo, useState } from 'react';
import Card from '../components/CardWrapper';
import ChartDetailsModalWrapper from '../components/ChartDetailsModalWrapper';
import ChartWrapper from '../components/ChartWrapper';
import { useChartData } from '../hooks/useChartData';
import { useTooltipFormatter } from '../hooks/useTooltipFormatter';
import { DashboardPaths, processTooltipFields } from '../utils';
import { createDistributionMetricOptions, getChartBarOptions } from '../utils/chartOptions';
import { createTooltipFormatter } from '../utils/tooltipUtils';
// Add sortMetricData function
export const sortMetricData = (data, valueKey, sortOrder = 'desc') => {
return [...data].sort((a, b) => {
const aValue = Number(a[valueKey]) || 0;
const bValue = Number(b[valueKey]) || 0;
return sortOrder === 'asc' ? aValue - bValue : bValue - aValue;
});
};
const constructMetricData = (baseData, queryConfig, isEmpty, loading, error, refreshFn, currency) => ({
value: baseData?.[queryConfig.valueKey] ?? 0,
previousValue: queryConfig.previousValueKey ? (baseData?.[queryConfig.previousValueKey] ?? 0) : undefined,
change: baseData?.[queryConfig.changeKey] ?? 0,
title: queryConfig.title,
format: queryConfig.format,
isEmpty,
loading,
currency,
error,
refresh: () => {
refreshFn().catch(() => { });
},
});
const HorizontalBarChart = ({ title, colorScheme = ['#60A5FA', '#93C5FD'], className = '', url = DashboardPaths.QUERY, tooltipValueFormat, query, sortOrder = 'desc', tooltipTitle = 'Value', tooltipUnit = '', tooltipAdditionalFields = [], currencyDisplaySymbol, metricDetailData, showDetailsModal = true, ...props }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { data: metricData = [], error, isEmpty, loading, refresh } = useChartData(url, query);
const statisticsUrl = metricDetailData?.statisticsUrl ?? DashboardPaths.METRICS;
const { data: fullMetricData = [], error: fullDataError, isEmpty: isfullMetricDataEmpty, loading: isLoadingFullMetricData, refresh: refreshFullMetricData, } = useChartData(url, {
...query,
limit: undefined,
});
const useStatisticData = (queryProps) => {
const { data, error, isEmpty, loading, refresh } = useChartData(statisticsUrl, {
...queryProps.query,
});
return { data, error, isEmpty, loading, refresh };
};
const { data: itemStatData, error: itemStatDataError, isEmpty: isitemStatMetricDataEmpty, loading: isLoadingItemStatlMetricData, refresh: refreshItemStatMetricData, } = useStatisticData(metricDetailData.itemStatisticsQuery);
const { data: valueStatData, error: valueStatDataError, isEmpty: isValueStatMetricDataEmpty, loading: isLoadingValueStatlMetricData, refresh: refreshValueStatMetricData, } = useStatisticData(metricDetailData.valueMetricsQuery);
const { data: averageStatData, error: averageStatDataError, isEmpty: isAverageStatMetricDataEmpty, loading: isLoadingAverageStatlMetricData, refresh: refreshAverageStatMetricData, } = useStatisticData(metricDetailData.averageMetricsQuery);
const { data: distributionStatData, error: distributionStatDataError, isEmpty: isDistributionStatMetricDataEmpty, loading: isLoadingDistributionStatlMetricData, refresh: refreshDistributionStatMetricData, } = useChartData(url, {
...metricDetailData.distributionMetricsQuery.query,
});
const sortedMetricData = useMemo(() => sortMetricData(metricData, metricDetailData.valueKey, sortOrder), [metricData, metricDetailData.valueKey, sortOrder]);
const sortedFullMetricData = useMemo(() => sortMetricData(fullMetricData, metricDetailData.valueKey, sortOrder), [fullMetricData, metricDetailData.valueKey, sortOrder]);
const { processedTooltipFields, tooltipFormatterFn } = useTooltipFormatter(tooltipValueFormat, tooltipTitle, tooltipUnit, tooltipAdditionalFields, metricData, currencyDisplaySymbol);
const processedDistributionTooltipFields = useMemo(() => processTooltipFields(metricDetailData.distributionChartTooltip.tooltipAdditionalFields), [metricDetailData.distributionChartTooltip.tooltipAdditionalFields]);
const createChartOptions = useCallback((data, title, colorScheme, metricDetailData, tooltipConfig) => {
const currency = data?.[0]?.currency;
const formatterFn = tooltipConfig
? createTooltipFormatter(tooltipValueFormat, tooltipConfig.tooltipTitle ?? tooltipTitle, tooltipConfig.tooltipUnit ?? tooltipUnit, tooltipConfig.tooltipFields ?? processedTooltipFields, tooltipConfig.data ?? data, currency, currencyDisplaySymbol)
: tooltipFormatterFn;
return getChartBarOptions({
title,
colorScheme,
yAxisDataNameKey: metricDetailData.yAxisDataNameKey,
xAxisNameKey: metricDetailData.xAxisNameKey,
xAxisLabel: metricDetailData.xAxisLabel,
yAxisLabel: metricDetailData.yAxisLabel,
chartToolTip: {
...metricDetailData.chartToolTip,
formatter: formatterFn,
},
data,
});
}, [tooltipFormatterFn, tooltipTitle, tooltipUnit, processedTooltipFields]);
const createChartOptionsWithData = useCallback((data, tooltipConfig) => createChartOptions(data, title, colorScheme, metricDetailData, tooltipConfig), [title, colorScheme, metricDetailData, createChartOptions]);
const chartBarOptions = useMemo(() => createChartOptionsWithData(sortedMetricData), [sortedMetricData, createChartOptionsWithData]);
const fullChartOptions = useMemo(() => createChartOptionsWithData(sortedFullMetricData), [sortedFullMetricData, createChartOptionsWithData]);
const { topFiveItems, bottomFiveItems } = useMemo(() => {
if (!sortedFullMetricData.length) {
return {
topFiveItems: [],
bottomFiveItems: [],
};
}
const topFiveMetric = sortedFullMetricData.slice(0, 5);
const bottomFiveMetric = sortedFullMetricData.slice(-5).reverse();
return {
topFiveItems: createChartOptionsWithData(topFiveMetric, {
tooltipTitle: metricDetailData.topFiveChartTitle,
tooltipUnit,
tooltipFields: processedTooltipFields,
data: topFiveMetric,
}),
bottomFiveItems: createChartOptionsWithData(bottomFiveMetric, {
tooltipTitle: metricDetailData.bottomFiveChartTitle,
tooltipUnit,
tooltipFields: processedTooltipFields,
data: bottomFiveMetric,
}),
};
}, [sortedFullMetricData, createChartOptionsWithData, tooltipUnit, processedTooltipFields]);
const distributionMetricData = useMemo(() => {
return createDistributionMetricOptions({
distributionChartTooltip: metricDetailData.distributionChartTooltip,
distributionData: distributionStatData ?? [],
processedTooltipFields: processedDistributionTooltipFields,
xAxisDataValueKey: metricDetailData.distributionMetricsQuery.xAxisDataValueKey,
seriesDataValueKey: metricDetailData.distributionMetricsQuery.seriesDataValueKey,
});
}, [
distributionStatData,
metricDetailData.distributionMetricsQuery.seriesDataValueKey,
metricDetailData.distributionMetricsQuery.xAxisDataValueKey,
metricDetailData.distributionChartTooltip,
processedDistributionTooltipFields,
]);
return (_jsxs(_Fragment, { children: [_jsx(Card, { title: title, ...props, onOpenDetails: metricData ? () => setIsModalOpen(true) : undefined, children: _jsx(ChartWrapper, { loading: loading, error: error, isEmpty: isEmpty, onRetry: refresh, children: _jsx("div", { className: "mt-[19px]", children: _jsx(ReactECharts, { option: chartBarOptions, style: { height: '100%', minHeight: '300px' }, className: "h-[300px] md:h-[400px]", opts: { renderer: 'canvas' }, notMerge: true }) }) }) }), showDetailsModal && (_jsx(ChartDetailsModalWrapper, { metricDetailData: metricDetailData, isModalOpen: isModalOpen, setIsModalOpen: setIsModalOpen, fullData: fullMetricData, columns: metricDetailData.dataGridColumns, error: fullDataError, isEmpty: isfullMetricDataEmpty, loading: isLoadingFullMetricData, detailsTableTitle: metricDetailData.detailsTableTitle, refresh: refreshFullMetricData, renderDetailsChart: fullMetricData.length <= 40, chartData: fullChartOptions, top5PerformingItems: topFiveItems, bottom5NonPerformingItems: bottomFiveItems, valueKey: metricDetailData.valueKey, dataGridQuery: query, searchKey: metricDetailData.dataGridSearchKey, searchInputPlaceHolder: metricDetailData.dataSearchInputPlaceHolder, itemMetricsData: constructMetricData(itemStatData, metricDetailData.itemStatisticsQuery, isitemStatMetricDataEmpty, isLoadingItemStatlMetricData, itemStatDataError, refreshItemStatMetricData, itemStatData?.additionalData?.currency ?? undefined), valueMetricsData: constructMetricData(valueStatData, metricDetailData.valueMetricsQuery, isValueStatMetricDataEmpty, isLoadingValueStatlMetricData, valueStatDataError, refreshValueStatMetricData, valueStatData?.additionalData?.currency ?? undefined), averageMetricsData: constructMetricData(averageStatData, metricDetailData.averageMetricsQuery, isAverageStatMetricDataEmpty, isLoadingAverageStatlMetricData, averageStatDataError, refreshAverageStatMetricData, averageStatData?.additionalData?.currency ?? undefined), distributionMetricsData: {
data: distributionMetricData ?? [],
title: metricDetailData.distributionMetricsQuery.title,
isEmpty: isDistributionStatMetricDataEmpty,
loading: isLoadingDistributionStatlMetricData,
error: distributionStatDataError,
refresh: () => {
refreshDistributionStatMetricData().catch(() => { });
},
chartToolTip: metricDetailData.distributionMetricsQuery.chartToolTip,
}, topFivePerformingChartTitle: metricDetailData.topFiveChartTitle, bottomFivePerformingChartTitle: metricDetailData.bottomFiveChartTitle }))] }));
};
export default HorizontalBarChart;