@pattern-stack/frontend-patterns 0.0.6 → 0.1.1
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/index.d.ts +0 -1
- package/dist/atoms/composed/index.d.ts.map +1 -1
- package/dist/atoms/types/index.d.ts +0 -2
- package/dist/atoms/types/index.d.ts.map +1 -1
- package/dist/atoms/ui/ErrorBoundary.d.ts +1 -1
- package/dist/atoms/ui/button.d.ts +1 -1
- package/dist/atoms/utils/utils.d.ts +0 -2
- 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 +15 -403
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +14 -403
- package/dist/index.js.map +1 -1
- package/dist/molecules/layout/Sidebar.d.ts.map +1 -1
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +0 -2
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -1
- package/dist/molecules/layout/index.d.ts +0 -3
- package/dist/molecules/layout/index.d.ts.map +1 -1
- package/dist/templates/factory.d.ts +1 -2
- package/dist/templates/factory.d.ts.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/package.json +3 -7
- package/src/App.tsx +1 -11
- package/src/atoms/composed/index.ts +0 -1
- package/src/atoms/types/index.ts +1 -3
- package/src/atoms/utils/utils.ts +2 -4
- package/src/molecules/layout/Sidebar.tsx +23 -10
- package/src/molecules/layout/SidebarButton/SidebarButton.tsx +10 -32
- package/src/molecules/layout/index.ts +1 -4
- package/src/organisms/index.ts +1 -5
- package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +75 -77
- package/src/pages/AdminShowcase/index.tsx +1 -2
- package/src/pages/index.ts +1 -2
- package/src/templates/factory.tsx +7 -14
- package/src/templates/index.ts +0 -4
- package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts +0 -19
- package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts.map +0 -1
- package/dist/atoms/composed/SalesPanel/index.d.ts +0 -2
- package/dist/atoms/composed/SalesPanel/index.d.ts.map +0 -1
- package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts +0 -63
- package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts.map +0 -1
- package/dist/atoms/types/entity-config.d.ts +0 -117
- package/dist/atoms/types/entity-config.d.ts.map +0 -1
- package/dist/atoms/types/navigation.d.ts +0 -30
- package/dist/atoms/types/navigation.d.ts.map +0 -1
- package/dist/atoms/utils/icon-resolver.d.ts +0 -72
- package/dist/atoms/utils/icon-resolver.d.ts.map +0 -1
- package/dist/atoms/utils/metric-engine.d.ts +0 -30
- package/dist/atoms/utils/metric-engine.d.ts.map +0 -1
- package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts +0 -16
- package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts.map +0 -1
- package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts +0 -2
- package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts.map +0 -1
- package/dist/molecules/layout/NavigationContext.d.ts +0 -15
- package/dist/molecules/layout/NavigationContext.d.ts.map +0 -1
- package/src/__tests__/atoms/composed/databadge.test.tsx +0 -106
- package/src/__tests__/atoms/composed/statcard.test.tsx +0 -133
- package/src/__tests__/atoms/utils/icon-resolver.test.tsx +0 -140
- package/src/atoms/composed/SalesPanel/SalesPanel.tsx +0 -116
- package/src/atoms/composed/SalesPanel/index.ts +0 -1
- package/src/atoms/composed/SalesPanel/mockSalesData.ts +0 -151
- package/src/atoms/types/entity-config.ts +0 -127
- package/src/atoms/types/navigation.ts +0 -43
- package/src/atoms/utils/icon-resolver.tsx +0 -54
- package/src/atoms/utils/metric-engine.ts +0 -236
- package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +0 -42
- package/src/molecules/layout/DashboardWithSidePanel/index.ts +0 -1
- package/src/molecules/layout/NavigationContext.tsx +0 -63
- package/src/organisms/entity/CategoryBreakdownPanel.tsx +0 -427
- package/src/organisms/entity/EntityListPanel.tsx +0 -339
- package/src/organisms/entity/MetricsOverviewPanel.tsx +0 -236
- package/src/organisms/entity/TrendAnalysisPanel.tsx +0 -337
- package/src/organisms/entity/index.ts +0 -4
- package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +0 -158
- package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +0 -137
- package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +0 -117
- package/src/pages/EntityShowcase/index.ts +0 -2
- package/src/pages/EntityTemplateExample.tsx +0 -229
- package/src/pages/TestEntityTemplate.tsx +0 -40
- package/src/templates/entity/EntityManagementTemplate.tsx +0 -430
- package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +0 -277
- package/src/templates/entity/configs/financial-config.ts +0 -141
- package/src/templates/entity/configs/index.ts +0 -1
- package/src/templates/entity/index.ts +0 -3
- package/src/templates/financial/FinancialDashboardTemplate.tsx +0 -326
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Navigation configuration types for createReactApp factory
|
|
2
|
-
|
|
3
|
-
export type IconName =
|
|
4
|
-
| 'Palette' | 'Menu' | 'X' | 'Shield' | 'Users' | 'BarChart3'
|
|
5
|
-
| 'Database' | 'TrendingUp' | 'Layout' | 'Home' | 'Settings'
|
|
6
|
-
| 'Bell' | 'Search' | 'Plus' | 'Edit' | 'Trash2' | 'Eye'
|
|
7
|
-
| 'Download' | 'Upload' | 'Share' | 'Lock' | 'Unlock'
|
|
8
|
-
| 'Mail' | 'Phone' | 'Calendar' | 'Clock' | 'MapPin'
|
|
9
|
-
| 'Star' | 'Heart' | 'Bookmark' | 'Tag' | 'Flag'
|
|
10
|
-
| 'File' | 'Folder' | 'Image' | 'Video' | 'Music'
|
|
11
|
-
| 'ChevronRight' | 'ChevronDown' | 'ChevronLeft' | 'ChevronUp'
|
|
12
|
-
| 'ArrowRight' | 'ArrowLeft' | 'ArrowUp' | 'ArrowDown'
|
|
13
|
-
| 'Check' | 'AlertCircle' | 'Info' | 'HelpCircle'
|
|
14
|
-
|
|
15
|
-
export interface NavigationItem {
|
|
16
|
-
/** Unique identifier for the navigation item */
|
|
17
|
-
value: string
|
|
18
|
-
/** Display label for the navigation item */
|
|
19
|
-
label: string
|
|
20
|
-
/** Path to navigate to when clicked */
|
|
21
|
-
path: string
|
|
22
|
-
/** Icon name from Lucide React icons */
|
|
23
|
-
icon: IconName
|
|
24
|
-
/** Optional category for color theming (1-8) */
|
|
25
|
-
category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
|
|
26
|
-
/** Optional badge text/count to display */
|
|
27
|
-
badge?: string | number
|
|
28
|
-
/** Whether this item is disabled */
|
|
29
|
-
disabled?: boolean
|
|
30
|
-
/** Child navigation items for nested menus */
|
|
31
|
-
children?: NavigationItem[]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface NavigationConfig {
|
|
35
|
-
/** Main navigation items */
|
|
36
|
-
items: NavigationItem[]
|
|
37
|
-
/** Whether to show the default showcase navigation as fallback */
|
|
38
|
-
showDefaultNavigation?: boolean
|
|
39
|
-
/** Custom logo or title for the sidebar header */
|
|
40
|
-
logo?: string
|
|
41
|
-
/** Whether the sidebar starts expanded */
|
|
42
|
-
defaultExpanded?: boolean
|
|
43
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Palette, Menu, X, Shield, Users, BarChart3, Database, TrendingUp, Layout,
|
|
3
|
-
Home, Settings, Bell, Search, Plus, Edit, Trash2, Eye, Download, Upload,
|
|
4
|
-
Share, Lock, Unlock, Mail, Phone, Calendar, Clock, MapPin, Star, Heart,
|
|
5
|
-
Bookmark, Tag, Flag, File, Folder, Image, Video, Music, ChevronRight,
|
|
6
|
-
ChevronDown, ChevronLeft, ChevronUp, ArrowRight, ArrowLeft, ArrowUp,
|
|
7
|
-
ArrowDown, Check, AlertCircle, Info, HelpCircle
|
|
8
|
-
} from 'lucide-react'
|
|
9
|
-
import type { IconName } from '../types'
|
|
10
|
-
|
|
11
|
-
// Map of icon names to Lucide React components
|
|
12
|
-
export const iconMap = {
|
|
13
|
-
Palette, Menu, X, Shield, Users, BarChart3, Database, TrendingUp, Layout,
|
|
14
|
-
Home, Settings, Bell, Search, Plus, Edit, Trash2, Eye, Download, Upload,
|
|
15
|
-
Share, Lock, Unlock, Mail, Phone, Calendar, Clock, MapPin, Star, Heart,
|
|
16
|
-
Bookmark, Tag, Flag, File, Folder, Image, Video, Music, ChevronRight,
|
|
17
|
-
ChevronDown, ChevronLeft, ChevronUp, ArrowRight, ArrowLeft, ArrowUp,
|
|
18
|
-
ArrowDown, Check, AlertCircle, Info, HelpCircle
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface IconProps {
|
|
22
|
-
name: IconName
|
|
23
|
-
className?: string
|
|
24
|
-
size?: number
|
|
25
|
-
[key: string]: any // Allow any additional props to be passed through
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Resolves an icon name to the corresponding Lucide React component
|
|
30
|
-
*/
|
|
31
|
-
export const Icon = ({ name, className = "w-5 h-5", size, ...props }: IconProps) => {
|
|
32
|
-
const IconComponent = iconMap[name]
|
|
33
|
-
|
|
34
|
-
if (!IconComponent) {
|
|
35
|
-
console.warn(`Icon "${name}" not found. Using default Menu icon.`)
|
|
36
|
-
return <Menu className={className} size={size} {...props} />
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return <IconComponent className={className} size={size} {...props} />
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Get the icon component directly for custom rendering
|
|
44
|
-
*/
|
|
45
|
-
export const getIcon = (name: IconName) => {
|
|
46
|
-
return iconMap[name] || Menu
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Check if an icon name is valid
|
|
51
|
-
*/
|
|
52
|
-
export const isValidIcon = (name: string): name is IconName => {
|
|
53
|
-
return name in iconMap
|
|
54
|
-
}
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import type { MetricConfig, MetricValue, EntityData, FormatConfig, TrendDataPoint } from '../types';
|
|
2
|
-
|
|
3
|
-
export class MetricCalculationEngine {
|
|
4
|
-
static calculateMetric(
|
|
5
|
-
config: MetricConfig,
|
|
6
|
-
data: EntityData[],
|
|
7
|
-
previousData?: EntityData[]
|
|
8
|
-
): MetricValue {
|
|
9
|
-
const currentValue = this.aggregateValue(config, data);
|
|
10
|
-
const previousValue = previousData ? this.aggregateValue(config, previousData) : undefined;
|
|
11
|
-
|
|
12
|
-
const trend = this.calculateTrend(currentValue, previousValue);
|
|
13
|
-
const formattedValue = this.formatValue(currentValue, config.format, config.type);
|
|
14
|
-
|
|
15
|
-
const target = typeof config.target === 'function'
|
|
16
|
-
? config.target(data)
|
|
17
|
-
: config.target;
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
current: currentValue,
|
|
21
|
-
previous: previousValue,
|
|
22
|
-
trend,
|
|
23
|
-
target,
|
|
24
|
-
formattedValue
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private static aggregateValue(config: MetricConfig, data: EntityData[]): number {
|
|
29
|
-
if (!data.length) return 0;
|
|
30
|
-
|
|
31
|
-
const values = data
|
|
32
|
-
.map(item => {
|
|
33
|
-
const value = this.extractValue(item, config.key);
|
|
34
|
-
return typeof value === 'number' ? value : 0;
|
|
35
|
-
})
|
|
36
|
-
.filter(value => !isNaN(value));
|
|
37
|
-
|
|
38
|
-
if (!values.length) return 0;
|
|
39
|
-
|
|
40
|
-
switch (config.aggregation || 'sum') {
|
|
41
|
-
case 'sum':
|
|
42
|
-
return values.reduce((sum, value) => sum + value, 0);
|
|
43
|
-
case 'avg':
|
|
44
|
-
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
45
|
-
case 'count':
|
|
46
|
-
return values.length;
|
|
47
|
-
case 'min':
|
|
48
|
-
return Math.min(...values);
|
|
49
|
-
case 'max':
|
|
50
|
-
return Math.max(...values);
|
|
51
|
-
default:
|
|
52
|
-
return values.reduce((sum, value) => sum + value, 0);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private static extractValue(item: EntityData, key: string): unknown {
|
|
57
|
-
return key.split('.').reduce((obj: any, k: string) => obj?.[k], item);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private static calculateTrend(current: number, previous?: number): 'up' | 'down' | 'neutral' {
|
|
61
|
-
if (previous === undefined || previous === 0) return 'neutral';
|
|
62
|
-
|
|
63
|
-
const change = ((current - previous) / Math.abs(previous)) * 100;
|
|
64
|
-
|
|
65
|
-
if (change > 1) return 'up';
|
|
66
|
-
if (change < -1) return 'down';
|
|
67
|
-
return 'neutral';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private static formatValue(value: number, format?: FormatConfig, type?: string): string {
|
|
71
|
-
let formatted = value;
|
|
72
|
-
|
|
73
|
-
if (format?.decimals !== undefined) {
|
|
74
|
-
formatted = Number(value.toFixed(format.decimals));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let result = formatted.toString();
|
|
78
|
-
|
|
79
|
-
if (format?.thousands !== false && Math.abs(formatted) >= 1000) {
|
|
80
|
-
result = formatted.toLocaleString();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
switch (type) {
|
|
84
|
-
case 'currency':
|
|
85
|
-
result = new Intl.NumberFormat('en-US', {
|
|
86
|
-
style: 'currency',
|
|
87
|
-
currency: 'USD',
|
|
88
|
-
minimumFractionDigits: format?.decimals ?? 2,
|
|
89
|
-
maximumFractionDigits: format?.decimals ?? 2
|
|
90
|
-
}).format(formatted);
|
|
91
|
-
break;
|
|
92
|
-
case 'percentage':
|
|
93
|
-
result = `${formatted.toFixed(format?.decimals ?? 1)}%`;
|
|
94
|
-
break;
|
|
95
|
-
case 'duration':
|
|
96
|
-
result = this.formatDuration(formatted);
|
|
97
|
-
break;
|
|
98
|
-
case 'ratio':
|
|
99
|
-
result = `${formatted.toFixed(format?.decimals ?? 2)}:1`;
|
|
100
|
-
break;
|
|
101
|
-
default:
|
|
102
|
-
if (format?.prefix) result = format.prefix + result;
|
|
103
|
-
if (format?.suffix) result = result + format.suffix;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private static formatDuration(minutes: number): string {
|
|
110
|
-
const hours = Math.floor(minutes / 60);
|
|
111
|
-
const mins = Math.floor(minutes % 60);
|
|
112
|
-
|
|
113
|
-
if (hours > 0) {
|
|
114
|
-
return `${hours}h ${mins}m`;
|
|
115
|
-
}
|
|
116
|
-
return `${mins}m`;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
static calculateTrendData(
|
|
120
|
-
config: MetricConfig,
|
|
121
|
-
data: EntityData[],
|
|
122
|
-
dateField: string = 'date',
|
|
123
|
-
periods: number = 12
|
|
124
|
-
): TrendDataPoint[] {
|
|
125
|
-
const groupedData = this.groupByPeriod(data, dateField);
|
|
126
|
-
const sortedDates = Object.keys(groupedData).sort();
|
|
127
|
-
|
|
128
|
-
return sortedDates.slice(-periods).map(date => ({
|
|
129
|
-
date,
|
|
130
|
-
value: this.aggregateValue(config, groupedData[date]),
|
|
131
|
-
label: this.formatDateLabel(date)
|
|
132
|
-
}));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private static groupByPeriod(data: EntityData[], dateField: string): Record<string, EntityData[]> {
|
|
136
|
-
return data.reduce((groups, item) => {
|
|
137
|
-
const date = this.extractValue(item, dateField);
|
|
138
|
-
if (!date) return groups;
|
|
139
|
-
|
|
140
|
-
const period = new Date(date as string | number | Date).toISOString().split('T')[0];
|
|
141
|
-
|
|
142
|
-
if (!groups[period]) groups[period] = [];
|
|
143
|
-
groups[period].push(item);
|
|
144
|
-
|
|
145
|
-
return groups;
|
|
146
|
-
}, {} as Record<string, EntityData[]>);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private static formatDateLabel(dateString: string): string {
|
|
150
|
-
const date = new Date(dateString);
|
|
151
|
-
return date.toLocaleDateString('en-US', {
|
|
152
|
-
month: 'short',
|
|
153
|
-
day: 'numeric'
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
static calculateCategoryBreakdown(
|
|
158
|
-
data: EntityData[],
|
|
159
|
-
categoryField: string,
|
|
160
|
-
valueField: string,
|
|
161
|
-
maxCategories: number = 8
|
|
162
|
-
) {
|
|
163
|
-
const groups = data.reduce((acc, item) => {
|
|
164
|
-
const category = String(this.extractValue(item, categoryField) || 'Unknown');
|
|
165
|
-
const value = this.extractValue(item, valueField) || 0;
|
|
166
|
-
|
|
167
|
-
if (!acc[category]) acc[category] = 0;
|
|
168
|
-
acc[category] += typeof value === 'number' ? value : 0;
|
|
169
|
-
|
|
170
|
-
return acc;
|
|
171
|
-
}, {} as Record<string, number>);
|
|
172
|
-
|
|
173
|
-
const total = Object.values(groups).reduce((sum, value) => sum + value, 0);
|
|
174
|
-
|
|
175
|
-
let categories = Object.entries(groups)
|
|
176
|
-
.map(([category, value]) => ({
|
|
177
|
-
category,
|
|
178
|
-
value,
|
|
179
|
-
percentage: total > 0 ? (value / total) * 100 : 0
|
|
180
|
-
}))
|
|
181
|
-
.sort((a, b) => b.value - a.value);
|
|
182
|
-
|
|
183
|
-
if (categories.length > maxCategories) {
|
|
184
|
-
const topCategories = categories.slice(0, maxCategories - 1);
|
|
185
|
-
const otherValue = categories.slice(maxCategories - 1)
|
|
186
|
-
.reduce((sum, cat) => sum + cat.value, 0);
|
|
187
|
-
|
|
188
|
-
topCategories.push({
|
|
189
|
-
category: 'Other',
|
|
190
|
-
value: otherValue,
|
|
191
|
-
percentage: total > 0 ? (otherValue / total) * 100 : 0
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
categories = topCategories;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return categories;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
static detectInsights(
|
|
201
|
-
metrics: Record<string, MetricValue>,
|
|
202
|
-
thresholds: Record<string, { warning: number; critical: number }> = {}
|
|
203
|
-
) {
|
|
204
|
-
const insights = [];
|
|
205
|
-
|
|
206
|
-
for (const [key, metric] of Object.entries(metrics)) {
|
|
207
|
-
const threshold = thresholds[key];
|
|
208
|
-
|
|
209
|
-
if (metric.target && metric.current < metric.target * 0.8) {
|
|
210
|
-
insights.push({
|
|
211
|
-
type: 'negative' as const,
|
|
212
|
-
message: `${key} is significantly below target`,
|
|
213
|
-
value: metric.current
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (metric.trend === 'up' && metric.previous && metric.current > metric.previous * 1.2) {
|
|
218
|
-
insights.push({
|
|
219
|
-
type: 'positive' as const,
|
|
220
|
-
message: `${key} showing strong growth`,
|
|
221
|
-
value: metric.current
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (threshold && metric.current >= threshold.critical) {
|
|
226
|
-
insights.push({
|
|
227
|
-
type: 'negative' as const,
|
|
228
|
-
message: `${key} has reached critical threshold`,
|
|
229
|
-
value: metric.current
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return insights.slice(0, 5);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { cn } from '../../../atoms/utils/utils';
|
|
3
|
-
|
|
4
|
-
interface DashboardWithSidePanelProps {
|
|
5
|
-
/** Main dashboard content */
|
|
6
|
-
children: React.ReactNode;
|
|
7
|
-
/** Side panel component to display */
|
|
8
|
-
sidePanel?: React.ReactNode;
|
|
9
|
-
/** Whether to show the side panel */
|
|
10
|
-
showSidePanel?: boolean;
|
|
11
|
-
/** Side panel width in Tailwind units (e.g., 72, 80) */
|
|
12
|
-
sidePanelWidth?: number;
|
|
13
|
-
/** Additional CSS classes */
|
|
14
|
-
className?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const DashboardWithSidePanel: React.FC<DashboardWithSidePanelProps> = ({
|
|
18
|
-
children,
|
|
19
|
-
sidePanel,
|
|
20
|
-
showSidePanel = false,
|
|
21
|
-
sidePanelWidth = 72,
|
|
22
|
-
className
|
|
23
|
-
}) => {
|
|
24
|
-
const marginClass = `pr-${sidePanelWidth}`;
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div className={cn('relative h-full', className)}>
|
|
28
|
-
{/* Main Dashboard Content */}
|
|
29
|
-
<div
|
|
30
|
-
className={cn(
|
|
31
|
-
'transition-all duration-300',
|
|
32
|
-
showSidePanel ? marginClass : ''
|
|
33
|
-
)}
|
|
34
|
-
>
|
|
35
|
-
{children}
|
|
36
|
-
</div>
|
|
37
|
-
|
|
38
|
-
{/* Side Panel */}
|
|
39
|
-
{showSidePanel && sidePanel}
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { DashboardWithSidePanel } from './DashboardWithSidePanel';
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, type ReactNode } from 'react'
|
|
2
|
-
import type { NavigationConfig, NavigationItem } from '../../atoms/types'
|
|
3
|
-
|
|
4
|
-
interface NavigationContextType {
|
|
5
|
-
navigation: NavigationConfig
|
|
6
|
-
setNavigation: (config: NavigationConfig) => void
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const NavigationContext = createContext<NavigationContextType | undefined>(undefined)
|
|
10
|
-
|
|
11
|
-
interface NavigationProviderProps {
|
|
12
|
-
children: ReactNode
|
|
13
|
-
initialNavigation?: NavigationConfig
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Default showcase navigation for backwards compatibility
|
|
17
|
-
const defaultShowcaseNavigation: NavigationItem[] = [
|
|
18
|
-
{ value: 'showcase', label: 'Showcase', icon: 'Palette', path: '/showcase', category: 5 },
|
|
19
|
-
{ value: 'admin-dashboard', label: 'Admin Dashboard', icon: 'Shield', path: '/admin/dashboard', category: 2 },
|
|
20
|
-
{ value: 'admin-users', label: 'User Management', icon: 'Users', path: '/admin/users', category: 3 },
|
|
21
|
-
{ value: 'admin-sales', label: 'Sales Dashboard', icon: 'TrendingUp', path: '/admin/sales', category: 4 },
|
|
22
|
-
{ value: 'entity-performance', label: 'Performance Dashboard', icon: 'BarChart3', path: '/entity/performance', category: 6 },
|
|
23
|
-
{ value: 'entity-management', label: 'Entity Management', icon: 'Database', path: '/entity/management', category: 7 },
|
|
24
|
-
{ value: 'entity-template', label: 'Template Example', icon: 'Layout', path: '/entity/template-example', category: 1 }
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
export const NavigationProvider = ({ children, initialNavigation }: NavigationProviderProps) => {
|
|
28
|
-
const [navigation, setNavigation] = React.useState<NavigationConfig>(
|
|
29
|
-
initialNavigation || {
|
|
30
|
-
items: defaultShowcaseNavigation,
|
|
31
|
-
showDefaultNavigation: true,
|
|
32
|
-
defaultExpanded: true
|
|
33
|
-
}
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<NavigationContext.Provider value={{ navigation, setNavigation }}>
|
|
38
|
-
{children}
|
|
39
|
-
</NavigationContext.Provider>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export const useNavigation = () => {
|
|
44
|
-
const context = useContext(NavigationContext)
|
|
45
|
-
if (context === undefined) {
|
|
46
|
-
throw new Error('useNavigation must be used within a NavigationProvider')
|
|
47
|
-
}
|
|
48
|
-
return context
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Helper to get navigation items with fallback
|
|
52
|
-
export const getNavigationItems = (config: NavigationConfig): NavigationItem[] => {
|
|
53
|
-
if (config.items.length > 0) {
|
|
54
|
-
return config.items
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Fallback to showcase navigation if no items provided
|
|
58
|
-
if (config.showDefaultNavigation !== false) {
|
|
59
|
-
return defaultShowcaseNavigation
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return []
|
|
63
|
-
}
|