@pagamio/frontend-commons-lib 0.8.213 → 0.8.214
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/TanstackQueryProvider.d.ts +146 -0
- package/lib/api/TanstackQueryProvider.js +140 -0
- package/lib/api/index.d.ts +2 -0
- package/lib/api/index.js +2 -0
- package/lib/api/tanstackQuery.d.ts +249 -0
- package/lib/api/tanstackQuery.js +299 -0
- package/lib/components/layout/Sidebar.js +47 -24
- package/lib/components/ui/Sheet.d.ts +1 -1
- package/lib/context/SidebarContext.d.ts +10 -0
- package/lib/dashboard-visuals-v2/DashboardWrapperV2.d.ts +81 -0
- package/lib/dashboard-visuals-v2/DashboardWrapperV2.js +217 -0
- package/lib/dashboard-visuals-v2/cards/CardGrid.d.ts +22 -0
- package/lib/dashboard-visuals-v2/cards/CardGrid.js +27 -0
- package/lib/dashboard-visuals-v2/cards/InfoCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/InfoCard.js +26 -0
- package/lib/dashboard-visuals-v2/cards/MetricCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/MetricCard.js +37 -0
- package/lib/dashboard-visuals-v2/cards/StatCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/StatCard.js +92 -0
- package/lib/dashboard-visuals-v2/cards/TopItemsCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/TopItemsCard.js +34 -0
- package/lib/dashboard-visuals-v2/cards/TransactionListCard.d.ts +7 -0
- package/lib/dashboard-visuals-v2/cards/TransactionListCard.js +24 -0
- package/lib/dashboard-visuals-v2/cards/index.d.ts +9 -0
- package/lib/dashboard-visuals-v2/cards/index.js +9 -0
- package/lib/dashboard-visuals-v2/charts/AreaChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/AreaChart.js +124 -0
- package/lib/dashboard-visuals-v2/charts/BarChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/BarChart.js +106 -0
- package/lib/dashboard-visuals-v2/charts/BaseChart.d.ts +61 -0
- package/lib/dashboard-visuals-v2/charts/BaseChart.js +173 -0
- package/lib/dashboard-visuals-v2/charts/DonutChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/DonutChart.js +108 -0
- package/lib/dashboard-visuals-v2/charts/HeatmapChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/HeatmapChart.js +101 -0
- package/lib/dashboard-visuals-v2/charts/LineChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/LineChart.js +109 -0
- package/lib/dashboard-visuals-v2/charts/MixedChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/MixedChart.js +106 -0
- package/lib/dashboard-visuals-v2/charts/PieChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/PieChart.js +10 -0
- package/lib/dashboard-visuals-v2/charts/RadialChart.d.ts +7 -0
- package/lib/dashboard-visuals-v2/charts/RadialChart.js +72 -0
- package/lib/dashboard-visuals-v2/charts/index.d.ts +12 -0
- package/lib/dashboard-visuals-v2/charts/index.js +12 -0
- package/lib/dashboard-visuals-v2/components/DataFetchingVisual.d.ts +0 -0
- package/lib/dashboard-visuals-v2/components/DataFetchingVisual.js +1 -0
- package/lib/dashboard-visuals-v2/components/index.d.ts +0 -0
- package/lib/dashboard-visuals-v2/components/index.js +1 -0
- package/lib/dashboard-visuals-v2/hooks/index.d.ts +4 -0
- package/lib/dashboard-visuals-v2/hooks/index.js +4 -0
- package/lib/dashboard-visuals-v2/hooks/useChartData.d.ts +72 -0
- package/lib/dashboard-visuals-v2/hooks/useChartData.js +122 -0
- package/lib/dashboard-visuals-v2/index.d.ts +10 -0
- package/lib/dashboard-visuals-v2/index.js +16 -0
- package/lib/dashboard-visuals-v2/types/card.types.d.ts +237 -0
- package/lib/dashboard-visuals-v2/types/card.types.js +30 -0
- package/lib/dashboard-visuals-v2/types/chart.types.d.ts +308 -0
- package/lib/dashboard-visuals-v2/types/chart.types.js +25 -0
- package/lib/dashboard-visuals-v2/types/index.d.ts +6 -0
- package/lib/dashboard-visuals-v2/types/index.js +6 -0
- package/lib/dashboard-visuals-v2/utils/index.d.ts +0 -0
- package/lib/dashboard-visuals-v2/utils/index.js +1 -0
- package/lib/dashboard-visuals-v2/utils/propsTransformer.d.ts +0 -0
- package/lib/dashboard-visuals-v2/utils/propsTransformer.js +1 -0
- package/lib/dashboard-visuals-v2/visualRegistry.d.ts +59 -0
- package/lib/dashboard-visuals-v2/visualRegistry.js +110 -0
- package/lib/data-table-v2/DataTable.d.ts +7 -0
- package/lib/data-table-v2/DataTable.js +206 -0
- package/lib/data-table-v2/components/DataTableBody.d.ts +19 -0
- package/lib/data-table-v2/components/DataTableBody.js +20 -0
- package/lib/data-table-v2/components/DataTableEmpty.d.ts +17 -0
- package/lib/data-table-v2/components/DataTableEmpty.js +13 -0
- package/lib/data-table-v2/components/DataTableError.d.ts +16 -0
- package/lib/data-table-v2/components/DataTableError.js +14 -0
- package/lib/data-table-v2/components/DataTableHeader.d.ts +15 -0
- package/lib/data-table-v2/components/DataTableHeader.js +31 -0
- package/lib/data-table-v2/components/DataTableLoading.d.ts +14 -0
- package/lib/data-table-v2/components/DataTableLoading.js +19 -0
- package/lib/data-table-v2/components/DataTablePagination.d.ts +36 -0
- package/lib/data-table-v2/components/DataTablePagination.js +20 -0
- package/lib/data-table-v2/components/DataTableRowActions.d.ts +13 -0
- package/lib/data-table-v2/components/DataTableRowActions.js +57 -0
- package/lib/data-table-v2/components/DataTableSearch.d.ts +19 -0
- package/lib/data-table-v2/components/DataTableSearch.js +33 -0
- package/lib/data-table-v2/components/DataTableToolbar.d.ts +54 -0
- package/lib/data-table-v2/components/DataTableToolbar.js +28 -0
- package/lib/data-table-v2/components/index.d.ts +12 -0
- package/lib/data-table-v2/components/index.js +12 -0
- package/lib/data-table-v2/hooks/index.d.ts +4 -0
- package/lib/data-table-v2/hooks/index.js +4 -0
- package/lib/data-table-v2/hooks/useTableData.d.ts +118 -0
- package/lib/data-table-v2/hooks/useTableData.js +210 -0
- package/lib/data-table-v2/index.d.ts +9 -0
- package/lib/data-table-v2/index.js +12 -0
- package/lib/data-table-v2/types/index.d.ts +296 -0
- package/lib/data-table-v2/types/index.js +1 -0
- package/lib/data-table-v2/utils/export.d.ts +26 -0
- package/lib/data-table-v2/utils/export.js +92 -0
- package/lib/data-table-v2/utils/index.d.ts +4 -0
- package/lib/data-table-v2/utils/index.js +4 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +23 -0
- package/lib/styles.css +212 -0
- package/package.json +7 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview DashboardWrapperV2 - Main dashboard component using ApexCharts
|
|
4
|
+
* Drop-in replacement for V1 DashboardWrapper with improved performance
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import { useApi } from '../api';
|
|
8
|
+
import { FilterComponent, Tab } from '../components';
|
|
9
|
+
import { cn } from '../helpers';
|
|
10
|
+
import { useMediaQueries } from '../shared';
|
|
11
|
+
import visualRegistryV2, { isRegisteredVisual } from './visualRegistry';
|
|
12
|
+
const defaultDashboardApiPaths = {
|
|
13
|
+
query: '/analytics/query',
|
|
14
|
+
metrics: '/analytics/metrics',
|
|
15
|
+
};
|
|
16
|
+
// Helper to get grid span from column count
|
|
17
|
+
const getGridColSpan = (numVisuals, visualId) => {
|
|
18
|
+
if (numVisuals === 1)
|
|
19
|
+
return 12;
|
|
20
|
+
if (numVisuals === 2)
|
|
21
|
+
return 6;
|
|
22
|
+
if (numVisuals === 3)
|
|
23
|
+
return 4;
|
|
24
|
+
if (numVisuals === 4)
|
|
25
|
+
return 3;
|
|
26
|
+
// For > 4 visuals, use 4 columns layout
|
|
27
|
+
return 3;
|
|
28
|
+
};
|
|
29
|
+
// Helper to extract filter options from response
|
|
30
|
+
const getFilterOptionsData = (response, key, valueKey) => {
|
|
31
|
+
const optionsData = response.map((item) => {
|
|
32
|
+
const record = item;
|
|
33
|
+
const value = valueKey ? record[valueKey] : record[key];
|
|
34
|
+
const label = record.label || record.name || value;
|
|
35
|
+
return {
|
|
36
|
+
value: String(value ?? ''),
|
|
37
|
+
label: String(label ?? ''),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
return { optionsData };
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* DashboardWrapperV2 Component
|
|
44
|
+
* Drop-in replacement for DashboardWrapper using ApexCharts
|
|
45
|
+
*/
|
|
46
|
+
export function DashboardWrapperV2({ data, showVisualHeader = true, showEventsTabbedLayout = false, handleFilterChange: externalHandleFilterChange, selectedFilters = {}, tourSelectedFilters = {}, resetFilters = () => { }, handleApplyFilters = () => { }, handleApplyTourFilters = () => { }, handleTourFilterChange = () => { }, apiPaths = defaultDashboardApiPaths, className, classNames, gridGap = 4, fullWidth = false, }) {
|
|
47
|
+
const [loading, setLoading] = useState(true);
|
|
48
|
+
const [error, setError] = useState(null);
|
|
49
|
+
const [tourError, setTourError] = useState(null);
|
|
50
|
+
const [refreshCount, setRefreshCount] = useState(0);
|
|
51
|
+
const [filterOptions, setFilterOptions] = useState({});
|
|
52
|
+
const [tourFilterOptions, setTourFilterOptions] = useState({});
|
|
53
|
+
const { isSm, isXs } = useMediaQueries();
|
|
54
|
+
const apiClient = useApi();
|
|
55
|
+
const { config, visualData, eventsVisualData } = data;
|
|
56
|
+
// Handle filter change with default fallback
|
|
57
|
+
const handleFilterChange = externalHandleFilterChange ?? (() => { });
|
|
58
|
+
// Fetch filter options
|
|
59
|
+
const fetchFilterData = async (key, valueKey, url, query, isTourFilter = false) => {
|
|
60
|
+
try {
|
|
61
|
+
setLoading(true);
|
|
62
|
+
if (isTourFilter) {
|
|
63
|
+
setTourError(null);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
setError(null);
|
|
67
|
+
}
|
|
68
|
+
const response = await apiClient.post(url, query);
|
|
69
|
+
if (response) {
|
|
70
|
+
const { optionsData } = getFilterOptionsData(response, key, valueKey);
|
|
71
|
+
if (isTourFilter) {
|
|
72
|
+
setTourFilterOptions((prev) => ({ ...prev, [key]: optionsData }));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
setFilterOptions((prev) => ({ ...prev, [key]: optionsData }));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const errorMessage = 'Failed to fetch filter data';
|
|
81
|
+
if (isTourFilter) {
|
|
82
|
+
setTourError(err instanceof Error ? err : new Error(errorMessage));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
setError(err instanceof Error ? err : new Error(errorMessage));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
setLoading(false);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const fetchFilters = (filters, isTour = false) => {
|
|
94
|
+
filters.forEach((filter) => {
|
|
95
|
+
if (filter.query) {
|
|
96
|
+
const defaultQueryUrl = apiPaths.query;
|
|
97
|
+
const queryUrl = isTour ? (filter.tourUrl ?? defaultQueryUrl) : (filter.url ?? defaultQueryUrl);
|
|
98
|
+
fetchFilterData(filter.name, filter.valueKey, queryUrl, filter.query, isTour);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
fetchFilters(config.filters);
|
|
103
|
+
if (config.tourFilters) {
|
|
104
|
+
fetchFilters(config.tourFilters, true);
|
|
105
|
+
}
|
|
106
|
+
}, [config.filters, refreshCount, apiPaths]);
|
|
107
|
+
const handleRetry = () => setRefreshCount((prev) => prev + 1);
|
|
108
|
+
// Build dashboard filters with custom date range support
|
|
109
|
+
const dashboardFilters = useMemo(() => {
|
|
110
|
+
const baseFilters = [...config.filters];
|
|
111
|
+
const dateRangeValue = typeof selectedFilters?.dateRange === 'string' ? selectedFilters.dateRange.toUpperCase() : '';
|
|
112
|
+
if (dateRangeValue === 'CUSTOM_RANGE') {
|
|
113
|
+
baseFilters.push({
|
|
114
|
+
name: 'customDateRange',
|
|
115
|
+
label: 'Custom Date Range',
|
|
116
|
+
placeholder: 'Select date range',
|
|
117
|
+
type: 'date-range',
|
|
118
|
+
options: [],
|
|
119
|
+
multi: false,
|
|
120
|
+
rangeKeys: { start: 'startDate', end: 'endDate' },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return baseFilters;
|
|
124
|
+
}, [config.filters, selectedFilters?.dateRange]);
|
|
125
|
+
// Resolve filter type
|
|
126
|
+
const resolveFilterType = (filter) => {
|
|
127
|
+
if (filter.type === 'date-range')
|
|
128
|
+
return 'date-range';
|
|
129
|
+
if (filter.type === 'date')
|
|
130
|
+
return 'date';
|
|
131
|
+
if (filter.type === 'multi-select')
|
|
132
|
+
return 'multi-select';
|
|
133
|
+
if (filter.type === 'select')
|
|
134
|
+
return 'select';
|
|
135
|
+
return filter.multi ? 'multi-select' : 'select';
|
|
136
|
+
};
|
|
137
|
+
// Render filter component
|
|
138
|
+
const renderFilters = (filters, options, selected, onChange, onApply, filterError) => {
|
|
139
|
+
if (loading) {
|
|
140
|
+
return (_jsx("div", { className: "mb-5 p-4 bg-white rounded-lg border border-gray-200 animate-pulse", children: _jsx("div", { className: "flex gap-4", children: Array.from({ length: 3 })
|
|
141
|
+
.map((value, index) => {
|
|
142
|
+
void value;
|
|
143
|
+
return index;
|
|
144
|
+
})
|
|
145
|
+
.map((skeletonIndex) => (_jsx("div", { className: "h-10 bg-gray-200 rounded w-40" }, skeletonIndex))) }) }));
|
|
146
|
+
}
|
|
147
|
+
if (filterError) {
|
|
148
|
+
return (_jsxs("div", { className: "mb-5 p-4 bg-red-50 rounded-lg border border-red-200", children: [_jsx("p", { className: "text-red-600", children: filterError.message }), _jsx("button", { onClick: handleRetry, className: "mt-2 px-4 py-2 bg-red-100 text-red-700 rounded hover:bg-red-200", children: "Retry" })] }));
|
|
149
|
+
}
|
|
150
|
+
return (_jsx(FilterComponent, { filters: filters.map((filter) => ({
|
|
151
|
+
name: filter.name,
|
|
152
|
+
placeholder: filter.placeholder ?? `Select ${filter.name}`,
|
|
153
|
+
type: resolveFilterType(filter),
|
|
154
|
+
options: options[filter.name] || filter.options || [],
|
|
155
|
+
rangeKeys: filter.rangeKeys,
|
|
156
|
+
})), showClearFilters: true, selectedFilters: selected, handleFilterChange: onChange, handleApplyFilters: onApply, resetFilters: resetFilters, isNarrow: isSm }));
|
|
157
|
+
};
|
|
158
|
+
// Render a single visual
|
|
159
|
+
const renderVisual = useCallback((visual, sectionTitle, span) => {
|
|
160
|
+
const { metricData } = visual;
|
|
161
|
+
const matchesSmall = isXs || isSm;
|
|
162
|
+
const VisualComponent = visualRegistryV2[metricData.type];
|
|
163
|
+
if (!VisualComponent) {
|
|
164
|
+
if (!isRegisteredVisual(metricData.type)) {
|
|
165
|
+
console.warn(`[DashboardWrapperV2] Visual type "${metricData.type}" not found in V2 registry. ` +
|
|
166
|
+
`Available types: ${Object.keys(visualRegistryV2).join(', ')}`);
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
// Determine API URL based on component type
|
|
171
|
+
const isTileType = metricData.type === 'Tile' || metricData.type === 'StatCard' || metricData.type === 'MetricCard';
|
|
172
|
+
const defaultUrl = isTileType ? apiPaths.metrics : apiPaths.query;
|
|
173
|
+
const url = metricData.url || defaultUrl;
|
|
174
|
+
// Build props compatible with V2 components
|
|
175
|
+
const props = {
|
|
176
|
+
// Common props
|
|
177
|
+
data: metricData.data,
|
|
178
|
+
title: metricData.title,
|
|
179
|
+
colors: metricData.colors,
|
|
180
|
+
// Chart-specific props
|
|
181
|
+
series: metricData.series,
|
|
182
|
+
categories: metricData.categories,
|
|
183
|
+
stacked: metricData.stacked,
|
|
184
|
+
// Card-specific props
|
|
185
|
+
value: metricData.value,
|
|
186
|
+
icon: metricData.icon,
|
|
187
|
+
trend: metricData.trend,
|
|
188
|
+
trendValue: metricData.trendValue,
|
|
189
|
+
// Additional props from metricData
|
|
190
|
+
...metricData,
|
|
191
|
+
// Override with computed values
|
|
192
|
+
url,
|
|
193
|
+
themeColor: config.themeColor,
|
|
194
|
+
};
|
|
195
|
+
// Calculate grid span class
|
|
196
|
+
const colSpan = matchesSmall ? 12 : (visual.gridColSpan ?? span);
|
|
197
|
+
const colSpanClass = `col-span-${colSpan}`;
|
|
198
|
+
return (_jsx("div", { className: cn(colSpanClass, classNames?.visual), children: _jsx(VisualComponent, { ...props }) }, `visual-${sectionTitle}-${visual.id}`));
|
|
199
|
+
}, [config.themeColor, isXs, isSm, apiPaths, classNames?.visual]);
|
|
200
|
+
// Render visual sections
|
|
201
|
+
const renderSections = (sections) => (_jsx("main", { children: sections.map((section) => (_jsxs("div", { className: cn('mb-4', classNames?.section), children: [section.sectionTitle && (_jsx("h2", { className: cn('mb-3 text-lg font-semibold text-gray-900', classNames?.sectionTitle), children: section.sectionTitle })), _jsx("div", { className: cn('grid grid-cols-12', `gap-${gridGap}`, classNames?.grid), children: section.data.map((visual) => {
|
|
202
|
+
const numVisuals = section.data.length;
|
|
203
|
+
const span = getGridColSpan(numVisuals, visual.id);
|
|
204
|
+
return renderVisual(visual, section.sectionTitle, span);
|
|
205
|
+
}) })] }, section.id))) }));
|
|
206
|
+
// Overview tab content
|
|
207
|
+
const overviewContent = (_jsxs(_Fragment, { children: [renderFilters(dashboardFilters, filterOptions, selectedFilters, handleFilterChange, handleApplyFilters, error), renderSections(visualData)] }));
|
|
208
|
+
// Events/Tour tab content
|
|
209
|
+
const eventsContent = eventsVisualData ? (_jsxs(_Fragment, { children: [renderFilters(config.tourFilters || [], tourFilterOptions, tourSelectedFilters, handleTourFilterChange, handleApplyTourFilters, tourError), renderSections(eventsVisualData)] })) : null;
|
|
210
|
+
// Tab configuration
|
|
211
|
+
const tabs = [
|
|
212
|
+
{ id: 1, title: 'Overview', content: overviewContent },
|
|
213
|
+
{ id: 2, title: 'Tour', content: eventsContent },
|
|
214
|
+
];
|
|
215
|
+
return (_jsxs("div", { className: cn(fullWidth ? 'w-full' : 'max-w-7xl mx-auto', className), children: [showVisualHeader && (_jsxs("div", { className: cn('mb-6', classNames?.header), children: [_jsx("h1", { className: cn('text-2xl font-bold text-gray-900', classNames?.title), children: config.title }), _jsx("p", { className: cn('text-gray-600 mt-1', classNames?.summary), children: config.summary })] })), showEventsTabbedLayout ? _jsx(Tab, { tabs: tabs, defaultTab: 0, className: "justify-end" }) : overviewContent] }));
|
|
216
|
+
}
|
|
217
|
+
export default DashboardWrapperV2;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Card Grid component - Responsive card layout
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import type { CardGridProps } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Card Grid component
|
|
8
|
+
* Responsive grid layout for stat/metric cards
|
|
9
|
+
*/
|
|
10
|
+
export declare function CardGrid({ children, columns, gap, className, }: CardGridProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/**
|
|
12
|
+
* Pre-configured grid layouts
|
|
13
|
+
*/
|
|
14
|
+
export declare const StatCardGrid: ({ children, className }: {
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare const MetricCardGrid: ({ children, className }: {
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
className?: string;
|
|
21
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export default CardGrid;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Card Grid component
|
|
5
|
+
* Responsive grid layout for stat/metric cards
|
|
6
|
+
*/
|
|
7
|
+
export function CardGrid({ children, columns = { default: 1, sm: 2, md: 3, lg: 4 }, gap = 4, className, }) {
|
|
8
|
+
// Build column classes
|
|
9
|
+
const columnClasses = [
|
|
10
|
+
columns.default && `grid-cols-${columns.default}`,
|
|
11
|
+
columns.sm && `sm:grid-cols-${columns.sm}`,
|
|
12
|
+
columns.md && `md:grid-cols-${columns.md}`,
|
|
13
|
+
columns.lg && `lg:grid-cols-${columns.lg}`,
|
|
14
|
+
columns.xl && `xl:grid-cols-${columns.xl}`,
|
|
15
|
+
]
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.join(' ');
|
|
18
|
+
// Gap class
|
|
19
|
+
const gapClass = `gap-${gap}`;
|
|
20
|
+
return _jsx("div", { className: cn('grid', columnClasses, gapClass, className), children: children });
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Pre-configured grid layouts
|
|
24
|
+
*/
|
|
25
|
+
export const StatCardGrid = ({ children, className }) => (_jsx(CardGrid, { columns: { default: 1, sm: 2, lg: 4 }, gap: 4, className: className, children: children }));
|
|
26
|
+
export const MetricCardGrid = ({ children, className }) => (_jsx(CardGrid, { columns: { default: 1, md: 2, lg: 3 }, gap: 6, className: className, children: children }));
|
|
27
|
+
export default CardGrid;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { InfoCardProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Info Card component
|
|
4
|
+
* Flexible card for displaying various content types
|
|
5
|
+
*/
|
|
6
|
+
export declare function InfoCard({ title, value, description, icon, badge, badgeVariant, footer, children, loading, error, onClick, className, classNames, }: InfoCardProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default InfoCard;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Info Card component
|
|
5
|
+
* Flexible card for displaying various content types
|
|
6
|
+
*/
|
|
7
|
+
export function InfoCard({ title, value, description, icon, badge, badgeVariant = 'default', footer, children, loading = false, error, onClick, className, classNames, }) {
|
|
8
|
+
// Badge variant classes
|
|
9
|
+
const badgeClasses = {
|
|
10
|
+
default: 'bg-gray-100 text-gray-700',
|
|
11
|
+
primary: 'bg-blue-100 text-blue-700',
|
|
12
|
+
success: 'bg-green-100 text-green-700',
|
|
13
|
+
warning: 'bg-amber-100 text-amber-700',
|
|
14
|
+
danger: 'bg-red-100 text-red-700',
|
|
15
|
+
};
|
|
16
|
+
// Loading state
|
|
17
|
+
if (loading) {
|
|
18
|
+
return (_jsx("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm animate-pulse', className, classNames?.container), children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "w-10 h-10 rounded-lg bg-gray-200" }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3" }), _jsx("div", { className: "h-6 bg-gray-200 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-gray-200 rounded w-2/3" })] })] }) }));
|
|
19
|
+
}
|
|
20
|
+
// Error state
|
|
21
|
+
if (error) {
|
|
22
|
+
return (_jsx("div", { className: cn('rounded-xl border border-red-200 bg-red-50 p-4 shadow-sm', className, classNames?.container), children: _jsx("p", { className: "text-red-600 text-sm", children: "Error loading data" }) }));
|
|
23
|
+
}
|
|
24
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm transition-all duration-200', onClick && 'cursor-pointer hover:shadow-md hover:border-gray-300', className, classNames?.container), onClick: onClick, children: [_jsxs("div", { className: "flex items-start gap-3", children: [icon && (_jsx("div", { className: cn('w-10 h-10 rounded-lg bg-blue-100 text-blue-600 flex items-center justify-center flex-shrink-0', classNames?.icon), children: icon })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { children: [title && _jsx("p", { className: cn('text-sm font-medium text-gray-500', classNames?.title), children: title }), value && _jsx("p", { className: cn('text-xl font-bold text-gray-900 mt-0.5', classNames?.value), children: value })] }), badge && (_jsx("span", { className: cn('px-2 py-1 rounded-full text-xs font-medium', badgeClasses[badgeVariant]), children: badge }))] }), description && _jsx("p", { className: "text-sm text-gray-600 mt-2", children: description })] })] }), children && _jsx("div", { className: "mt-4", children: children }), footer && _jsx("div", { className: "mt-4 pt-3 border-t border-gray-100", children: footer })] }));
|
|
25
|
+
}
|
|
26
|
+
export default InfoCard;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MetricCardProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Metric Card component
|
|
4
|
+
* Displays metric with optional sparkline chart
|
|
5
|
+
*/
|
|
6
|
+
export declare function MetricCard({ title, value, icon, trend, trendValue, trendLabel, sparklineData, sparklineColor, subtitle, action, loading, error, className, classNames, }: MetricCardProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default MetricCard;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../helpers';
|
|
3
|
+
import { AreaChart } from '../charts/AreaChart';
|
|
4
|
+
/**
|
|
5
|
+
* Metric Card component
|
|
6
|
+
* Displays metric with optional sparkline chart
|
|
7
|
+
*/
|
|
8
|
+
export function MetricCard({ title, value, icon, trend, trendValue, trendLabel, sparklineData, sparklineColor = '#3B82F6', subtitle, action, loading = false, error, className, classNames, }) {
|
|
9
|
+
// Loading state
|
|
10
|
+
if (loading) {
|
|
11
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm animate-pulse', className, classNames?.container), children: [_jsxs("div", { className: "flex items-start justify-between mb-4", children: [_jsxs("div", { className: "space-y-2 flex-1", children: [_jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-gray-200 rounded w-1/2" })] }), _jsx("div", { className: "w-10 h-10 rounded-lg bg-gray-200" })] }), _jsx("div", { className: "h-16 bg-gray-200 rounded" })] }));
|
|
12
|
+
}
|
|
13
|
+
// Error state
|
|
14
|
+
if (error) {
|
|
15
|
+
return (_jsx("div", { className: cn('rounded-xl border border-red-200 bg-red-50 p-4 shadow-sm', className, classNames?.container), children: _jsx("p", { className: "text-red-600 text-sm", children: "Error loading metric" }) }));
|
|
16
|
+
}
|
|
17
|
+
// Trend classes
|
|
18
|
+
const trendClasses = {
|
|
19
|
+
up: 'text-green-600 bg-green-50',
|
|
20
|
+
down: 'text-red-600 bg-red-50',
|
|
21
|
+
neutral: 'text-gray-600 bg-gray-50',
|
|
22
|
+
};
|
|
23
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm', className, classNames?.container), children: [_jsxs("div", { className: "flex items-start justify-between mb-3", children: [_jsxs("div", { children: [_jsx("p", { className: cn('text-sm font-medium text-gray-500', classNames?.title), children: title }), _jsxs("div", { className: "flex items-baseline gap-2 mt-1", children: [_jsx("p", { className: cn('text-2xl font-bold text-gray-900', classNames?.value), children: value }), trend && trendValue && (_jsxs("span", { className: cn('px-2 py-0.5 rounded-full text-xs font-medium', trendClasses[trend]), children: [trend === 'up' ? '↑' : trend === 'down' ? '↓' : '', trendValue] }))] }), (trendLabel || subtitle) && _jsx("p", { className: "text-xs text-gray-500 mt-1", children: trendLabel || subtitle })] }), icon && (_jsx("div", { className: cn('w-10 h-10 rounded-lg bg-blue-100 text-blue-600 flex items-center justify-center', classNames?.icon), children: icon }))] }), sparklineData && sparklineData.length > 0 && (_jsx("div", { className: "h-16 -mx-2 -mb-2", children: _jsx(AreaChart, { data: sparklineData.map((v, i) => ({ label: i.toString(), value: v })), style: { height: 64 }, apexOptions: {
|
|
24
|
+
chart: {
|
|
25
|
+
sparkline: { enabled: true },
|
|
26
|
+
toolbar: { show: false },
|
|
27
|
+
},
|
|
28
|
+
colors: [sparklineColor],
|
|
29
|
+
xaxis: { labels: { show: false } },
|
|
30
|
+
yaxis: { labels: { show: false } },
|
|
31
|
+
tooltip: { enabled: false },
|
|
32
|
+
stroke: { width: 2 },
|
|
33
|
+
legend: { show: false },
|
|
34
|
+
grid: { show: false },
|
|
35
|
+
} }) })), action && _jsx("div", { className: "mt-3 pt-3 border-t border-gray-100", children: action })] }));
|
|
36
|
+
}
|
|
37
|
+
export default MetricCard;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StatCardProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Stat Card component
|
|
4
|
+
* Displays a single metric with optional trend indicator
|
|
5
|
+
*/
|
|
6
|
+
export declare function StatCard({ title, value, icon, trend, trendValue, trendLabel, description, variant, size, loading, error, onClick, className, classNames, }: StatCardProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default StatCard;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Stat Card component - Single metric display
|
|
4
|
+
*/
|
|
5
|
+
import { TrendingDown, TrendingUp } from 'lucide-react';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { cn } from '../../helpers';
|
|
8
|
+
/**
|
|
9
|
+
* Stat Card component
|
|
10
|
+
* Displays a single metric with optional trend indicator
|
|
11
|
+
*/
|
|
12
|
+
export function StatCard({ title, value, icon, trend, trendValue, trendLabel, description, variant = 'default', size = 'md', loading = false, error, onClick, className, classNames, }) {
|
|
13
|
+
// Size classes
|
|
14
|
+
const sizeClasses = {
|
|
15
|
+
sm: {
|
|
16
|
+
container: 'p-3',
|
|
17
|
+
title: 'text-xs',
|
|
18
|
+
value: 'text-xl',
|
|
19
|
+
icon: 'w-8 h-8',
|
|
20
|
+
iconInner: 'w-4 h-4',
|
|
21
|
+
},
|
|
22
|
+
md: {
|
|
23
|
+
container: 'p-4',
|
|
24
|
+
title: 'text-sm',
|
|
25
|
+
value: 'text-2xl',
|
|
26
|
+
icon: 'w-10 h-10',
|
|
27
|
+
iconInner: 'w-5 h-5',
|
|
28
|
+
},
|
|
29
|
+
lg: {
|
|
30
|
+
container: 'p-6',
|
|
31
|
+
title: 'text-base',
|
|
32
|
+
value: 'text-3xl',
|
|
33
|
+
icon: 'w-12 h-12',
|
|
34
|
+
iconInner: 'w-6 h-6',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
// Variant classes
|
|
38
|
+
const variantClasses = {
|
|
39
|
+
default: 'bg-white border border-gray-200',
|
|
40
|
+
primary: 'bg-blue-50 border border-blue-200',
|
|
41
|
+
success: 'bg-green-50 border border-green-200',
|
|
42
|
+
warning: 'bg-amber-50 border border-amber-200',
|
|
43
|
+
danger: 'bg-red-50 border border-red-200',
|
|
44
|
+
gradient: 'bg-gradient-to-br from-blue-500 to-purple-600 text-white',
|
|
45
|
+
// Solid colored variants for dashboard cards
|
|
46
|
+
'primary-solid': 'bg-blue-500 text-white border-0',
|
|
47
|
+
'success-solid': 'bg-emerald-500 text-white border-0',
|
|
48
|
+
'warning-solid': 'bg-amber-500 text-white border-0',
|
|
49
|
+
'danger-solid': 'bg-red-500 text-white border-0',
|
|
50
|
+
'purple-solid': 'bg-indigo-500 text-white border-0',
|
|
51
|
+
'orange-solid': 'bg-orange-500 text-white border-0',
|
|
52
|
+
};
|
|
53
|
+
// Icon background classes
|
|
54
|
+
const iconBgClasses = {
|
|
55
|
+
default: 'bg-gray-100 text-gray-600',
|
|
56
|
+
primary: 'bg-blue-100 text-blue-600',
|
|
57
|
+
success: 'bg-green-100 text-green-600',
|
|
58
|
+
warning: 'bg-amber-100 text-amber-600',
|
|
59
|
+
danger: 'bg-red-100 text-red-600',
|
|
60
|
+
gradient: 'bg-white/20 text-white',
|
|
61
|
+
// Solid variant icon backgrounds
|
|
62
|
+
'primary-solid': 'bg-white/20 text-white',
|
|
63
|
+
'success-solid': 'bg-white/20 text-white',
|
|
64
|
+
'warning-solid': 'bg-white/20 text-white',
|
|
65
|
+
'danger-solid': 'bg-white/20 text-white',
|
|
66
|
+
'purple-solid': 'bg-white/20 text-white',
|
|
67
|
+
'orange-solid': 'bg-white/20 text-white',
|
|
68
|
+
};
|
|
69
|
+
// Trend classes
|
|
70
|
+
const trendClasses = {
|
|
71
|
+
up: 'text-green-600',
|
|
72
|
+
down: 'text-red-600',
|
|
73
|
+
neutral: 'text-gray-500',
|
|
74
|
+
};
|
|
75
|
+
const sizes = sizeClasses[size];
|
|
76
|
+
// Loading state
|
|
77
|
+
if (loading) {
|
|
78
|
+
return (_jsx("div", { className: cn('rounded-xl shadow-sm animate-pulse', sizes.container, variantClasses[variant], className, classNames?.container), children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "space-y-3 flex-1", children: [_jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3" }), _jsx("div", { className: "h-8 bg-gray-200 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-gray-200 rounded w-2/3" })] }), _jsx("div", { className: cn('rounded-lg bg-gray-200', sizes.icon) })] }) }));
|
|
79
|
+
}
|
|
80
|
+
// Error state
|
|
81
|
+
if (error) {
|
|
82
|
+
return (_jsx("div", { className: cn('rounded-xl shadow-sm border border-red-200 bg-red-50', sizes.container, className, classNames?.container), children: _jsx("div", { className: "flex items-center gap-2 text-red-600", children: _jsx("span", { className: "text-sm", children: "Error loading data" }) }) }));
|
|
83
|
+
}
|
|
84
|
+
// Helper to check if variant is a solid color
|
|
85
|
+
const isSolidVariant = variant.endsWith('-solid') || variant === 'gradient';
|
|
86
|
+
return (_jsx("div", { className: cn('rounded-xl shadow-sm transition-all duration-200', onClick && 'cursor-pointer hover:shadow-md', sizes.container, variantClasses[variant] || variantClasses.default, className, classNames?.container), onClick: onClick, children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: cn('font-medium', sizes.title, isSolidVariant ? 'text-white/80' : 'text-gray-500', classNames?.title), children: title }), _jsx("p", { className: cn('font-bold tracking-tight', sizes.value, isSolidVariant ? 'text-white' : 'text-gray-900', classNames?.value), children: value }), _jsxs("div", { className: "flex items-center gap-2 mt-2", children: [trend && trendValue && (_jsxs("span", { className: cn('inline-flex items-center gap-1 text-xs font-medium', isSolidVariant ? (trend === 'up' ? 'text-white' : 'text-white/90') : trendClasses[trend], classNames?.trend), children: [trend === 'up' && _jsx(TrendingUp, { className: "w-3 h-3" }), trend === 'down' && _jsx(TrendingDown, { className: "w-3 h-3" }), trendValue] })), (trendLabel || description) && (_jsx("span", { className: cn('text-xs', isSolidVariant ? 'text-white/60' : 'text-gray-500'), children: trendLabel || description }))] })] }), icon && (_jsx("div", { className: cn('rounded-lg flex items-center justify-center flex-shrink-0', sizes.icon, iconBgClasses[variant] || iconBgClasses.default, classNames?.icon), children: React.isValidElement(icon)
|
|
87
|
+
? React.cloneElement(icon, {
|
|
88
|
+
className: cn(sizes.iconInner, icon.props?.className),
|
|
89
|
+
})
|
|
90
|
+
: icon }))] }) }));
|
|
91
|
+
}
|
|
92
|
+
export default StatCard;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TopItemsCardProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Top Items Card component
|
|
4
|
+
* Displays a ranked list with progress bars
|
|
5
|
+
*/
|
|
6
|
+
export declare function TopItemsCard<TItem extends Record<string, unknown>>({ title, items, labelKey, valueKey, renderLabel, renderValue, showRank, showProgress, progressColor, maxItems, loading, error, className, classNames, }: TopItemsCardProps<TItem>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default TopItemsCard;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Top Items Card component
|
|
5
|
+
* Displays a ranked list with progress bars
|
|
6
|
+
*/
|
|
7
|
+
export function TopItemsCard({ title, items, labelKey, valueKey, renderLabel, renderValue, showRank = true, showProgress = true, progressColor = '#3B82F6', maxItems = 5, loading = false, error, className, classNames, }) {
|
|
8
|
+
// Loading state
|
|
9
|
+
if (loading) {
|
|
10
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm', className, classNames?.container), children: [_jsx("div", { className: "h-5 bg-gray-200 rounded w-1/3 mb-4 animate-pulse" }), _jsx("div", { className: "space-y-4", children: Array.from({ length: 3 })
|
|
11
|
+
.map((value, index) => {
|
|
12
|
+
void value;
|
|
13
|
+
return index;
|
|
14
|
+
})
|
|
15
|
+
.map((skeletonIndex) => (_jsxs("div", { className: "animate-pulse", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("div", { className: "h-4 bg-gray-200 rounded w-1/3" }), _jsx("div", { className: "h-4 bg-gray-200 rounded w-16" })] }), _jsx("div", { className: "h-2 bg-gray-200 rounded-full" })] }, skeletonIndex))) })] }));
|
|
16
|
+
}
|
|
17
|
+
// Error state
|
|
18
|
+
if (error) {
|
|
19
|
+
return (_jsx("div", { className: cn('rounded-xl border border-red-200 bg-red-50 p-4 shadow-sm', className, classNames?.container), children: _jsx("p", { className: "text-red-600 text-sm", children: "Error loading data" }) }));
|
|
20
|
+
}
|
|
21
|
+
const displayItems = items.slice(0, maxItems);
|
|
22
|
+
const maxValue = Math.max(...displayItems.map((item) => Number(item[valueKey]) || 0));
|
|
23
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm', className, classNames?.container), children: [_jsx("h3", { className: cn('font-semibold text-gray-900 mb-4', classNames?.title), children: title }), _jsx("div", { className: "space-y-4", children: displayItems.length === 0 ? (_jsx("div", { className: "text-center text-gray-500 py-4", children: "No data available" })) : (displayItems.map((item, index) => {
|
|
24
|
+
const value = Number(item[valueKey]) || 0;
|
|
25
|
+
const progress = maxValue > 0 ? (value / maxValue) * 100 : 0;
|
|
26
|
+
const label = renderLabel ? renderLabel(item, index) : String(item[labelKey] ?? '');
|
|
27
|
+
const displayValue = renderValue ? renderValue(item, index) : value.toLocaleString();
|
|
28
|
+
return (_jsxs("div", { className: classNames?.item, children: [_jsxs("div", { className: "flex items-center justify-between mb-1.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [showRank && _jsxs("span", { className: "text-xs font-medium text-gray-400 w-5", children: ["#", index + 1] }), _jsx("span", { className: "text-sm font-medium text-gray-700 truncate", children: label })] }), _jsx("span", { className: "text-sm font-semibold text-gray-900", children: displayValue })] }), showProgress && (_jsx("div", { className: "h-2 bg-gray-100 rounded-full overflow-hidden", children: _jsx("div", { className: "h-full rounded-full transition-all duration-500", style: {
|
|
29
|
+
width: `${progress}%`,
|
|
30
|
+
backgroundColor: progressColor,
|
|
31
|
+
} }) }))] }, index));
|
|
32
|
+
})) })] }));
|
|
33
|
+
}
|
|
34
|
+
export default TopItemsCard;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TransactionListCardProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Transaction List Card component
|
|
4
|
+
* Displays a list of transactions or activity items
|
|
5
|
+
*/
|
|
6
|
+
export declare function TransactionListCard<TItem extends Record<string, unknown>>({ title, items, renderItem, keyExtractor, maxItems, emptyMessage, viewAllLabel, onViewAll, loading, error, className, classNames, }: TransactionListCardProps<TItem>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default TransactionListCard;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../helpers';
|
|
3
|
+
/**
|
|
4
|
+
* Transaction List Card component
|
|
5
|
+
* Displays a list of transactions or activity items
|
|
6
|
+
*/
|
|
7
|
+
export function TransactionListCard({ title, items, renderItem, keyExtractor, maxItems = 5, emptyMessage = 'No items to display', viewAllLabel = 'View All', onViewAll, loading = false, error, className, classNames, }) {
|
|
8
|
+
// Loading state
|
|
9
|
+
if (loading) {
|
|
10
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 p-4 shadow-sm', className, classNames?.container), children: [_jsx("div", { className: "h-5 bg-gray-200 rounded w-1/3 mb-4 animate-pulse" }), _jsx("div", { className: "space-y-3", children: Array.from({ length: 3 })
|
|
11
|
+
.map((value, index) => {
|
|
12
|
+
void value;
|
|
13
|
+
return index;
|
|
14
|
+
})
|
|
15
|
+
.map((skeletonIndex) => (_jsxs("div", { className: "flex items-center gap-3 animate-pulse", children: [_jsx("div", { className: "w-10 h-10 rounded-full bg-gray-200" }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("div", { className: "h-4 bg-gray-200 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-gray-200 rounded w-1/3" })] }), _jsx("div", { className: "h-4 bg-gray-200 rounded w-16" })] }, skeletonIndex))) })] }));
|
|
16
|
+
}
|
|
17
|
+
// Error state
|
|
18
|
+
if (error) {
|
|
19
|
+
return (_jsx("div", { className: cn('rounded-xl border border-red-200 bg-red-50 p-4 shadow-sm', className, classNames?.container), children: _jsx("p", { className: "text-red-600 text-sm", children: "Error loading transactions" }) }));
|
|
20
|
+
}
|
|
21
|
+
const displayItems = items.slice(0, maxItems);
|
|
22
|
+
return (_jsxs("div", { className: cn('rounded-xl bg-white border border-gray-200 shadow-sm', className, classNames?.container), children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-100", children: [_jsx("h3", { className: cn('font-semibold text-gray-900', classNames?.title), children: title }), onViewAll && items.length > maxItems && (_jsx("button", { onClick: onViewAll, className: "text-sm text-blue-600 hover:text-blue-700 font-medium", children: viewAllLabel }))] }), _jsx("div", { className: cn('divide-y divide-gray-100', classNames?.list), children: displayItems.length === 0 ? (_jsx("div", { className: "px-4 py-8 text-center text-gray-500", children: emptyMessage })) : (displayItems.map((item, index) => (_jsx("div", { className: cn('px-4 py-3', classNames?.item), children: renderItem(item, index) }, keyExtractor ? keyExtractor(item, index) : index)))) })] }));
|
|
23
|
+
}
|
|
24
|
+
export default TransactionListCard;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Cards index - Export all card components
|
|
3
|
+
*/
|
|
4
|
+
export { StatCard } from './StatCard';
|
|
5
|
+
export { MetricCard } from './MetricCard';
|
|
6
|
+
export { InfoCard } from './InfoCard';
|
|
7
|
+
export { CardGrid, StatCardGrid, MetricCardGrid } from './CardGrid';
|
|
8
|
+
export { TransactionListCard } from './TransactionListCard';
|
|
9
|
+
export { TopItemsCard } from './TopItemsCard';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Cards index - Export all card components
|
|
3
|
+
*/
|
|
4
|
+
export { StatCard } from './StatCard';
|
|
5
|
+
export { MetricCard } from './MetricCard';
|
|
6
|
+
export { InfoCard } from './InfoCard';
|
|
7
|
+
export { CardGrid, StatCardGrid, MetricCardGrid } from './CardGrid';
|
|
8
|
+
export { TransactionListCard } from './TransactionListCard';
|
|
9
|
+
export { TopItemsCard } from './TopItemsCard';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AreaChartProps, ChartDataPoint } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Area Chart component
|
|
4
|
+
* Line chart with filled area underneath
|
|
5
|
+
*/
|
|
6
|
+
export declare function AreaChart<TData extends ChartDataPoint = ChartDataPoint>({ data, series: providedSeries, categories: providedCategories, xKey, yKey, yKeys, seriesNames, curve, strokeWidth, showMarkers, markerSize, fillOpacity, gradient, style, tooltip, xAxis, yAxis, apexOptions, onDataPointClick, ...baseProps }: AreaChartProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default AreaChart;
|