@pagamio/frontend-commons-lib
Version:
Pagamio library for Frontend reusable components like the form engine and table container
190 lines (189 loc) • 11.1 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 ChartDetailsModal from '../components/ChartDetailsModal';
import ChartWrapper from '../components/ChartWrapper';
import { useChartData } from '../hooks/useChartData';
import { DashboardPaths } from '../utils';
import { createDistributionMetricOptions, getChartBarOptions, getLineGraphOptions } from '../utils/chartOptions';
import { createTooltipFormatter, processTooltipFields } from '../utils/tooltipUtils';
const LineGraph = ({ title, yAxisLabel, xAxisLabel, color = '#60A5FA', className = '', xAxisDataKey, yAxisDataKey, categoryDataKey, url = DashboardPaths.QUERY, query, chartToolTip, tooltipValueFormat, tooltipTitle = 'Value', tooltipUnit = '', tooltipAdditionalFields = [], yAxisLabelFormatter, metricDetailData, showDetailsModal = true, ...props }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { data: metricData = [], error, isEmpty, loading, refresh } = useChartData(url, query);
const processData = useCallback((data) => {
return data.map((item) => ({
name: item[xAxisDataKey],
value: item[yAxisDataKey],
category: item[categoryDataKey],
}));
}, [xAxisDataKey, yAxisDataKey, categoryDataKey]);
const processLineGraphData = (data) => {
const groupedData = {};
data.forEach((transaction) => {
const category = transaction[categoryDataKey] ?? props.seriesName ?? xAxisLabel;
if (!groupedData[category]) {
groupedData[category] = [];
}
groupedData[category].push({
date: transaction[xAxisDataKey],
value: transaction[yAxisDataKey],
});
});
return Object.entries(groupedData).map(([name, data]) => ({
name,
data,
}));
};
const statisticsUrl = metricDetailData?.statisticsUrl ?? DashboardPaths.METRICS;
const statisticsHooks = {
item: useChartData(statisticsUrl, {
...metricDetailData.itemStatisticsQuery.query,
}),
value: useChartData(statisticsUrl, {
...metricDetailData.valueMetricsQuery.query,
}),
average: useChartData(statisticsUrl, {
...metricDetailData.averageMetricsQuery.query,
}),
distribution: useChartData(url, {
...metricDetailData.distributionMetricsQuery.query,
}),
};
const processedTooltipFields = useMemo(() => processTooltipFields(tooltipAdditionalFields), [tooltipAdditionalFields]);
const processedDistributionTooltipFields = useMemo(() => processTooltipFields(metricDetailData.distributionChartTooltip.tooltipAdditionalFields), [metricDetailData.distributionChartTooltip.tooltipAdditionalFields]);
const createTooltipConfig = useCallback((tooltipOverrides) => ({
tooltipTitle: tooltipOverrides?.tooltipTitle ?? tooltipTitle,
tooltipUnit: tooltipOverrides?.tooltipUnit ?? tooltipUnit,
tooltipFields: tooltipOverrides?.tooltipFields?.map((field) => ({
...field,
label: field.label ?? '',
toolTipkey: field.toolTipkey ?? '',
valueKey: field.valueKey ?? '',
nameKey: field.nameKey ?? '',
suffix: field.suffix ?? '',
format: field.format ?? 'number',
formatter: field.formatter ?? ((value) => String(value)),
})) ?? processedTooltipFields,
data: tooltipOverrides?.data,
}), [tooltipTitle, tooltipUnit, processedTooltipFields]);
const createChartOptions = useCallback((data, config) => {
const currency = data?.[0]?.currency;
const tooltipSettings = createTooltipConfig(config.tooltipConfig);
const formatterFn = createTooltipFormatter(tooltipValueFormat, tooltipSettings.tooltipTitle, tooltipSettings.tooltipUnit, tooltipSettings.tooltipFields, tooltipSettings.data ?? data, currency);
if (config.processLineGraphOptions) {
return getLineGraphOptions({
title: config.title,
yAxisLabel: yAxisLabel,
chartToolTip: {
...chartToolTip,
formatter: formatterFn,
},
data,
yAxisLabelFormatter,
});
}
else {
return getChartBarOptions({
title: config.title,
yAxisDataNameKey: metricDetailData.yAxisDataNameKey,
xAxisNameKey: metricDetailData.xAxisNameKey,
xAxisLabel: metricDetailData.xAxisLabel,
yAxisLabel: metricDetailData.yAxisLabel,
chartToolTip: {
...chartToolTip,
formatter: formatterFn,
},
data,
});
}
}, [createTooltipConfig, tooltipValueFormat, color, metricDetailData, chartToolTip]);
const fullDataHook = useChartData(url, {
...query,
limit: undefined,
});
const lineChartSeriesData = useMemo(() => processLineGraphData(metricData ?? []), [metricData]);
const chartBarOptions = useMemo(() => createChartOptions(lineChartSeriesData, { title, processLineGraphOptions: true }), [metricData]);
const fullChartSeriesData = useMemo(() => processData(fullDataHook.data ?? []), [fullDataHook.data]);
const fullChartOptions = createChartOptions(fullChartSeriesData, { title, processLineGraphOptions: false });
const { topFiveItems, bottomFiveItems } = useMemo(() => {
if (!fullDataHook.data?.length) {
return {
topFiveItems: getChartBarOptions({
data: [],
title: metricDetailData.topFiveChartTitle,
yAxisDataNameKey: metricDetailData.xAxisNameKey,
xAxisNameKey: metricDetailData.yAxisDataNameKey,
xAxisLabel: metricDetailData.yAxisLabel,
yAxisLabel: metricDetailData.xAxisLabel,
chartToolTip,
}),
bottomFiveItems: getChartBarOptions({
data: [],
title: metricDetailData.bottomFiveChartTitle,
yAxisDataNameKey: metricDetailData.xAxisNameKey,
xAxisNameKey: metricDetailData.yAxisDataNameKey,
xAxisLabel: metricDetailData.yAxisLabel,
yAxisLabel: metricDetailData.xAxisLabel,
chartToolTip,
}),
};
}
const topFiveMetric = fullDataHook.data.slice(0, 5);
const bottomFiveMetric = fullDataHook.data.slice(-5).reverse();
return {
topFiveItems: createChartOptions(topFiveMetric, {
title: metricDetailData.topFiveChartTitle,
tooltipConfig: { tooltipTitle: metricDetailData.topFiveChartTitle },
processLineGraphOptions: false,
}),
bottomFiveItems: createChartOptions(bottomFiveMetric, {
title: metricDetailData.bottomFiveChartTitle,
tooltipConfig: { tooltipTitle: metricDetailData.bottomFiveChartTitle },
processLineGraphOptions: false,
}),
};
}, [fullDataHook, createChartOptions, metricDetailData, processData, color, chartToolTip]);
const distributionMetricData = useMemo(() => {
return createDistributionMetricOptions({
distributionChartTooltip: metricDetailData.distributionChartTooltip,
distributionData: statisticsHooks.distribution.data ?? [],
processedTooltipFields: processedDistributionTooltipFields,
xAxisDataValueKey: metricDetailData.distributionMetricsQuery.xAxisDataValueKey,
seriesDataValueKey: metricDetailData.distributionMetricsQuery.seriesDataValueKey,
});
}, [
statisticsHooks.distribution.data,
metricDetailData.distributionMetricsQuery.seriesDataValueKey,
metricDetailData.distributionMetricsQuery.xAxisDataValueKey,
metricDetailData.distributionChartTooltip,
processedDistributionTooltipFields,
]);
const createMetricsData = useCallback((hook, query) => ({
value: hook.data?.[query.valueKey] ?? 0,
previousValue: query.previousValueKey ? (hook.data?.[query.previousValueKey] ?? 0) : undefined,
change: hook.data?.[query.changeKey] ?? 0,
title: query.title,
format: query.format,
isEmpty: hook.isEmpty,
loading: hook.loading,
error: hook.error,
currency: hook.data?.additionalData?.currency,
refresh: () => hook.refresh().catch(() => { }),
}), []);
const metricsData = useMemo(() => ({
item: createMetricsData(statisticsHooks.item, metricDetailData.itemStatisticsQuery),
value: createMetricsData(statisticsHooks.value, metricDetailData.valueMetricsQuery),
average: createMetricsData(statisticsHooks.average, metricDetailData.averageMetricsQuery),
}), [createMetricsData, statisticsHooks, metricDetailData]);
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: '350px' } }) }) }) }), showDetailsModal && (_jsx(ChartDetailsModal, { title: metricDetailData.title, isOpen: isModalOpen, onClose: () => setIsModalOpen(false), data: fullDataHook.data ?? [], columns: metricDetailData.dataGridColumns, error: fullDataHook.error, isEmpty: fullDataHook.isEmpty, loading: fullDataHook.loading, detailsTableTitle: metricDetailData.detailsTableTitle, refresh: fullDataHook.refresh, renderDetailsChart: fullDataHook.data ? fullDataHook.data.length <= 40 : false, chartData: fullChartOptions, top5PerformingItems: topFiveItems, bottom5NonPerformingItems: bottomFiveItems, valueKey: metricDetailData.valueKey, dataGridQuery: query, searchKey: metricDetailData.dataGridSearchKey, searchInputPlaceHolder: metricDetailData.dataSearchInputPlaceHolder, itemMetricsData: metricsData.item, valueMetricsData: metricsData.value, averageMetricsData: metricsData.average, distributionMetricsData: {
data: distributionMetricData ?? [],
title: metricDetailData.distributionMetricsQuery.title,
isEmpty: statisticsHooks.distribution.isEmpty,
loading: statisticsHooks.distribution.loading,
error: statisticsHooks.distribution.error,
refresh: () => statisticsHooks.distribution.refresh().catch(() => { }),
chartToolTip: metricDetailData.distributionMetricsQuery.chartToolTip,
} }))] }));
};
export default LineGraph;