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