@pattern-stack/frontend-patterns 0.0.3 → 0.0.4

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 (154) hide show
  1. package/dist/index.es.js +1 -1
  2. package/dist/index.js +1 -0
  3. package/package.json +5 -3
  4. package/src/App.css +42 -0
  5. package/src/App.tsx +54 -0
  6. package/src/__tests__/README.md +221 -0
  7. package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
  8. package/src/__tests__/atoms/ui/button.test.tsx +68 -0
  9. package/src/__tests__/atoms/utils/simple.test.ts +18 -0
  10. package/src/__tests__/atoms/utils/utils.test.ts +77 -0
  11. package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
  12. package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
  13. package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
  14. package/src/__tests__/setup.ts +51 -0
  15. package/src/__tests__/utils.tsx +123 -0
  16. package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
  17. package/src/atoms/composed/Accordion/index.ts +1 -0
  18. package/src/atoms/composed/Alert/Alert.tsx +132 -0
  19. package/src/atoms/composed/Alert/index.ts +1 -0
  20. package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
  21. package/src/atoms/composed/Breadcrumb/index.ts +1 -0
  22. package/src/atoms/composed/Chart/Chart.tsx +425 -0
  23. package/src/atoms/composed/Chart/index.ts +2 -0
  24. package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
  25. package/src/atoms/composed/ColorSwatch/index.ts +1 -0
  26. package/src/atoms/composed/DarkModeToggle.tsx +66 -0
  27. package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
  28. package/src/atoms/composed/DataBadge/index.ts +1 -0
  29. package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
  30. package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
  31. package/src/atoms/composed/DataTable/index.ts +2 -0
  32. package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
  33. package/src/atoms/composed/DateTimePicker/index.ts +2 -0
  34. package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
  35. package/src/atoms/composed/DetailedCard/index.ts +2 -0
  36. package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
  37. package/src/atoms/composed/EmptyState/index.ts +1 -0
  38. package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
  39. package/src/atoms/composed/FileUpload/index.ts +2 -0
  40. package/src/atoms/composed/FormField/FormField.tsx +92 -0
  41. package/src/atoms/composed/FormField/index.ts +1 -0
  42. package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
  43. package/src/atoms/composed/GlobalSearch/index.ts +1 -0
  44. package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
  45. package/src/atoms/composed/IconBadge/index.ts +2 -0
  46. package/src/atoms/composed/Modal/Modal.tsx +223 -0
  47. package/src/atoms/composed/Modal/index.ts +2 -0
  48. package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
  49. package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
  50. package/src/atoms/composed/ProgressBar/index.ts +1 -0
  51. package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
  52. package/src/atoms/composed/StatCard/index.ts +1 -0
  53. package/src/atoms/composed/StyleGuide.tsx +717 -0
  54. package/src/atoms/composed/Toast/Toast.tsx +219 -0
  55. package/src/atoms/composed/Toast/index.ts +1 -0
  56. package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
  57. package/src/atoms/composed/Tooltip/index.ts +1 -0
  58. package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
  59. package/src/atoms/composed/UserAvatar/index.ts +1 -0
  60. package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
  61. package/src/atoms/composed/UserMenu/index.ts +1 -0
  62. package/src/atoms/composed/index.ts +29 -0
  63. package/src/atoms/hooks/useApi.ts +80 -0
  64. package/src/atoms/hooks/useHealth.ts +17 -0
  65. package/src/atoms/index.ts +13 -0
  66. package/src/atoms/services/api/client.ts +134 -0
  67. package/src/atoms/services/auth-service.ts +248 -0
  68. package/src/atoms/services/health.ts +15 -0
  69. package/src/atoms/services/index.ts +3 -0
  70. package/src/atoms/shared/config/constants.ts +17 -0
  71. package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
  72. package/src/atoms/shared/config/environment.ts +10 -0
  73. package/src/atoms/shared/index.ts +4 -0
  74. package/src/atoms/shared/styles/color-palettes.css +566 -0
  75. package/src/atoms/types/auth.ts +62 -0
  76. package/src/atoms/types/generated.ts +1469 -0
  77. package/src/atoms/types/index.ts +4 -0
  78. package/src/atoms/types/loading.ts +28 -0
  79. package/src/atoms/ui/Badge.tsx +30 -0
  80. package/src/atoms/ui/ErrorBoundary.tsx +59 -0
  81. package/src/atoms/ui/Select.tsx +53 -0
  82. package/src/atoms/ui/Switch.tsx +42 -0
  83. package/src/atoms/ui/Tabs.tsx +118 -0
  84. package/src/atoms/ui/avatar.tsx +48 -0
  85. package/src/atoms/ui/button.tsx +70 -0
  86. package/src/atoms/ui/card.tsx +76 -0
  87. package/src/atoms/ui/dropdown-menu.tsx +199 -0
  88. package/src/atoms/ui/index.ts +39 -0
  89. package/src/atoms/ui/input.tsx +23 -0
  90. package/src/atoms/ui/label.tsx +23 -0
  91. package/src/atoms/ui/skeleton.tsx +13 -0
  92. package/src/atoms/ui/spinner.tsx +49 -0
  93. package/src/atoms/ui/table.tsx +116 -0
  94. package/src/atoms/utils/animations.ts +135 -0
  95. package/src/atoms/utils/tooltip-helpers.ts +140 -0
  96. package/src/atoms/utils/utils.ts +9 -0
  97. package/src/features/auth/components/LoginForm.tsx +168 -0
  98. package/src/features/auth/components/LogoutButton.tsx +19 -0
  99. package/src/features/auth/components/ProtectedRoute.tsx +60 -0
  100. package/src/features/auth/components/index.ts +4 -0
  101. package/src/features/auth/hooks/index.ts +2 -0
  102. package/src/features/auth/hooks/useAuth.tsx +205 -0
  103. package/src/features/auth/hooks/usePermissions.ts +35 -0
  104. package/src/features/auth/index.ts +2 -0
  105. package/src/features/index.ts +2 -0
  106. package/src/index.css +704 -0
  107. package/src/index.ts +13 -0
  108. package/src/main.tsx +48 -0
  109. package/src/molecules/.gitkeep +0 -0
  110. package/src/molecules/forms/FormGroup.tsx +75 -0
  111. package/src/molecules/forms/SearchInput.tsx +259 -0
  112. package/src/molecules/forms/index.ts +4 -0
  113. package/src/molecules/index.ts +4 -0
  114. package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
  115. package/src/molecules/layout/AppHeader/index.ts +1 -0
  116. package/src/molecules/layout/AppLayout.tsx +29 -0
  117. package/src/molecules/layout/PageTemplate.tsx +87 -0
  118. package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
  119. package/src/molecules/layout/SectionHeader/index.ts +1 -0
  120. package/src/molecules/layout/ShowcaseSection.tsx +57 -0
  121. package/src/molecules/layout/Sidebar.tsx +144 -0
  122. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
  123. package/src/molecules/layout/SidebarButton/index.ts +1 -0
  124. package/src/molecules/layout/SidebarContext.tsx +31 -0
  125. package/src/molecules/layout/index.ts +7 -0
  126. package/src/molecules/navigation/NavMenu.tsx +188 -0
  127. package/src/molecules/navigation/Pagination.tsx +172 -0
  128. package/src/molecules/navigation/index.ts +4 -0
  129. package/src/organisms/index.ts +5 -0
  130. package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
  131. package/src/organisms/showcase/index.ts +1 -0
  132. package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
  133. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +171 -0
  134. package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
  135. package/src/pages/AdminShowcase/index.tsx +3 -0
  136. package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
  137. package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
  138. package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
  139. package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
  140. package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
  141. package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
  142. package/src/pages/ComponentShowcase/index.tsx +188 -0
  143. package/src/pages/index.ts +2 -0
  144. package/src/templates/AuthTemplate.tsx +216 -0
  145. package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
  146. package/src/templates/DashboardTemplate.tsx +232 -0
  147. package/src/templates/DataTemplate.tsx +319 -0
  148. package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
  149. package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
  150. package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
  151. package/src/templates/admin/index.ts +29 -0
  152. package/src/templates/factory.tsx +169 -0
  153. package/src/templates/index.ts +37 -0
  154. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1 @@
1
+ export { ComponentShowcasePage } from './ComponentShowcasePage';
@@ -0,0 +1,242 @@
1
+ import React, { useState } from 'react';
2
+ import { AdminCRUDTemplate } from '../../templates/admin';
3
+ import type { ResourceSchema, CRUDPermissions, CRUDActions } from '../../templates/admin';
4
+ import { DataBadge } from '../../atoms/composed';
5
+
6
+ // Mock user data
7
+ const mockUsers = [
8
+ {
9
+ id: '1',
10
+ name: 'John Doe',
11
+ email: 'john.doe@example.com',
12
+ role: 'Admin',
13
+ status: 'Active',
14
+ lastLogin: '2024-01-15',
15
+ isActive: true
16
+ },
17
+ {
18
+ id: '2',
19
+ name: 'Sarah Wilson',
20
+ email: 'sarah.wilson@example.com',
21
+ role: 'Editor',
22
+ status: 'Active',
23
+ lastLogin: '2024-01-14',
24
+ isActive: true
25
+ },
26
+ {
27
+ id: '3',
28
+ name: 'Mike Johnson',
29
+ email: 'mike.johnson@example.com',
30
+ role: 'Viewer',
31
+ status: 'Inactive',
32
+ lastLogin: '2024-01-10',
33
+ isActive: false
34
+ },
35
+ {
36
+ id: '4',
37
+ name: 'Emily Davis',
38
+ email: 'emily.davis@example.com',
39
+ role: 'Editor',
40
+ status: 'Active',
41
+ lastLogin: '2024-01-15',
42
+ isActive: true
43
+ },
44
+ {
45
+ id: '5',
46
+ name: 'Alex Chen',
47
+ email: 'alex.chen@example.com',
48
+ role: 'Admin',
49
+ status: 'Active',
50
+ lastLogin: '2024-01-13',
51
+ isActive: true
52
+ }
53
+ ];
54
+
55
+ const userSchema: ResourceSchema = {
56
+ primaryKey: 'id',
57
+ displayName: 'User',
58
+ displayNamePlural: 'Users',
59
+ fields: [
60
+ {
61
+ name: 'name',
62
+ label: 'Full Name',
63
+ type: 'text',
64
+ required: true,
65
+ searchable: true,
66
+ showInTable: true
67
+ },
68
+ {
69
+ name: 'email',
70
+ label: 'Email Address',
71
+ type: 'email',
72
+ required: true,
73
+ searchable: true,
74
+ showInTable: true
75
+ },
76
+ {
77
+ name: 'role',
78
+ label: 'Role',
79
+ type: 'select',
80
+ required: true,
81
+ showInTable: true,
82
+ options: [
83
+ { value: 'admin', label: 'Administrator' },
84
+ { value: 'editor', label: 'Editor' },
85
+ { value: 'viewer', label: 'Viewer' }
86
+ ],
87
+ render: (value) => {
88
+ const roleCategories: Record<string, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8> = {
89
+ 'Admin': 1,
90
+ 'Editor': 2,
91
+ 'Viewer': 6
92
+ };
93
+ return (
94
+ <DataBadge
95
+ variant="category"
96
+ category={roleCategories[value as string] || 3}
97
+ >
98
+ {value as string}
99
+ </DataBadge>
100
+ );
101
+ }
102
+ },
103
+ {
104
+ name: 'isActive',
105
+ label: 'Active Status',
106
+ type: 'boolean',
107
+ required: true,
108
+ showInTable: true
109
+ },
110
+ {
111
+ name: 'lastLogin',
112
+ label: 'Last Login',
113
+ type: 'date',
114
+ showInTable: true
115
+ }
116
+ ]
117
+ };
118
+
119
+ const permissions: CRUDPermissions = {
120
+ view: true,
121
+ create: true,
122
+ edit: true,
123
+ delete: true,
124
+ export: true,
125
+ import: true,
126
+ bulkEdit: true
127
+ };
128
+
129
+ export const AdminCRUDShowcase: React.FC = () => {
130
+ const [users, setUsers] = useState(mockUsers);
131
+ const [isLoading, setIsLoading] = useState(false);
132
+
133
+ const actions: CRUDActions = {
134
+ onCreate: async (data) => {
135
+ setIsLoading(true);
136
+ // Simulate API call
137
+ await new Promise(resolve => setTimeout(resolve, 1000));
138
+
139
+ const newUser = {
140
+ id: String(users.length + 1),
141
+ name: data.name as string,
142
+ email: data.email as string,
143
+ role: data.role as string,
144
+ status: data.isActive ? 'Active' : 'Inactive',
145
+ lastLogin: new Date().toISOString().split('T')[0],
146
+ isActive: data.isActive as boolean
147
+ };
148
+
149
+ setUsers(prev => [...prev, newUser]);
150
+ setIsLoading(false);
151
+ console.log('Created user:', newUser);
152
+ },
153
+
154
+ onUpdate: async (id, data) => {
155
+ setIsLoading(true);
156
+ await new Promise(resolve => setTimeout(resolve, 1000));
157
+
158
+ setUsers(prev => prev.map(user =>
159
+ user.id === id
160
+ ? {
161
+ ...user,
162
+ name: data.name as string,
163
+ email: data.email as string,
164
+ role: data.role as string,
165
+ isActive: data.isActive as boolean,
166
+ status: data.isActive ? 'Active' : 'Inactive'
167
+ }
168
+ : user
169
+ ));
170
+ setIsLoading(false);
171
+ console.log('Updated user:', id, data);
172
+ },
173
+
174
+ onDelete: async (id) => {
175
+ setIsLoading(true);
176
+ await new Promise(resolve => setTimeout(resolve, 500));
177
+
178
+ setUsers(prev => prev.filter(user => user.id !== id));
179
+ setIsLoading(false);
180
+ console.log('Deleted user:', id);
181
+ },
182
+
183
+ onBulkDelete: async (ids) => {
184
+ setIsLoading(true);
185
+ await new Promise(resolve => setTimeout(resolve, 1000));
186
+
187
+ setUsers(prev => prev.filter(user => !ids.includes(user.id)));
188
+ setIsLoading(false);
189
+ console.log('Bulk deleted users:', ids);
190
+ },
191
+
192
+ onExport: async (format) => {
193
+ console.log('Exporting users as:', format);
194
+ // Simulate export
195
+ const data = JSON.stringify(users, null, 2);
196
+ const blob = new Blob([data], { type: 'application/json' });
197
+ const url = URL.createObjectURL(blob);
198
+ const a = document.createElement('a');
199
+ a.href = url;
200
+ a.download = `users.${format}`;
201
+ a.click();
202
+ URL.revokeObjectURL(url);
203
+ },
204
+
205
+ onImport: async (file) => {
206
+ console.log('Importing users from file:', file.name);
207
+ // In a real app, you'd parse the file and add users
208
+ },
209
+
210
+ onRefresh: async () => {
211
+ setIsLoading(true);
212
+ await new Promise(resolve => setTimeout(resolve, 1000));
213
+ setIsLoading(false);
214
+ console.log('Refreshed user data');
215
+ }
216
+ };
217
+
218
+ const customActions = [
219
+ {
220
+ label: 'Send Invitation',
221
+ onClick: () => console.log('Sending bulk invitations'),
222
+ variant: 'outline' as const
223
+ },
224
+ {
225
+ label: 'Generate Report',
226
+ onClick: () => console.log('Generating user report'),
227
+ variant: 'outline' as const
228
+ }
229
+ ];
230
+
231
+ return (
232
+ <AdminCRUDTemplate
233
+ schema={userSchema}
234
+ data={users}
235
+ permissions={permissions}
236
+ actions={actions}
237
+ isLoading={isLoading}
238
+ customActions={customActions}
239
+ category={1}
240
+ />
241
+ );
242
+ };
@@ -0,0 +1,171 @@
1
+ import React from 'react';
2
+ import { AdminDashboardTemplate } from '../../templates/admin';
3
+ import type { MetricCard, ChartConfig, ActivityItem, AlertItem } from '../../templates/admin';
4
+ import { Users, DollarSign, TrendingUp } from 'lucide-react';
5
+
6
+ // Mock data for demonstration
7
+ const mockMetrics: MetricCard[] = [
8
+ {
9
+ title: 'Total Users',
10
+ value: '12,486',
11
+ change: 12.5,
12
+ changePeriod: 'from last month',
13
+ icon: <Users className="w-5 h-5" />,
14
+ category: 1,
15
+ onClick: () => console.log('Navigate to users')
16
+ },
17
+ {
18
+ title: 'Revenue',
19
+ value: '$89,432',
20
+ change: 8.2,
21
+ changePeriod: 'from last month',
22
+ icon: <DollarSign className="w-5 h-5" />,
23
+ category: 2,
24
+ onClick: () => console.log('Navigate to revenue')
25
+ },
26
+ {
27
+ title: 'Growth Rate',
28
+ value: '23.8%',
29
+ change: -2.1,
30
+ changePeriod: 'from last quarter',
31
+ icon: <TrendingUp className="w-5 h-5" />,
32
+ category: 3,
33
+ onClick: () => console.log('Navigate to analytics')
34
+ },
35
+ {
36
+ title: 'Active Sessions',
37
+ value: '1,247',
38
+ change: 5.7,
39
+ changePeriod: 'from yesterday',
40
+ icon: <Users className="w-5 h-5" />,
41
+ category: 4,
42
+ onClick: () => console.log('Navigate to sessions')
43
+ }
44
+ ];
45
+
46
+ const mockCharts: ChartConfig[] = [
47
+ {
48
+ title: 'User Growth',
49
+ type: 'line',
50
+ data: [
51
+ { label: 'Jan', value: 1200 },
52
+ { label: 'Feb', value: 1400 },
53
+ { label: 'Mar', value: 1800 },
54
+ { label: 'Apr', value: 2200 },
55
+ { label: 'May', value: 2800 },
56
+ { label: 'Jun', value: 3200 }
57
+ ],
58
+ category: 1
59
+ },
60
+ {
61
+ title: 'Revenue Distribution',
62
+ type: 'pie',
63
+ data: [
64
+ { label: 'Subscriptions', value: 45000, category: 2 },
65
+ { label: 'One-time', value: 28000, category: 3 },
66
+ { label: 'Enterprise', value: 16000, category: 1 }
67
+ ],
68
+ category: 2
69
+ }
70
+ ];
71
+
72
+ const mockActivities: ActivityItem[] = [
73
+ {
74
+ id: '1',
75
+ type: 'user',
76
+ message: 'New user registered: john.doe@example.com',
77
+ timestamp: new Date(Date.now() - 5 * 60 * 1000),
78
+ user: 'System'
79
+ },
80
+ {
81
+ id: '2',
82
+ type: 'security',
83
+ message: 'Failed login attempt detected',
84
+ timestamp: new Date(Date.now() - 15 * 60 * 1000),
85
+ user: 'Security Monitor'
86
+ },
87
+ {
88
+ id: '3',
89
+ type: 'content',
90
+ message: 'Blog post published: "Getting Started Guide"',
91
+ timestamp: new Date(Date.now() - 30 * 60 * 1000),
92
+ user: 'content.manager'
93
+ },
94
+ {
95
+ id: '4',
96
+ type: 'system',
97
+ message: 'Database backup completed successfully',
98
+ timestamp: new Date(Date.now() - 60 * 60 * 1000),
99
+ user: 'System'
100
+ },
101
+ {
102
+ id: '5',
103
+ type: 'user',
104
+ message: 'User updated profile: sarah.wilson@example.com',
105
+ timestamp: new Date(Date.now() - 90 * 60 * 1000),
106
+ user: 'sarah.wilson'
107
+ },
108
+ {
109
+ id: '6',
110
+ type: 'error',
111
+ message: 'Payment processing error for order #12847',
112
+ timestamp: new Date(Date.now() - 120 * 60 * 1000),
113
+ user: 'Payment System'
114
+ }
115
+ ];
116
+
117
+ const mockAlerts: AlertItem[] = [
118
+ {
119
+ id: '1',
120
+ severity: 'critical',
121
+ title: 'High CPU Usage',
122
+ description: 'Server CPU usage has exceeded 90% for the last 5 minutes',
123
+ timestamp: new Date(Date.now() - 10 * 60 * 1000),
124
+ onResolve: () => console.log('Resolving CPU alert')
125
+ },
126
+ {
127
+ id: '2',
128
+ severity: 'medium',
129
+ title: 'Low Disk Space',
130
+ description: 'Database server disk usage is at 85%',
131
+ timestamp: new Date(Date.now() - 30 * 60 * 1000),
132
+ onResolve: () => console.log('Resolving disk space alert')
133
+ },
134
+ {
135
+ id: '3',
136
+ severity: 'low',
137
+ title: 'SSL Certificate Expiring',
138
+ description: 'SSL certificate will expire in 30 days',
139
+ timestamp: new Date(Date.now() - 60 * 60 * 1000),
140
+ onResolve: () => console.log('Resolving SSL alert')
141
+ }
142
+ ];
143
+
144
+ export const AdminDashboardShowcase: React.FC = () => {
145
+ const handleRefresh = () => {
146
+ console.log('Refreshing dashboard data...');
147
+ };
148
+
149
+ return (
150
+ <AdminDashboardTemplate
151
+ title="System Overview"
152
+ description="Monitor key metrics, system health, and recent activity"
153
+ metrics={mockMetrics}
154
+ charts={mockCharts}
155
+ activities={mockActivities}
156
+ alerts={mockAlerts}
157
+ onRefresh={handleRefresh}
158
+ lastUpdated={new Date()}
159
+ actions={
160
+ <div className="flex gap-2">
161
+ <button className="px-3 py-2 text-sm border border-input rounded-md hover:bg-muted">
162
+ Export Report
163
+ </button>
164
+ <button className="px-3 py-2 text-sm bg-category-1 text-white rounded-md hover:bg-category-1/90">
165
+ View Analytics
166
+ </button>
167
+ </div>
168
+ }
169
+ />
170
+ );
171
+ };