@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.
Files changed (86) hide show
  1. package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts +19 -0
  2. package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts.map +1 -0
  3. package/dist/atoms/composed/SalesPanel/index.d.ts +2 -0
  4. package/dist/atoms/composed/SalesPanel/index.d.ts.map +1 -0
  5. package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts +63 -0
  6. package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts.map +1 -0
  7. package/dist/atoms/composed/index.d.ts +1 -0
  8. package/dist/atoms/composed/index.d.ts.map +1 -1
  9. package/dist/atoms/types/entity-config.d.ts +117 -0
  10. package/dist/atoms/types/entity-config.d.ts.map +1 -0
  11. package/dist/atoms/types/index.d.ts +2 -0
  12. package/dist/atoms/types/index.d.ts.map +1 -1
  13. package/dist/atoms/types/navigation.d.ts +30 -0
  14. package/dist/atoms/types/navigation.d.ts.map +1 -0
  15. package/dist/atoms/ui/ErrorBoundary.d.ts +1 -1
  16. package/dist/atoms/ui/button.d.ts +1 -1
  17. package/dist/atoms/utils/icon-resolver.d.ts +72 -0
  18. package/dist/atoms/utils/icon-resolver.d.ts.map +1 -0
  19. package/dist/atoms/utils/metric-engine.d.ts +30 -0
  20. package/dist/atoms/utils/metric-engine.d.ts.map +1 -0
  21. package/dist/atoms/utils/utils.d.ts +2 -0
  22. package/dist/atoms/utils/utils.d.ts.map +1 -1
  23. package/dist/features/auth/components/ProtectedRoute.d.ts +1 -1
  24. package/dist/frontend-patterns.css +1 -1
  25. package/dist/index.es.js +402 -14
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/index.js +402 -14
  28. package/dist/index.js.map +1 -1
  29. package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts +16 -0
  30. package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts.map +1 -0
  31. package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts +2 -0
  32. package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts.map +1 -0
  33. package/dist/molecules/layout/NavigationContext.d.ts +15 -0
  34. package/dist/molecules/layout/NavigationContext.d.ts.map +1 -0
  35. package/dist/molecules/layout/Sidebar.d.ts.map +1 -1
  36. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +2 -0
  37. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -1
  38. package/dist/molecules/layout/index.d.ts +3 -0
  39. package/dist/molecules/layout/index.d.ts.map +1 -1
  40. package/dist/templates/factory.d.ts +2 -1
  41. package/dist/templates/factory.d.ts.map +1 -1
  42. package/dist/templates/index.d.ts.map +1 -1
  43. package/package.json +7 -3
  44. package/src/App.tsx +11 -1
  45. package/src/__tests__/atoms/composed/databadge.test.tsx +106 -0
  46. package/src/__tests__/atoms/composed/statcard.test.tsx +133 -0
  47. package/src/__tests__/atoms/utils/icon-resolver.test.tsx +140 -0
  48. package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
  49. package/src/atoms/composed/SalesPanel/index.ts +1 -0
  50. package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
  51. package/src/atoms/composed/index.ts +1 -0
  52. package/src/atoms/types/entity-config.ts +127 -0
  53. package/src/atoms/types/index.ts +3 -1
  54. package/src/atoms/types/navigation.ts +43 -0
  55. package/src/atoms/utils/icon-resolver.tsx +54 -0
  56. package/src/atoms/utils/metric-engine.ts +236 -0
  57. package/src/atoms/utils/utils.ts +4 -2
  58. package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
  59. package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
  60. package/src/molecules/layout/NavigationContext.tsx +63 -0
  61. package/src/molecules/layout/Sidebar.tsx +10 -23
  62. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +32 -10
  63. package/src/molecules/layout/index.ts +4 -1
  64. package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
  65. package/src/organisms/entity/EntityListPanel.tsx +339 -0
  66. package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
  67. package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
  68. package/src/organisms/entity/index.ts +4 -0
  69. package/src/organisms/index.ts +5 -1
  70. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +77 -75
  71. package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
  72. package/src/pages/AdminShowcase/index.tsx +2 -1
  73. package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
  74. package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
  75. package/src/pages/EntityShowcase/index.ts +2 -0
  76. package/src/pages/EntityTemplateExample.tsx +229 -0
  77. package/src/pages/TestEntityTemplate.tsx +40 -0
  78. package/src/pages/index.ts +2 -1
  79. package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
  80. package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
  81. package/src/templates/entity/configs/financial-config.ts +141 -0
  82. package/src/templates/entity/configs/index.ts +1 -0
  83. package/src/templates/entity/index.ts +3 -0
  84. package/src/templates/factory.tsx +14 -7
  85. package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
  86. 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';
@@ -0,0 +1,3 @@
1
+ export * from './EntityPerformanceDashboardTemplate';
2
+ export * from './EntityManagementTemplate';
3
+ export * from './configs';
@@ -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
- <SidebarProvider>
98
- {tree}
99
- </SidebarProvider>
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 && <ReactQueryDevtools initialIsOpen={false} />}
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
  }