@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.
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -0
- package/package.json +5 -3
- package/src/App.css +42 -0
- package/src/App.tsx +54 -0
- package/src/__tests__/README.md +221 -0
- package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
- package/src/__tests__/atoms/ui/button.test.tsx +68 -0
- package/src/__tests__/atoms/utils/simple.test.ts +18 -0
- package/src/__tests__/atoms/utils/utils.test.ts +77 -0
- package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
- package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
- package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
- package/src/__tests__/setup.ts +51 -0
- package/src/__tests__/utils.tsx +123 -0
- package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
- package/src/atoms/composed/Accordion/index.ts +1 -0
- package/src/atoms/composed/Alert/Alert.tsx +132 -0
- package/src/atoms/composed/Alert/index.ts +1 -0
- package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
- package/src/atoms/composed/Breadcrumb/index.ts +1 -0
- package/src/atoms/composed/Chart/Chart.tsx +425 -0
- package/src/atoms/composed/Chart/index.ts +2 -0
- package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
- package/src/atoms/composed/ColorSwatch/index.ts +1 -0
- package/src/atoms/composed/DarkModeToggle.tsx +66 -0
- package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
- package/src/atoms/composed/DataBadge/index.ts +1 -0
- package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
- package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
- package/src/atoms/composed/DataTable/index.ts +2 -0
- package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
- package/src/atoms/composed/DateTimePicker/index.ts +2 -0
- package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
- package/src/atoms/composed/DetailedCard/index.ts +2 -0
- package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
- package/src/atoms/composed/EmptyState/index.ts +1 -0
- package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
- package/src/atoms/composed/FileUpload/index.ts +2 -0
- package/src/atoms/composed/FormField/FormField.tsx +92 -0
- package/src/atoms/composed/FormField/index.ts +1 -0
- package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
- package/src/atoms/composed/GlobalSearch/index.ts +1 -0
- package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
- package/src/atoms/composed/IconBadge/index.ts +2 -0
- package/src/atoms/composed/Modal/Modal.tsx +223 -0
- package/src/atoms/composed/Modal/index.ts +2 -0
- package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
- package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
- package/src/atoms/composed/ProgressBar/index.ts +1 -0
- package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
- package/src/atoms/composed/StatCard/index.ts +1 -0
- package/src/atoms/composed/StyleGuide.tsx +717 -0
- package/src/atoms/composed/Toast/Toast.tsx +219 -0
- package/src/atoms/composed/Toast/index.ts +1 -0
- package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
- package/src/atoms/composed/Tooltip/index.ts +1 -0
- package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
- package/src/atoms/composed/UserAvatar/index.ts +1 -0
- package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
- package/src/atoms/composed/UserMenu/index.ts +1 -0
- package/src/atoms/composed/index.ts +29 -0
- package/src/atoms/hooks/useApi.ts +80 -0
- package/src/atoms/hooks/useHealth.ts +17 -0
- package/src/atoms/index.ts +13 -0
- package/src/atoms/services/api/client.ts +134 -0
- package/src/atoms/services/auth-service.ts +248 -0
- package/src/atoms/services/health.ts +15 -0
- package/src/atoms/services/index.ts +3 -0
- package/src/atoms/shared/config/constants.ts +17 -0
- package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
- package/src/atoms/shared/config/environment.ts +10 -0
- package/src/atoms/shared/index.ts +4 -0
- package/src/atoms/shared/styles/color-palettes.css +566 -0
- package/src/atoms/types/auth.ts +62 -0
- package/src/atoms/types/generated.ts +1469 -0
- package/src/atoms/types/index.ts +4 -0
- package/src/atoms/types/loading.ts +28 -0
- package/src/atoms/ui/Badge.tsx +30 -0
- package/src/atoms/ui/ErrorBoundary.tsx +59 -0
- package/src/atoms/ui/Select.tsx +53 -0
- package/src/atoms/ui/Switch.tsx +42 -0
- package/src/atoms/ui/Tabs.tsx +118 -0
- package/src/atoms/ui/avatar.tsx +48 -0
- package/src/atoms/ui/button.tsx +70 -0
- package/src/atoms/ui/card.tsx +76 -0
- package/src/atoms/ui/dropdown-menu.tsx +199 -0
- package/src/atoms/ui/index.ts +39 -0
- package/src/atoms/ui/input.tsx +23 -0
- package/src/atoms/ui/label.tsx +23 -0
- package/src/atoms/ui/skeleton.tsx +13 -0
- package/src/atoms/ui/spinner.tsx +49 -0
- package/src/atoms/ui/table.tsx +116 -0
- package/src/atoms/utils/animations.ts +135 -0
- package/src/atoms/utils/tooltip-helpers.ts +140 -0
- package/src/atoms/utils/utils.ts +9 -0
- package/src/features/auth/components/LoginForm.tsx +168 -0
- package/src/features/auth/components/LogoutButton.tsx +19 -0
- package/src/features/auth/components/ProtectedRoute.tsx +60 -0
- package/src/features/auth/components/index.ts +4 -0
- package/src/features/auth/hooks/index.ts +2 -0
- package/src/features/auth/hooks/useAuth.tsx +205 -0
- package/src/features/auth/hooks/usePermissions.ts +35 -0
- package/src/features/auth/index.ts +2 -0
- package/src/features/index.ts +2 -0
- package/src/index.css +704 -0
- package/src/index.ts +13 -0
- package/src/main.tsx +48 -0
- package/src/molecules/.gitkeep +0 -0
- package/src/molecules/forms/FormGroup.tsx +75 -0
- package/src/molecules/forms/SearchInput.tsx +259 -0
- package/src/molecules/forms/index.ts +4 -0
- package/src/molecules/index.ts +4 -0
- package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
- package/src/molecules/layout/AppHeader/index.ts +1 -0
- package/src/molecules/layout/AppLayout.tsx +29 -0
- package/src/molecules/layout/PageTemplate.tsx +87 -0
- package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
- package/src/molecules/layout/SectionHeader/index.ts +1 -0
- package/src/molecules/layout/ShowcaseSection.tsx +57 -0
- package/src/molecules/layout/Sidebar.tsx +144 -0
- package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
- package/src/molecules/layout/SidebarButton/index.ts +1 -0
- package/src/molecules/layout/SidebarContext.tsx +31 -0
- package/src/molecules/layout/index.ts +7 -0
- package/src/molecules/navigation/NavMenu.tsx +188 -0
- package/src/molecules/navigation/Pagination.tsx +172 -0
- package/src/molecules/navigation/index.ts +4 -0
- package/src/organisms/index.ts +5 -0
- package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
- package/src/organisms/showcase/index.ts +1 -0
- package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
- package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +171 -0
- package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
- package/src/pages/AdminShowcase/index.tsx +3 -0
- package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
- package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
- package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
- package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
- package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
- package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
- package/src/pages/ComponentShowcase/index.tsx +188 -0
- package/src/pages/index.ts +2 -0
- package/src/templates/AuthTemplate.tsx +216 -0
- package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
- package/src/templates/DashboardTemplate.tsx +232 -0
- package/src/templates/DataTemplate.tsx +319 -0
- package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
- package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
- package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
- package/src/templates/admin/index.ts +29 -0
- package/src/templates/factory.tsx +169 -0
- package/src/templates/index.ts +37 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { cn } from '../../atoms/utils/utils';
|
|
3
|
+
import { StatCard } from '../../atoms/composed/StatCard';
|
|
4
|
+
import { Chart } from '../../atoms/composed/Chart';
|
|
5
|
+
import { Card } from '../../atoms/ui/card';
|
|
6
|
+
import { DashboardGrid } from '../DashboardTemplate';
|
|
7
|
+
import { DataBadge } from '../../atoms/composed/DataBadge';
|
|
8
|
+
import { IconBadge } from '../../atoms/composed/IconBadge';
|
|
9
|
+
import { Button } from '../../atoms/ui/button';
|
|
10
|
+
import { getContainerHeightClass } from '../../atoms/shared/config/dashboard-sizes';
|
|
11
|
+
import { Activity, AlertTriangle, TrendingUp, Users, Settings, RefreshCw } from 'lucide-react';
|
|
12
|
+
|
|
13
|
+
export interface MetricCard {
|
|
14
|
+
/** Metric title */
|
|
15
|
+
title: string;
|
|
16
|
+
/** Main value to display */
|
|
17
|
+
value: string | number;
|
|
18
|
+
/** Change percentage */
|
|
19
|
+
change?: number;
|
|
20
|
+
/** Change period description */
|
|
21
|
+
changePeriod?: string;
|
|
22
|
+
/** Metric category for styling */
|
|
23
|
+
category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
24
|
+
/** Custom icon */
|
|
25
|
+
icon?: React.ReactNode;
|
|
26
|
+
/** Click handler */
|
|
27
|
+
onClick?: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ChartConfig {
|
|
31
|
+
/** Chart title */
|
|
32
|
+
title: string;
|
|
33
|
+
/** Chart type */
|
|
34
|
+
type: 'line' | 'bar' | 'pie' | 'area';
|
|
35
|
+
/** Chart data */
|
|
36
|
+
data: Array<{ label: string; value: number; category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 }>;
|
|
37
|
+
/** Chart configuration */
|
|
38
|
+
config?: Record<string, unknown>;
|
|
39
|
+
/** Chart category for styling */
|
|
40
|
+
category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ActivityItem {
|
|
44
|
+
/** Activity ID */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Activity type */
|
|
47
|
+
type: 'user' | 'system' | 'security' | 'content' | 'error';
|
|
48
|
+
/** Activity message */
|
|
49
|
+
message: string;
|
|
50
|
+
/** Activity timestamp */
|
|
51
|
+
timestamp: Date;
|
|
52
|
+
/** User who performed the activity */
|
|
53
|
+
user?: string;
|
|
54
|
+
/** Additional metadata */
|
|
55
|
+
metadata?: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AlertItem {
|
|
59
|
+
/** Alert ID */
|
|
60
|
+
id: string;
|
|
61
|
+
/** Alert severity */
|
|
62
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
63
|
+
/** Alert title */
|
|
64
|
+
title: string;
|
|
65
|
+
/** Alert description */
|
|
66
|
+
description: string;
|
|
67
|
+
/** Alert timestamp */
|
|
68
|
+
timestamp: Date;
|
|
69
|
+
/** Whether alert is resolved */
|
|
70
|
+
resolved?: boolean;
|
|
71
|
+
/** Action to resolve alert */
|
|
72
|
+
onResolve?: () => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface AdminDashboardTemplateProps {
|
|
76
|
+
/** Dashboard title */
|
|
77
|
+
title?: string;
|
|
78
|
+
/** Dashboard description */
|
|
79
|
+
description?: string;
|
|
80
|
+
/** Key metrics to display */
|
|
81
|
+
metrics: MetricCard[];
|
|
82
|
+
/** Charts configuration */
|
|
83
|
+
charts?: ChartConfig[];
|
|
84
|
+
/** Recent activities */
|
|
85
|
+
activities?: ActivityItem[];
|
|
86
|
+
/** System alerts */
|
|
87
|
+
alerts?: AlertItem[];
|
|
88
|
+
/** Additional actions in header */
|
|
89
|
+
actions?: React.ReactNode;
|
|
90
|
+
/** Additional CSS classes */
|
|
91
|
+
className?: string;
|
|
92
|
+
/** Whether data is loading */
|
|
93
|
+
isLoading?: boolean;
|
|
94
|
+
/** Refresh handler */
|
|
95
|
+
onRefresh?: () => void;
|
|
96
|
+
/** Last updated timestamp */
|
|
97
|
+
lastUpdated?: Date;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const AdminDashboardTemplate: React.FC<AdminDashboardTemplateProps> = ({
|
|
101
|
+
title = "Admin Dashboard",
|
|
102
|
+
description = "System overview and key metrics",
|
|
103
|
+
metrics,
|
|
104
|
+
charts = [],
|
|
105
|
+
activities = [],
|
|
106
|
+
alerts = [],
|
|
107
|
+
actions,
|
|
108
|
+
className,
|
|
109
|
+
isLoading = false,
|
|
110
|
+
onRefresh,
|
|
111
|
+
lastUpdated
|
|
112
|
+
}) => {
|
|
113
|
+
const unreadAlerts = alerts.filter(alert => !alert.resolved);
|
|
114
|
+
const criticalAlerts = unreadAlerts.filter(alert => alert.severity === 'critical');
|
|
115
|
+
|
|
116
|
+
const getActivityIcon = (type: ActivityItem['type']) => {
|
|
117
|
+
switch (type) {
|
|
118
|
+
case 'user': return <Users className="w-4 h-4" />;
|
|
119
|
+
case 'system': return <Settings className="w-4 h-4" />;
|
|
120
|
+
case 'security': return <AlertTriangle className="w-4 h-4" />;
|
|
121
|
+
case 'content': return <Activity className="w-4 h-4" />;
|
|
122
|
+
case 'error': return <AlertTriangle className="w-4 h-4" />;
|
|
123
|
+
default: return <Activity className="w-4 h-4" />;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const getActivityColor = (type: ActivityItem['type']) => {
|
|
128
|
+
switch (type) {
|
|
129
|
+
case 'user': return 'text-category-1';
|
|
130
|
+
case 'system': return 'text-category-2';
|
|
131
|
+
case 'security': return 'text-category-3';
|
|
132
|
+
case 'content': return 'text-category-5';
|
|
133
|
+
case 'error': return 'text-category-8';
|
|
134
|
+
default: return 'text-muted-foreground';
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const getAlertSeverityCategory = (severity: AlertItem['severity']): 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 => {
|
|
139
|
+
switch (severity) {
|
|
140
|
+
case 'low': return 6;
|
|
141
|
+
case 'medium': return 7;
|
|
142
|
+
case 'high': return 3;
|
|
143
|
+
case 'critical': return 8;
|
|
144
|
+
default: return 2;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className={cn('flex flex-col min-h-0 flex-1', className)}>
|
|
150
|
+
{/* Header Section */}
|
|
151
|
+
<div className="bg-gradient-to-br from-muted/50 via-muted/30 to-background border-b border-border/50">
|
|
152
|
+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12">
|
|
153
|
+
<div className="flex items-start justify-between">
|
|
154
|
+
<div className="flex items-center space-x-4">
|
|
155
|
+
<IconBadge
|
|
156
|
+
variant="category"
|
|
157
|
+
category={1}
|
|
158
|
+
size="lg"
|
|
159
|
+
icon={<Activity className="w-5 h-5" />}
|
|
160
|
+
tooltip="Dashboard Activity Overview"
|
|
161
|
+
/>
|
|
162
|
+
<div>
|
|
163
|
+
<h1 className="text-4xl font-bold text-foreground">{title}</h1>
|
|
164
|
+
<p className="text-lg text-muted-foreground mt-2">{description}</p>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div className="ml-6 flex-shrink-0">
|
|
169
|
+
{/* Action buttons */}
|
|
170
|
+
<div className="flex items-center gap-2 mb-2">
|
|
171
|
+
{onRefresh && (
|
|
172
|
+
<Button
|
|
173
|
+
variant="outline"
|
|
174
|
+
onClick={onRefresh}
|
|
175
|
+
disabled={isLoading}
|
|
176
|
+
tooltip={isLoading ? "Refreshing..." : "Refresh data"}
|
|
177
|
+
>
|
|
178
|
+
<RefreshCw className={cn("w-4 h-4 mr-2", isLoading && "animate-spin")} />
|
|
179
|
+
Refresh
|
|
180
|
+
</Button>
|
|
181
|
+
)}
|
|
182
|
+
{actions}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{/* Status indicators positioned under buttons */}
|
|
186
|
+
<div className="flex items-center gap-4 justify-end">
|
|
187
|
+
{criticalAlerts.length > 0 && (
|
|
188
|
+
<DataBadge
|
|
189
|
+
variant="status"
|
|
190
|
+
status="error"
|
|
191
|
+
size="sm"
|
|
192
|
+
>
|
|
193
|
+
<AlertTriangle className="w-3 h-3 mr-1" />
|
|
194
|
+
{criticalAlerts.length} Critical Alert{criticalAlerts.length !== 1 ? 's' : ''}
|
|
195
|
+
</DataBadge>
|
|
196
|
+
)}
|
|
197
|
+
|
|
198
|
+
{lastUpdated && (
|
|
199
|
+
<span className="text-sm text-muted-foreground">
|
|
200
|
+
Last updated: {lastUpdated.toLocaleTimeString()}
|
|
201
|
+
</span>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
{/* Main Content */}
|
|
210
|
+
<div className="flex-1 min-h-0 overflow-auto">
|
|
211
|
+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8 space-y-8">
|
|
212
|
+
|
|
213
|
+
{/* Key Metrics Grid */}
|
|
214
|
+
<DashboardGrid columns={4} gap="md">
|
|
215
|
+
{metrics.map((metric, index) => (
|
|
216
|
+
<StatCard
|
|
217
|
+
key={index}
|
|
218
|
+
title={metric.title}
|
|
219
|
+
value={metric.value}
|
|
220
|
+
trend={metric.change ? { value: metric.change, label: metric.changePeriod } : undefined}
|
|
221
|
+
category={metric.category || 1}
|
|
222
|
+
icon={metric.icon || <TrendingUp className="w-5 h-5" />}
|
|
223
|
+
onClick={metric.onClick}
|
|
224
|
+
className="cursor-pointer transition-shadow"
|
|
225
|
+
/>
|
|
226
|
+
))}
|
|
227
|
+
</DashboardGrid>
|
|
228
|
+
|
|
229
|
+
{/* Charts and Activities Row */}
|
|
230
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
231
|
+
|
|
232
|
+
{/* Charts Section */}
|
|
233
|
+
<div className="lg:col-span-2 space-y-6">
|
|
234
|
+
{charts.map((chart, index) => (
|
|
235
|
+
<Card key={index} className="p-6 overflow-hidden">
|
|
236
|
+
<div className="mb-4">
|
|
237
|
+
<h3 className="text-lg font-semibold text-foreground">
|
|
238
|
+
{chart.title}
|
|
239
|
+
</h3>
|
|
240
|
+
</div>
|
|
241
|
+
<div className={getContainerHeightClass('container-small-chart')}>
|
|
242
|
+
<Chart
|
|
243
|
+
title={chart.title}
|
|
244
|
+
type={chart.type}
|
|
245
|
+
data={chart.data}
|
|
246
|
+
category={chart.category}
|
|
247
|
+
height="small"
|
|
248
|
+
variant="minimal"
|
|
249
|
+
noWrapper={true}
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
252
|
+
</Card>
|
|
253
|
+
))}
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
{/* Right Sidebar */}
|
|
257
|
+
<div className="space-y-6">
|
|
258
|
+
|
|
259
|
+
{/* System Alerts */}
|
|
260
|
+
{unreadAlerts.length > 0 && (
|
|
261
|
+
<Card className="p-4">
|
|
262
|
+
<div className="mb-4">
|
|
263
|
+
<h3 className="text-lg font-semibold text-foreground flex items-center">
|
|
264
|
+
<AlertTriangle className="w-5 h-5 mr-2 text-category-8" />
|
|
265
|
+
System Alerts
|
|
266
|
+
</h3>
|
|
267
|
+
</div>
|
|
268
|
+
<div className="space-y-3">
|
|
269
|
+
{unreadAlerts.slice(0, 5).map((alert) => (
|
|
270
|
+
<div
|
|
271
|
+
key={alert.id}
|
|
272
|
+
className="flex items-start space-x-3 p-3 rounded-md border border-border"
|
|
273
|
+
>
|
|
274
|
+
<DataBadge
|
|
275
|
+
variant="category"
|
|
276
|
+
category={getAlertSeverityCategory(alert.severity)}
|
|
277
|
+
size="sm"
|
|
278
|
+
className="mt-0.5"
|
|
279
|
+
>
|
|
280
|
+
{alert.severity}
|
|
281
|
+
</DataBadge>
|
|
282
|
+
<div className="flex-1 min-w-0">
|
|
283
|
+
<p className="text-sm font-medium text-foreground">
|
|
284
|
+
{alert.title}
|
|
285
|
+
</p>
|
|
286
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
287
|
+
{alert.description}
|
|
288
|
+
</p>
|
|
289
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
290
|
+
{alert.timestamp.toLocaleString()}
|
|
291
|
+
</p>
|
|
292
|
+
{alert.onResolve && (
|
|
293
|
+
<Button
|
|
294
|
+
size="sm"
|
|
295
|
+
variant="outline"
|
|
296
|
+
className="mt-2"
|
|
297
|
+
onClick={alert.onResolve}
|
|
298
|
+
>
|
|
299
|
+
Resolve
|
|
300
|
+
</Button>
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
))}
|
|
305
|
+
</div>
|
|
306
|
+
</Card>
|
|
307
|
+
)}
|
|
308
|
+
|
|
309
|
+
{/* Recent Activity */}
|
|
310
|
+
<Card className="p-4">
|
|
311
|
+
<div className="mb-4">
|
|
312
|
+
<h3 className="text-lg font-semibold text-foreground flex items-center">
|
|
313
|
+
<Activity className="w-5 h-5 mr-2 text-category-2" />
|
|
314
|
+
Recent Activity
|
|
315
|
+
</h3>
|
|
316
|
+
</div>
|
|
317
|
+
<div className="space-y-3">
|
|
318
|
+
{activities.slice(0, 8).map((activity) => (
|
|
319
|
+
<div key={activity.id} className="flex items-start space-x-3">
|
|
320
|
+
<div className={cn(
|
|
321
|
+
"mt-1 flex-shrink-0",
|
|
322
|
+
getActivityColor(activity.type)
|
|
323
|
+
)}>
|
|
324
|
+
{getActivityIcon(activity.type)}
|
|
325
|
+
</div>
|
|
326
|
+
<div className="flex-1 min-w-0">
|
|
327
|
+
<p className="text-sm text-foreground">
|
|
328
|
+
{activity.message}
|
|
329
|
+
</p>
|
|
330
|
+
<div className="flex items-center gap-2 mt-1">
|
|
331
|
+
{activity.user && (
|
|
332
|
+
<span className="text-xs text-muted-foreground">
|
|
333
|
+
by {activity.user}
|
|
334
|
+
</span>
|
|
335
|
+
)}
|
|
336
|
+
<span className="text-xs text-muted-foreground">
|
|
337
|
+
{activity.timestamp.toLocaleString()}
|
|
338
|
+
</span>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
))}
|
|
343
|
+
</div>
|
|
344
|
+
</Card>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
};
|