@pagamio/frontend-commons-lib
Version:
Pagamio library for Frontend reusable components like the form engine and table container
200 lines (199 loc) • 10.1 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Grid } from '@mantine/core';
import { Card } from 'flowbite-react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useApi } from '../api';
import { FilterComponent, Tab } from '../components';
import { useMediaQueries } from '../shared';
import ErrorState from './components/ErrorState';
import FilterComponentSkeleton from './components/FilterComponentSkeleton';
import { DashboardPaths, getFilterOptionsData, getGridColProps } from './utils';
import componentRegistry from './visualRegistry';
const OverviewContent = ({ visualData, renderContent, renderVisual }) => {
return (_jsxs(_Fragment, { children: [renderContent(), _jsx("main", { children: visualData.map((section) => (_jsxs("div", { className: "mb-2", children: [section.sectionTitle && _jsx("h2", { className: "mb-2 text-1xl font-semibold", children: section.sectionTitle }), _jsx(Grid, { gutter: "md", align: "stretch", className: "mb-[6px]", children: section.data.map((visual) => {
const numVisuals = section.data.length;
const { span, offset } = getGridColProps(numVisuals, visual.id);
return renderVisual(visual, visual.id, section.sectionTitle, visual?.gridColSpan ?? span, offset);
}) })] }, section.id))) })] }));
};
const EventsContent = ({ eventsVisualData, renderContent, renderVisual }) => {
return (_jsxs(_Fragment, { children: [renderContent(), _jsx("main", { children: eventsVisualData
? eventsVisualData.map((section) => (_jsxs("div", { className: "mb-2", children: [section.sectionTitle && _jsx("h2", { className: "mb-2 text-1xl font-semibold", children: section.sectionTitle }), _jsx(Grid, { gutter: "md", align: "stretch", className: "mb-[6px]", children: section.data.map((visual) => {
const numVisuals = section.data.length;
const { span, offset } = getGridColProps(numVisuals, visual.id);
return renderVisual(visual, visual.id, section.sectionTitle, visual?.gridColSpan ?? span, offset);
}) })] }, section.id)))
: null })] }));
};
const renderFilterComponent = ({ isLoading, error, filters, filterOptions, selectedFilters, handleFilterChange, handleApplyFilters, resetFilters, handleRetry, isNarrow, }) => {
if (isLoading) {
return _jsx(FilterComponentSkeleton, {});
}
if (error) {
return (_jsx(Card, { className: "mb-5", children: _jsx(ErrorState, { error: error, onRetry: handleRetry }) }));
}
const resolveFilterType = (filter) => {
if (filter.type === 'date-range')
return 'date-range';
if (filter.type === 'date')
return 'date';
if (filter.type === 'multi-select')
return 'multi-select';
if (filter.type === 'select')
return 'select';
return filter.multi ? 'multi-select' : 'select';
};
return (_jsx(FilterComponent, { filters: filters.map((filter) => ({
name: filter.name,
placeholder: filter.placeholder ?? `Select ${filter.name}`,
type: resolveFilterType(filter),
options: filterOptions[filter.name] || filter.options,
rangeKeys: filter.rangeKeys,
})), showClearFilters: true, selectedFilters: selectedFilters, handleFilterChange: handleFilterChange, handleApplyFilters: handleApplyFilters, resetFilters: resetFilters, isNarrow: isNarrow }));
};
const DashboardWrapper = ({ data, showVisualHeader = true, showEventsTabbedLayout = false, handleFilterChange = () => { }, selectedFilters = {}, tourSelectedFilters = {}, resetFilters = () => { }, handleApplyFilters = () => { }, handleApplyTourFilters = () => { }, handleTourFilterChange = () => { }, }) => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [tourError, setTourError] = useState(null);
const [isRefreshOpttonsQuery, setIsRefreshOpttonsQuery] = useState(0);
const [filterOptions, setFilterOptions] = useState({});
const [tourFilterOptions, setTourFilterOptions] = useState({});
const { isSm, isXs } = useMediaQueries();
const apiClient = useApi();
const { config, visualData, eventsVisualData } = data;
const defaultTabIndex = 0;
const fetchFilterData = async (key, valueKey, url, query, isTourFilter = false) => {
try {
setLoading(true);
if (isTourFilter) {
setTourError(null);
}
else {
setError(null);
}
const response = await apiClient.post(url, query);
if (response) {
const { optionsData } = getFilterOptionsData(response, key, valueKey);
if (isTourFilter) {
setTourFilterOptions((prev) => ({ ...prev, [key]: optionsData }));
}
else {
setFilterOptions((prev) => ({ ...prev, [key]: optionsData }));
}
}
}
catch (err) {
const errorMessage = 'Failed to fetch metric filter data';
if (isTourFilter) {
setTourError(err instanceof Error ? err : new Error(errorMessage));
}
else {
setError(err instanceof Error ? err : new Error(errorMessage));
}
}
finally {
setLoading(false);
}
};
useEffect(() => {
const fetchFilters = (filters, isTour = false) => {
filters.forEach((filter) => {
if (filter.query) {
const queryUrl = isTour ? (filter.tourUrl ?? DashboardPaths.QUERY) : (filter.url ?? DashboardPaths.QUERY);
fetchFilterData(filter.name, filter.valueKey, queryUrl, filter.query, isTour);
}
});
};
fetchFilters(config.filters);
if (config.tourFilters) {
fetchFilters(config.tourFilters, true);
}
}, [config.filters, isRefreshOpttonsQuery]);
const handleRetry = () => {
setIsRefreshOpttonsQuery((prev) => prev + 1);
};
const renderTourFilter = () => {
return renderFilterComponent({
isLoading: loading,
error: tourError,
filters: config.tourFilters || [],
filterOptions: tourFilterOptions,
selectedFilters: tourSelectedFilters,
handleFilterChange: handleTourFilterChange,
handleApplyFilters: handleApplyTourFilters,
resetFilters,
handleRetry,
isNarrow: isSm,
});
};
const dashboardFilters = useMemo(() => {
const baseFilters = [...config.filters];
const dateRangeValue = typeof selectedFilters?.dateRange === 'string' ? selectedFilters.dateRange.toUpperCase() : '';
if (dateRangeValue === 'CUSTOM_RANGE') {
baseFilters.push({
name: 'customDateRange',
label: 'Custom Date Range',
placeholder: 'Select date range',
type: 'date-range',
options: [],
multi: false,
rangeKeys: {
start: 'startDate',
end: 'endDate',
},
});
}
return baseFilters;
}, [config.filters, selectedFilters?.dateRange]);
const renderContent = () => {
return renderFilterComponent({
isLoading: loading,
error,
filters: dashboardFilters,
filterOptions,
selectedFilters,
handleFilterChange,
handleApplyFilters,
resetFilters,
handleRetry,
isNarrow: isSm,
});
};
const renderVisual = useCallback((visual, index, sectionTitle, span, offset) => {
const { metricData } = visual;
const matchesSmall = isXs || isSm;
const VisualComponent = componentRegistry[metricData.type];
if (!VisualComponent) {
console.error(`Visual type "${metricData.type}" not recognized in section "${sectionTitle}" at visual index ${index}.`);
return null;
}
const props = {
data: metricData.data,
options: metricData.options,
title: metricData.title,
label: metricData.label,
colors: metricData.colors,
columns: metricData.columns,
format: metricData.format,
stacked: metricData.stacked ?? undefined,
themeColor: config.themeColor,
currencyDisplaySymbol: metricData.currencyDisplaySymbol,
...metricData,
};
return (_jsx(Grid.Col, { span: matchesSmall ? 12 : span, offset: offset, children: _jsx(VisualComponent, { ...props }) }, `visual-${visual.id}`));
}, [config.themeColor, isXs, isSm]);
const tabs = [
{
id: 1,
title: 'Overview',
content: _jsx(OverviewContent, { visualData: visualData, renderContent: renderContent, renderVisual: renderVisual }),
},
{
id: 2,
title: 'Tour',
content: (_jsx(EventsContent, { eventsVisualData: eventsVisualData, renderContent: renderTourFilter, renderVisual: renderVisual })),
},
];
return (_jsxs("div", { children: [showVisualHeader && (_jsxs("div", { className: "mb-4", children: [_jsx("h1", { className: "text-2xl font-bold", children: config.title }), _jsx("p", { className: "text-1xl text-gray-600", children: config.summary })] })), showEventsTabbedLayout ? (_jsx(Tab, { tabs: tabs, defaultTab: defaultTabIndex, className: "justify-end" })) : (_jsx(OverviewContent, { visualData: visualData, renderContent: renderContent, renderVisual: renderVisual }))] }));
};
export default DashboardWrapper;