@pattern-stack/frontend-patterns 0.0.4 → 0.0.6
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/dist/atoms/composed/SalesPanel/SalesPanel.d.ts +19 -0
- package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts.map +1 -0
- package/dist/atoms/composed/SalesPanel/index.d.ts +2 -0
- package/dist/atoms/composed/SalesPanel/index.d.ts.map +1 -0
- package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts +63 -0
- package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts.map +1 -0
- package/dist/atoms/composed/index.d.ts +1 -0
- package/dist/atoms/composed/index.d.ts.map +1 -1
- package/dist/atoms/types/entity-config.d.ts +117 -0
- package/dist/atoms/types/entity-config.d.ts.map +1 -0
- package/dist/atoms/types/index.d.ts +2 -0
- package/dist/atoms/types/index.d.ts.map +1 -1
- package/dist/atoms/types/navigation.d.ts +30 -0
- package/dist/atoms/types/navigation.d.ts.map +1 -0
- package/dist/atoms/ui/ErrorBoundary.d.ts +1 -1
- package/dist/atoms/ui/button.d.ts +1 -1
- package/dist/atoms/utils/icon-resolver.d.ts +72 -0
- package/dist/atoms/utils/icon-resolver.d.ts.map +1 -0
- package/dist/atoms/utils/metric-engine.d.ts +30 -0
- package/dist/atoms/utils/metric-engine.d.ts.map +1 -0
- package/dist/atoms/utils/utils.d.ts +2 -0
- package/dist/atoms/utils/utils.d.ts.map +1 -1
- package/dist/features/auth/components/ProtectedRoute.d.ts +1 -1
- package/dist/frontend-patterns.css +1 -1
- package/dist/index.es.js +402 -14
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +402 -14
- package/dist/index.js.map +1 -1
- package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts +16 -0
- package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts.map +1 -0
- package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts +2 -0
- package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts.map +1 -0
- package/dist/molecules/layout/NavigationContext.d.ts +15 -0
- package/dist/molecules/layout/NavigationContext.d.ts.map +1 -0
- package/dist/molecules/layout/Sidebar.d.ts.map +1 -1
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +2 -0
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -1
- package/dist/molecules/layout/index.d.ts +3 -0
- package/dist/molecules/layout/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts +2 -1
- package/dist/templates/factory.d.ts.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/package.json +7 -3
- package/src/App.tsx +11 -1
- package/src/__tests__/atoms/composed/databadge.test.tsx +106 -0
- package/src/__tests__/atoms/composed/statcard.test.tsx +133 -0
- package/src/__tests__/atoms/utils/icon-resolver.test.tsx +140 -0
- package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
- package/src/atoms/composed/SalesPanel/index.ts +1 -0
- package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
- package/src/atoms/composed/index.ts +1 -0
- package/src/atoms/types/entity-config.ts +127 -0
- package/src/atoms/types/index.ts +3 -1
- package/src/atoms/types/navigation.ts +43 -0
- package/src/atoms/utils/icon-resolver.tsx +54 -0
- package/src/atoms/utils/metric-engine.ts +236 -0
- package/src/atoms/utils/utils.ts +4 -2
- package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
- package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
- package/src/molecules/layout/NavigationContext.tsx +63 -0
- package/src/molecules/layout/Sidebar.tsx +10 -23
- package/src/molecules/layout/SidebarButton/SidebarButton.tsx +32 -10
- package/src/molecules/layout/index.ts +4 -1
- package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
- package/src/organisms/entity/EntityListPanel.tsx +339 -0
- package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
- package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
- package/src/organisms/entity/index.ts +4 -0
- package/src/organisms/index.ts +5 -1
- package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +77 -75
- package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
- package/src/pages/AdminShowcase/index.tsx +2 -1
- package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
- package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
- package/src/pages/EntityShowcase/index.ts +2 -0
- package/src/pages/EntityTemplateExample.tsx +229 -0
- package/src/pages/TestEntityTemplate.tsx +40 -0
- package/src/pages/index.ts +2 -1
- package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
- package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
- package/src/templates/entity/configs/financial-config.ts +141 -0
- package/src/templates/entity/configs/index.ts +1 -0
- package/src/templates/entity/index.ts +3 -0
- package/src/templates/factory.tsx +14 -7
- package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
- package/src/templates/index.ts +4 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AppLayout } from '../../molecules/layout/AppLayout';
|
|
3
|
+
import { PageTemplate } from '../../molecules/layout/PageTemplate';
|
|
4
|
+
import { SectionHeader } from '../../molecules/layout/SectionHeader';
|
|
5
|
+
import {
|
|
6
|
+
MetricsOverviewPanel,
|
|
7
|
+
MetricsOverviewWithInsightsPanel,
|
|
8
|
+
TrendAnalysisPanel,
|
|
9
|
+
CategoryBreakdownPanel
|
|
10
|
+
} from '../../organisms/entity';
|
|
11
|
+
import type { EntityTemplateConfig, EntityData, MetricValue, ActionConfig, MetricConfig } from '../../atoms/types';
|
|
12
|
+
import { cn } from '../../atoms/utils/utils';
|
|
13
|
+
|
|
14
|
+
export interface EntityPerformanceDashboardTemplateProps<T extends EntityData> {
|
|
15
|
+
config: EntityTemplateConfig<T>;
|
|
16
|
+
data: T[];
|
|
17
|
+
previousData?: T[];
|
|
18
|
+
isLoading?: boolean;
|
|
19
|
+
className?: string;
|
|
20
|
+
|
|
21
|
+
// Layout configuration
|
|
22
|
+
layout?: 'standard' | 'compact' | 'detailed';
|
|
23
|
+
showInsights?: boolean;
|
|
24
|
+
showTrends?: boolean;
|
|
25
|
+
showCategories?: boolean;
|
|
26
|
+
|
|
27
|
+
// Extension points - Render Props
|
|
28
|
+
renderCustomActions?: () => React.ReactNode;
|
|
29
|
+
renderAdditionalMetrics?: () => React.ReactNode;
|
|
30
|
+
renderCustomTrendChart?: (config: EntityTemplateConfig<T>, data: T[]) => React.ReactNode;
|
|
31
|
+
renderCustomCategoryView?: (config: EntityTemplateConfig<T>, data: T[]) => React.ReactNode;
|
|
32
|
+
|
|
33
|
+
// Extension points - Slots
|
|
34
|
+
headerSlot?: React.ReactNode;
|
|
35
|
+
footerSlot?: React.ReactNode;
|
|
36
|
+
metricsHeaderSlot?: React.ReactNode;
|
|
37
|
+
metricsFooterSlot?: React.ReactNode;
|
|
38
|
+
trendsHeaderSlot?: React.ReactNode;
|
|
39
|
+
trendsFooterSlot?: React.ReactNode;
|
|
40
|
+
categoriesHeaderSlot?: React.ReactNode;
|
|
41
|
+
categoriesFooterSlot?: React.ReactNode;
|
|
42
|
+
|
|
43
|
+
// Event handlers
|
|
44
|
+
onMetricClick?: (metric: MetricConfig, value: MetricValue) => void;
|
|
45
|
+
onCategoryClick?: (category: unknown) => void;
|
|
46
|
+
onTrendPeriodChange?: (period: string) => void;
|
|
47
|
+
onActionClick?: (action: ActionConfig, context: T[]) => void;
|
|
48
|
+
onRefresh?: () => void;
|
|
49
|
+
onExport?: (type: 'metrics' | 'trends' | 'categories', data: unknown) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const EntityPerformanceDashboardTemplate = <T extends EntityData>({
|
|
53
|
+
config,
|
|
54
|
+
data,
|
|
55
|
+
previousData,
|
|
56
|
+
isLoading = false,
|
|
57
|
+
className,
|
|
58
|
+
layout = 'standard',
|
|
59
|
+
showInsights = true,
|
|
60
|
+
showTrends = true,
|
|
61
|
+
showCategories = true,
|
|
62
|
+
renderCustomActions,
|
|
63
|
+
renderAdditionalMetrics,
|
|
64
|
+
renderCustomTrendChart,
|
|
65
|
+
renderCustomCategoryView,
|
|
66
|
+
headerSlot,
|
|
67
|
+
footerSlot,
|
|
68
|
+
metricsHeaderSlot,
|
|
69
|
+
metricsFooterSlot,
|
|
70
|
+
trendsHeaderSlot,
|
|
71
|
+
trendsFooterSlot,
|
|
72
|
+
categoriesHeaderSlot,
|
|
73
|
+
categoriesFooterSlot,
|
|
74
|
+
onMetricClick,
|
|
75
|
+
onCategoryClick,
|
|
76
|
+
onTrendPeriodChange,
|
|
77
|
+
onExport
|
|
78
|
+
}: EntityPerformanceDashboardTemplateProps<T>) => {
|
|
79
|
+
|
|
80
|
+
const getLayoutClasses = () => {
|
|
81
|
+
switch (layout) {
|
|
82
|
+
case 'compact':
|
|
83
|
+
return 'space-y-4';
|
|
84
|
+
case 'detailed':
|
|
85
|
+
return 'space-y-8';
|
|
86
|
+
case 'standard':
|
|
87
|
+
default:
|
|
88
|
+
return 'space-y-6';
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const renderPageHeader = () => {
|
|
93
|
+
return (
|
|
94
|
+
<SectionHeader
|
|
95
|
+
title={config.display.title}
|
|
96
|
+
description={config.display.description}
|
|
97
|
+
category={config.display.category}
|
|
98
|
+
actions={renderCustomActions ? renderCustomActions() : undefined}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const renderMetricsSection = () => {
|
|
104
|
+
if (!config.metrics.length) return null;
|
|
105
|
+
|
|
106
|
+
const MetricsComponent = showInsights ? MetricsOverviewWithInsightsPanel : MetricsOverviewPanel;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<section className="space-y-4">
|
|
110
|
+
{metricsHeaderSlot}
|
|
111
|
+
<MetricsComponent
|
|
112
|
+
metrics={config.metrics}
|
|
113
|
+
data={data}
|
|
114
|
+
previousData={previousData}
|
|
115
|
+
isLoading={isLoading}
|
|
116
|
+
onMetricClick={onMetricClick}
|
|
117
|
+
category={config.display.category}
|
|
118
|
+
columns={layout === 'compact' ? 2 : layout === 'detailed' ? 4 : 3}
|
|
119
|
+
headerSlot={renderAdditionalMetrics ? renderAdditionalMetrics() : undefined}
|
|
120
|
+
/>
|
|
121
|
+
{metricsFooterSlot}
|
|
122
|
+
</section>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const renderTrendsSection = () => {
|
|
127
|
+
if (!showTrends || !config.temporal || !config.metrics.length) return null;
|
|
128
|
+
|
|
129
|
+
if (renderCustomTrendChart) {
|
|
130
|
+
const customChart = renderCustomTrendChart(config, data);
|
|
131
|
+
if (customChart) {
|
|
132
|
+
return (
|
|
133
|
+
<section className="space-y-4">
|
|
134
|
+
{trendsHeaderSlot}
|
|
135
|
+
{customChart}
|
|
136
|
+
{trendsFooterSlot}
|
|
137
|
+
</section>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<section className="space-y-4">
|
|
144
|
+
{trendsHeaderSlot}
|
|
145
|
+
<TrendAnalysisPanel
|
|
146
|
+
metrics={config.metrics}
|
|
147
|
+
data={data}
|
|
148
|
+
temporal={config.temporal}
|
|
149
|
+
isLoading={isLoading}
|
|
150
|
+
category={config.display.category}
|
|
151
|
+
enablePeriodSelection={true}
|
|
152
|
+
enableChartTypeSelection={layout === 'detailed'}
|
|
153
|
+
showComparisons={layout !== 'compact'}
|
|
154
|
+
onPeriodChange={onTrendPeriodChange}
|
|
155
|
+
onExport={(chartData, metric) => onExport?.('trends', { chartData, metric })}
|
|
156
|
+
/>
|
|
157
|
+
{trendsFooterSlot}
|
|
158
|
+
</section>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const renderCategoriesSection = () => {
|
|
163
|
+
if (!showCategories || !config.categories || !data.length) return null;
|
|
164
|
+
|
|
165
|
+
// Find a suitable value field from metrics or use first numeric field
|
|
166
|
+
const valueField = config.metrics.find(m => m.type === 'currency' || m.type === 'count')?.key ||
|
|
167
|
+
Object.keys(data[0]).find(key => typeof data[0][key] === 'number') ||
|
|
168
|
+
'value';
|
|
169
|
+
|
|
170
|
+
if (renderCustomCategoryView) {
|
|
171
|
+
const customView = renderCustomCategoryView(config, data);
|
|
172
|
+
if (customView) {
|
|
173
|
+
return (
|
|
174
|
+
<section className="space-y-4">
|
|
175
|
+
{categoriesHeaderSlot}
|
|
176
|
+
{customView}
|
|
177
|
+
{categoriesFooterSlot}
|
|
178
|
+
</section>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<section className="space-y-4">
|
|
185
|
+
{categoriesHeaderSlot}
|
|
186
|
+
<CategoryBreakdownPanel
|
|
187
|
+
data={data}
|
|
188
|
+
categoryConfig={config.categories}
|
|
189
|
+
valueField={valueField}
|
|
190
|
+
isLoading={isLoading}
|
|
191
|
+
category={config.display.category}
|
|
192
|
+
title={`${config.display.title} by ${config.categories.defaultGroupBy}`}
|
|
193
|
+
defaultView={layout === 'compact' ? 'list' : 'both'}
|
|
194
|
+
enableDrillDown={config.categories.enableDrillDown}
|
|
195
|
+
onCategoryClick={onCategoryClick}
|
|
196
|
+
onExport={(categoryData) => onExport?.('categories', categoryData)}
|
|
197
|
+
/>
|
|
198
|
+
{categoriesFooterSlot}
|
|
199
|
+
</section>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const renderLayoutContent = () => {
|
|
204
|
+
const sections = [
|
|
205
|
+
renderMetricsSection(),
|
|
206
|
+
renderTrendsSection(),
|
|
207
|
+
renderCategoriesSection()
|
|
208
|
+
].filter(Boolean);
|
|
209
|
+
|
|
210
|
+
if (layout === 'detailed' && sections.length > 1) {
|
|
211
|
+
// For detailed layout, use a grid for trends and categories
|
|
212
|
+
const [metricsSection, ...otherSections] = sections;
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div className={getLayoutClasses()}>
|
|
216
|
+
{metricsSection}
|
|
217
|
+
|
|
218
|
+
{otherSections.length > 0 && (
|
|
219
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
220
|
+
{otherSections}
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<div className={getLayoutClasses()}>
|
|
229
|
+
{sections}
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
<AppLayout>
|
|
236
|
+
<PageTemplate
|
|
237
|
+
className={cn('container mx-auto px-4 py-6', className)}
|
|
238
|
+
data-component-name="EntityPerformanceDashboardTemplate"
|
|
239
|
+
>
|
|
240
|
+
{headerSlot}
|
|
241
|
+
{renderPageHeader()}
|
|
242
|
+
{renderLayoutContent()}
|
|
243
|
+
{footerSlot}
|
|
244
|
+
</PageTemplate>
|
|
245
|
+
</AppLayout>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// Enhanced version with real-time updates
|
|
250
|
+
export interface EntityPerformanceDashboardTemplateWithRealTimeProps<T extends EntityData>
|
|
251
|
+
extends EntityPerformanceDashboardTemplateProps<T> {
|
|
252
|
+
enableRealTime?: boolean;
|
|
253
|
+
refreshInterval?: number;
|
|
254
|
+
onDataUpdate?: (newData: T[]) => void;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export const EntityPerformanceDashboardTemplateWithRealTime = <T extends EntityData>({
|
|
258
|
+
enableRealTime = false,
|
|
259
|
+
refreshInterval = 30000, // 30 seconds
|
|
260
|
+
onDataUpdate,
|
|
261
|
+
...props
|
|
262
|
+
}: EntityPerformanceDashboardTemplateWithRealTimeProps<T>) => {
|
|
263
|
+
React.useEffect(() => {
|
|
264
|
+
if (!enableRealTime) return;
|
|
265
|
+
|
|
266
|
+
const interval = setInterval(() => {
|
|
267
|
+
if (onDataUpdate) {
|
|
268
|
+
// This would typically fetch new data from an API
|
|
269
|
+
onDataUpdate(props.data);
|
|
270
|
+
}
|
|
271
|
+
}, refreshInterval);
|
|
272
|
+
|
|
273
|
+
return () => clearInterval(interval);
|
|
274
|
+
}, [enableRealTime, refreshInterval, onDataUpdate, props.data]);
|
|
275
|
+
|
|
276
|
+
return <EntityPerformanceDashboardTemplate {...props} />;
|
|
277
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { EntityTemplateConfig, EntityData } from '../../../atoms/types';
|
|
2
|
+
import { DollarSign, Target, TrendingUp, Wallet } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
export interface FinancialTransaction extends EntityData {
|
|
5
|
+
id: string;
|
|
6
|
+
amount: number;
|
|
7
|
+
category: string;
|
|
8
|
+
account: string;
|
|
9
|
+
date: string;
|
|
10
|
+
description: string;
|
|
11
|
+
type: 'income' | 'expense' | 'transfer';
|
|
12
|
+
status: 'completed' | 'pending' | 'failed';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const financialConfig: EntityTemplateConfig<FinancialTransaction> = {
|
|
16
|
+
entityType: 'transactional',
|
|
17
|
+
display: {
|
|
18
|
+
title: 'Financial Dashboard',
|
|
19
|
+
description: 'Personal finance overview and transaction management',
|
|
20
|
+
category: 2 // Green theme for financial success
|
|
21
|
+
},
|
|
22
|
+
metrics: [
|
|
23
|
+
{
|
|
24
|
+
key: 'totalBalance',
|
|
25
|
+
label: 'Total Balance',
|
|
26
|
+
type: 'currency',
|
|
27
|
+
trend: true,
|
|
28
|
+
icon: DollarSign,
|
|
29
|
+
aggregation: 'sum'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: 'monthlyIncome',
|
|
33
|
+
label: 'Monthly Income',
|
|
34
|
+
type: 'currency',
|
|
35
|
+
trend: true,
|
|
36
|
+
icon: TrendingUp,
|
|
37
|
+
aggregation: 'sum'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'monthlyExpenses',
|
|
41
|
+
label: 'Monthly Expenses',
|
|
42
|
+
type: 'currency',
|
|
43
|
+
trend: true,
|
|
44
|
+
icon: Wallet,
|
|
45
|
+
aggregation: 'sum'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: 'savingsRate',
|
|
49
|
+
label: 'Savings Rate',
|
|
50
|
+
type: 'percentage',
|
|
51
|
+
target: 20,
|
|
52
|
+
icon: Target
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
temporal: {
|
|
56
|
+
cycles: ['monthly', 'quarterly', 'yearly'],
|
|
57
|
+
defaultCycle: 'monthly',
|
|
58
|
+
enableComparisons: true,
|
|
59
|
+
forecasting: {
|
|
60
|
+
enabled: true,
|
|
61
|
+
periods: 3,
|
|
62
|
+
algorithm: 'seasonal'
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
categories: {
|
|
66
|
+
hierarchy: ['account', 'category', 'type'],
|
|
67
|
+
defaultGroupBy: 'category',
|
|
68
|
+
enableDrillDown: true,
|
|
69
|
+
colorCoding: true
|
|
70
|
+
},
|
|
71
|
+
actions: [
|
|
72
|
+
{
|
|
73
|
+
label: 'Add Transaction',
|
|
74
|
+
type: 'primary',
|
|
75
|
+
onClick: ({ data, config }) => {
|
|
76
|
+
console.log('Adding new transaction', { data, config });
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: 'Generate Report',
|
|
81
|
+
type: 'secondary',
|
|
82
|
+
onClick: ({ selectedItems, data, config }) => {
|
|
83
|
+
console.log('Generating report', { selectedItems, data, config });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Sample financial data for testing
|
|
90
|
+
export const sampleFinancialData: FinancialTransaction[] = [
|
|
91
|
+
{
|
|
92
|
+
id: '1',
|
|
93
|
+
amount: 5000,
|
|
94
|
+
category: 'Salary',
|
|
95
|
+
account: 'Checking',
|
|
96
|
+
date: '2024-01-15',
|
|
97
|
+
description: 'Monthly salary',
|
|
98
|
+
type: 'income',
|
|
99
|
+
status: 'completed'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: '2',
|
|
103
|
+
amount: -1200,
|
|
104
|
+
category: 'Rent',
|
|
105
|
+
account: 'Checking',
|
|
106
|
+
date: '2024-01-01',
|
|
107
|
+
description: 'Monthly rent payment',
|
|
108
|
+
type: 'expense',
|
|
109
|
+
status: 'completed'
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: '3',
|
|
113
|
+
amount: -300,
|
|
114
|
+
category: 'Groceries',
|
|
115
|
+
account: 'Checking',
|
|
116
|
+
date: '2024-01-10',
|
|
117
|
+
description: 'Weekly groceries',
|
|
118
|
+
type: 'expense',
|
|
119
|
+
status: 'completed'
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: '4',
|
|
123
|
+
amount: -80,
|
|
124
|
+
category: 'Utilities',
|
|
125
|
+
account: 'Checking',
|
|
126
|
+
date: '2024-01-05',
|
|
127
|
+
description: 'Electric bill',
|
|
128
|
+
type: 'expense',
|
|
129
|
+
status: 'completed'
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: '5',
|
|
133
|
+
amount: -45,
|
|
134
|
+
category: 'Entertainment',
|
|
135
|
+
account: 'Checking',
|
|
136
|
+
date: '2024-01-12',
|
|
137
|
+
description: 'Movie tickets',
|
|
138
|
+
type: 'expense',
|
|
139
|
+
status: 'completed'
|
|
140
|
+
}
|
|
141
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './financial-config';
|
|
@@ -2,13 +2,12 @@ import React from 'react'
|
|
|
2
2
|
import type { ReactNode } from 'react'
|
|
3
3
|
import { createRoot } from 'react-dom/client'
|
|
4
4
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
5
|
-
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|
6
5
|
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
|
7
6
|
import { AuthProvider } from '../features/auth'
|
|
8
|
-
import { SidebarProvider } from '../molecules/layout'
|
|
7
|
+
import { SidebarProvider, NavigationProvider } from '../molecules/layout'
|
|
9
8
|
import { AppLayout } from '../molecules/layout'
|
|
10
9
|
import { DashboardTemplate } from './DashboardTemplate'
|
|
11
|
-
import type { AuthConfig } from '../atoms/types'
|
|
10
|
+
import type { AuthConfig, NavigationConfig } from '../atoms/types'
|
|
12
11
|
|
|
13
12
|
// App configuration interface
|
|
14
13
|
export interface AppConfig {
|
|
@@ -24,6 +23,7 @@ export interface AppConfig {
|
|
|
24
23
|
theme?: string
|
|
25
24
|
darkMode?: boolean
|
|
26
25
|
auth?: AuthConfig
|
|
26
|
+
navigation?: NavigationConfig
|
|
27
27
|
customProviders?: React.ComponentType<{ children: ReactNode }>[]
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -50,6 +50,7 @@ export function createReactApp(config: AppConfig | string): AppInstance {
|
|
|
50
50
|
enableQuery = true,
|
|
51
51
|
enableRouting = true,
|
|
52
52
|
auth,
|
|
53
|
+
navigation,
|
|
53
54
|
customProviders = []
|
|
54
55
|
} = appConfig
|
|
55
56
|
|
|
@@ -94,9 +95,11 @@ export function createReactApp(config: AppConfig | string): AppInstance {
|
|
|
94
95
|
// Wrap with layout providers
|
|
95
96
|
if (enableRouting) {
|
|
96
97
|
tree = (
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
<NavigationProvider initialNavigation={navigation}>
|
|
99
|
+
<SidebarProvider>
|
|
100
|
+
{tree}
|
|
101
|
+
</SidebarProvider>
|
|
102
|
+
</NavigationProvider>
|
|
100
103
|
)
|
|
101
104
|
}
|
|
102
105
|
|
|
@@ -110,7 +113,11 @@ export function createReactApp(config: AppConfig | string): AppInstance {
|
|
|
110
113
|
tree = (
|
|
111
114
|
<QueryClientProvider client={queryClient}>
|
|
112
115
|
{tree}
|
|
113
|
-
{import.meta.env.DEV &&
|
|
116
|
+
{import.meta.env.DEV && (
|
|
117
|
+
import('@tanstack/react-query-devtools').then(({ ReactQueryDevtools }) => (
|
|
118
|
+
<ReactQueryDevtools initialIsOpen={false} />
|
|
119
|
+
))
|
|
120
|
+
)}
|
|
114
121
|
</QueryClientProvider>
|
|
115
122
|
)
|
|
116
123
|
}
|