UNPKG

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
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;