@moontra/moonui-pro 2.4.5 → 2.5.0
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.d.ts +127 -25
- package/dist/index.mjs +2023 -186
- package/package.json +4 -1
- package/src/components/advanced-chart/index.tsx +11 -1
- package/src/components/dashboard/dashboard-grid.tsx +327 -0
- package/src/components/dashboard/demo.tsx +308 -0
- package/src/components/dashboard/index.tsx +683 -275
- package/src/components/dashboard/time-range-picker.tsx +267 -0
- package/src/components/dashboard/types.ts +222 -0
- package/src/components/dashboard/widgets/activity-feed.tsx +344 -0
- package/src/components/dashboard/widgets/chart-widget.tsx +418 -0
- package/src/components/dashboard/widgets/metric-card.tsx +343 -0
- package/src/components/index.ts +2 -2
- package/src/components/ui/calendar.tsx +65 -0
- package/src/components/ui/index.ts +12 -0
- package/src/components/ui/scroll-area.tsx +47 -0
- package/src/components/ui/sheet.tsx +139 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
|
84
84
|
"@radix-ui/react-label": "^2.1.7",
|
|
85
85
|
"@radix-ui/react-popover": "^1.1.14",
|
|
86
|
+
"@radix-ui/react-scroll-area": "^1.2.9",
|
|
86
87
|
"@radix-ui/react-select": "^2.2.5",
|
|
87
88
|
"@radix-ui/react-separator": "^1.1.7",
|
|
88
89
|
"@radix-ui/react-slot": "^1.2.3",
|
|
@@ -96,7 +97,9 @@
|
|
|
96
97
|
"date-fns": "^3.6.0",
|
|
97
98
|
"framer-motion": "^11.11.17",
|
|
98
99
|
"lucide-react": "^0.525.0",
|
|
100
|
+
"react-day-picker": "^9.8.0",
|
|
99
101
|
"react-dropzone": "^14.3.5",
|
|
102
|
+
"react-grid-layout": "^1.5.2",
|
|
100
103
|
"react-hook-form": "^7.53.2",
|
|
101
104
|
"react-intersection-observer": "^9.13.1",
|
|
102
105
|
"react-virtualized-auto-sizer": "^1.0.24",
|
|
@@ -340,15 +340,20 @@ export function AdvancedChart({
|
|
|
340
340
|
|
|
341
341
|
// Calculate trend for the first series
|
|
342
342
|
const trend = React.useMemo(() => {
|
|
343
|
-
if (!data.length || !series.length) return null
|
|
343
|
+
if (!data || !Array.isArray(data) || !data.length || !series || !Array.isArray(series) || !series.length) return null
|
|
344
344
|
|
|
345
345
|
const firstSeries = series[0]
|
|
346
|
+
if (!firstSeries || !firstSeries.dataKey) return null
|
|
347
|
+
|
|
346
348
|
const values = data.map(d => Number(d[firstSeries.dataKey])).filter(v => !isNaN(v))
|
|
347
349
|
|
|
348
350
|
if (values.length < 2) return null
|
|
349
351
|
|
|
350
352
|
const first = values[0]
|
|
351
353
|
const last = values[values.length - 1]
|
|
354
|
+
|
|
355
|
+
if (first === 0) return null // Avoid division by zero
|
|
356
|
+
|
|
352
357
|
const change = ((last - first) / first) * 100
|
|
353
358
|
|
|
354
359
|
return {
|
|
@@ -411,6 +416,11 @@ export function AdvancedChart({
|
|
|
411
416
|
)
|
|
412
417
|
|
|
413
418
|
const renderChart = () => {
|
|
419
|
+
// Güvenlik kontrolleri
|
|
420
|
+
if (!data || !Array.isArray(data) || !series || !Array.isArray(series)) {
|
|
421
|
+
return <div className="flex items-center justify-center h-full text-muted-foreground">No data available</div>
|
|
422
|
+
}
|
|
423
|
+
|
|
414
424
|
const commonProps = {
|
|
415
425
|
data,
|
|
416
426
|
width: typeof width === 'string' ? undefined : width,
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { motion, AnimatePresence } from 'framer-motion'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
import { Widget } from './types'
|
|
7
|
+
import { MetricCard } from './widgets/metric-card'
|
|
8
|
+
import { ChartWidget } from './widgets/chart-widget'
|
|
9
|
+
import { ActivityFeed } from './widgets/activity-feed'
|
|
10
|
+
import GridLayout, { Layout, Layouts } from 'react-grid-layout'
|
|
11
|
+
import 'react-grid-layout/css/styles.css'
|
|
12
|
+
import 'react-resizable/css/styles.css'
|
|
13
|
+
import {
|
|
14
|
+
Grip,
|
|
15
|
+
X,
|
|
16
|
+
Maximize2,
|
|
17
|
+
Minimize2,
|
|
18
|
+
Lock,
|
|
19
|
+
Unlock,
|
|
20
|
+
Settings
|
|
21
|
+
} from 'lucide-react'
|
|
22
|
+
import { Button } from '../ui/button'
|
|
23
|
+
|
|
24
|
+
interface DashboardGridProps {
|
|
25
|
+
widgets: Widget[]
|
|
26
|
+
onLayoutChange?: (layout: Layout[]) => void
|
|
27
|
+
onWidgetRemove?: (widgetId: string) => void
|
|
28
|
+
onWidgetAction?: (widgetId: string, action: string, data?: any) => void
|
|
29
|
+
editMode?: boolean
|
|
30
|
+
className?: string
|
|
31
|
+
glassmorphism?: boolean
|
|
32
|
+
breakpoints?: { lg: number; md: number; sm: number; xs: number; xxs: number }
|
|
33
|
+
cols?: { lg: number; md: number; sm: number; xs: number; xxs: number }
|
|
34
|
+
rowHeight?: number
|
|
35
|
+
margin?: [number, number]
|
|
36
|
+
containerPadding?: [number, number]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function DashboardGrid({
|
|
40
|
+
widgets,
|
|
41
|
+
onLayoutChange,
|
|
42
|
+
onWidgetRemove,
|
|
43
|
+
onWidgetAction,
|
|
44
|
+
editMode = false,
|
|
45
|
+
className,
|
|
46
|
+
glassmorphism = false,
|
|
47
|
+
breakpoints = { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 },
|
|
48
|
+
cols = { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
|
|
49
|
+
rowHeight = 60,
|
|
50
|
+
margin = [16, 16],
|
|
51
|
+
containerPadding = [0, 0]
|
|
52
|
+
}: DashboardGridProps) {
|
|
53
|
+
const [layouts, setLayouts] = React.useState<Layouts>({})
|
|
54
|
+
const [currentBreakpoint, setCurrentBreakpoint] = React.useState('lg')
|
|
55
|
+
const [lockedWidgets, setLockedWidgets] = React.useState<Set<string>>(new Set())
|
|
56
|
+
const [fullscreenWidget, setFullscreenWidget] = React.useState<string | null>(null)
|
|
57
|
+
|
|
58
|
+
// Layout oluştur
|
|
59
|
+
const generateLayout = (): Layout[] => {
|
|
60
|
+
return widgets.map((widget) => ({
|
|
61
|
+
i: widget.id,
|
|
62
|
+
x: widget.position.x,
|
|
63
|
+
y: widget.position.y,
|
|
64
|
+
w: widget.size.w,
|
|
65
|
+
h: widget.size.h,
|
|
66
|
+
minW: widget.size.minW || 2,
|
|
67
|
+
maxW: widget.size.maxW || 12,
|
|
68
|
+
minH: widget.size.minH || 2,
|
|
69
|
+
maxH: widget.size.maxH || 8,
|
|
70
|
+
isDraggable: editMode && !lockedWidgets.has(widget.id) && widget.permissions?.canMove !== false,
|
|
71
|
+
isResizable: editMode && !lockedWidgets.has(widget.id) && widget.permissions?.canResize !== false,
|
|
72
|
+
}))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Widget kilit durumunu değiştir
|
|
76
|
+
const toggleWidgetLock = (widgetId: string) => {
|
|
77
|
+
setLockedWidgets(prev => {
|
|
78
|
+
const newSet = new Set(prev)
|
|
79
|
+
if (newSet.has(widgetId)) {
|
|
80
|
+
newSet.delete(widgetId)
|
|
81
|
+
} else {
|
|
82
|
+
newSet.add(widgetId)
|
|
83
|
+
}
|
|
84
|
+
return newSet
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Widget'ı tam ekran yap
|
|
89
|
+
const toggleFullscreen = (widgetId: string) => {
|
|
90
|
+
if (fullscreenWidget === widgetId) {
|
|
91
|
+
setFullscreenWidget(null)
|
|
92
|
+
} else {
|
|
93
|
+
setFullscreenWidget(widgetId)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Widget render
|
|
98
|
+
const renderWidget = (widget: Widget) => {
|
|
99
|
+
const isLocked = lockedWidgets.has(widget.id)
|
|
100
|
+
const isFullscreen = fullscreenWidget === widget.id
|
|
101
|
+
|
|
102
|
+
const widgetContent = () => {
|
|
103
|
+
switch (widget.type) {
|
|
104
|
+
case 'metric':
|
|
105
|
+
return (
|
|
106
|
+
<MetricCard
|
|
107
|
+
data={widget.data}
|
|
108
|
+
onAction={(action) => onWidgetAction?.(widget.id, action)}
|
|
109
|
+
glassmorphism={glassmorphism}
|
|
110
|
+
/>
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
case 'chart':
|
|
114
|
+
return (
|
|
115
|
+
<ChartWidget
|
|
116
|
+
id={widget.id}
|
|
117
|
+
title={widget.title}
|
|
118
|
+
description={widget.description}
|
|
119
|
+
data={widget.data}
|
|
120
|
+
loading={widget.loading}
|
|
121
|
+
glassmorphism={glassmorphism}
|
|
122
|
+
onAction={(action, data) => onWidgetAction?.(widget.id, action, data)}
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
case 'activity':
|
|
127
|
+
return (
|
|
128
|
+
<ActivityFeed
|
|
129
|
+
items={widget.data?.items || []}
|
|
130
|
+
title={widget.title}
|
|
131
|
+
loading={widget.loading}
|
|
132
|
+
glassmorphism={glassmorphism}
|
|
133
|
+
realtime={widget.data?.realtime}
|
|
134
|
+
onAction={(action, data) => onWidgetAction?.(widget.id, action, data)}
|
|
135
|
+
/>
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
default:
|
|
139
|
+
return (
|
|
140
|
+
<div className="flex items-center justify-center h-full text-muted-foreground">
|
|
141
|
+
Widget type not supported: {widget.type}
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<motion.div
|
|
149
|
+
key={widget.id}
|
|
150
|
+
className={cn(
|
|
151
|
+
"relative group h-full",
|
|
152
|
+
isFullscreen && "!fixed !inset-4 !z-50 !h-auto !w-auto"
|
|
153
|
+
)}
|
|
154
|
+
layout={!isFullscreen}
|
|
155
|
+
transition={{ duration: 0.2 }}
|
|
156
|
+
>
|
|
157
|
+
{/* Widget içeriği */}
|
|
158
|
+
<div className="h-full">
|
|
159
|
+
{widgetContent()}
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{/* Edit mode kontrolleri */}
|
|
163
|
+
{editMode && !isFullscreen && (
|
|
164
|
+
<motion.div
|
|
165
|
+
initial={{ opacity: 0 }}
|
|
166
|
+
animate={{ opacity: 1 }}
|
|
167
|
+
className="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity"
|
|
168
|
+
>
|
|
169
|
+
<div className={cn(
|
|
170
|
+
"flex items-center gap-1 p-1 rounded-lg",
|
|
171
|
+
glassmorphism ? "bg-background/80 backdrop-blur-sm" : "bg-background/95",
|
|
172
|
+
"border shadow-sm"
|
|
173
|
+
)}>
|
|
174
|
+
{/* Drag handle */}
|
|
175
|
+
{!isLocked && (
|
|
176
|
+
<div className="drag-handle cursor-move p-1 hover:bg-muted rounded">
|
|
177
|
+
<Grip className="h-4 w-4" />
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
|
|
181
|
+
{/* Lock/Unlock */}
|
|
182
|
+
<Button
|
|
183
|
+
variant="ghost"
|
|
184
|
+
size="sm"
|
|
185
|
+
className="h-6 w-6 p-0"
|
|
186
|
+
onClick={() => toggleWidgetLock(widget.id)}
|
|
187
|
+
>
|
|
188
|
+
{isLocked ? (
|
|
189
|
+
<Lock className="h-3 w-3" />
|
|
190
|
+
) : (
|
|
191
|
+
<Unlock className="h-3 w-3" />
|
|
192
|
+
)}
|
|
193
|
+
</Button>
|
|
194
|
+
|
|
195
|
+
{/* Fullscreen */}
|
|
196
|
+
<Button
|
|
197
|
+
variant="ghost"
|
|
198
|
+
size="sm"
|
|
199
|
+
className="h-6 w-6 p-0"
|
|
200
|
+
onClick={() => toggleFullscreen(widget.id)}
|
|
201
|
+
>
|
|
202
|
+
<Maximize2 className="h-3 w-3" />
|
|
203
|
+
</Button>
|
|
204
|
+
|
|
205
|
+
{/* Settings */}
|
|
206
|
+
<Button
|
|
207
|
+
variant="ghost"
|
|
208
|
+
size="sm"
|
|
209
|
+
className="h-6 w-6 p-0"
|
|
210
|
+
onClick={() => onWidgetAction?.(widget.id, 'settings')}
|
|
211
|
+
>
|
|
212
|
+
<Settings className="h-3 w-3" />
|
|
213
|
+
</Button>
|
|
214
|
+
|
|
215
|
+
{/* Remove */}
|
|
216
|
+
{widget.permissions?.canDelete !== false && (
|
|
217
|
+
<Button
|
|
218
|
+
variant="ghost"
|
|
219
|
+
size="sm"
|
|
220
|
+
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
|
|
221
|
+
onClick={() => onWidgetRemove?.(widget.id)}
|
|
222
|
+
>
|
|
223
|
+
<X className="h-3 w-3" />
|
|
224
|
+
</Button>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
</motion.div>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
{/* Fullscreen kontrolleri */}
|
|
231
|
+
{isFullscreen && (
|
|
232
|
+
<motion.div
|
|
233
|
+
initial={{ opacity: 0, y: -10 }}
|
|
234
|
+
animate={{ opacity: 1, y: 0 }}
|
|
235
|
+
className="absolute top-4 right-4 z-10"
|
|
236
|
+
>
|
|
237
|
+
<Button
|
|
238
|
+
variant="outline"
|
|
239
|
+
size="sm"
|
|
240
|
+
onClick={() => toggleFullscreen(widget.id)}
|
|
241
|
+
>
|
|
242
|
+
<Minimize2 className="h-4 w-4 mr-2" />
|
|
243
|
+
Exit Fullscreen
|
|
244
|
+
</Button>
|
|
245
|
+
</motion.div>
|
|
246
|
+
)}
|
|
247
|
+
</motion.div>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<div className={cn("w-full", className)}>
|
|
253
|
+
<AnimatePresence>
|
|
254
|
+
{fullscreenWidget ? (
|
|
255
|
+
// Fullscreen widget
|
|
256
|
+
<motion.div
|
|
257
|
+
initial={{ opacity: 0 }}
|
|
258
|
+
animate={{ opacity: 1 }}
|
|
259
|
+
exit={{ opacity: 0 }}
|
|
260
|
+
className="fixed inset-0 z-40 bg-background"
|
|
261
|
+
>
|
|
262
|
+
{widgets
|
|
263
|
+
.filter(w => w.id === fullscreenWidget)
|
|
264
|
+
.map(renderWidget)}
|
|
265
|
+
</motion.div>
|
|
266
|
+
) : (
|
|
267
|
+
// Grid layout
|
|
268
|
+
<GridLayout
|
|
269
|
+
className="layout"
|
|
270
|
+
layout={generateLayout()}
|
|
271
|
+
cols={cols[currentBreakpoint as keyof typeof cols] || cols.lg}
|
|
272
|
+
rowHeight={rowHeight}
|
|
273
|
+
width={1200}
|
|
274
|
+
margin={margin}
|
|
275
|
+
containerPadding={containerPadding}
|
|
276
|
+
onLayoutChange={(layout) => {
|
|
277
|
+
onLayoutChange?.(layout)
|
|
278
|
+
}}
|
|
279
|
+
onBreakpointChange={(breakpoint) => {
|
|
280
|
+
setCurrentBreakpoint(breakpoint)
|
|
281
|
+
}}
|
|
282
|
+
draggableHandle=".drag-handle"
|
|
283
|
+
isDraggable={editMode}
|
|
284
|
+
isResizable={editMode}
|
|
285
|
+
compactType="vertical"
|
|
286
|
+
preventCollision={false}
|
|
287
|
+
>
|
|
288
|
+
{widgets.map(widget => (
|
|
289
|
+
<div key={widget.id}>
|
|
290
|
+
{renderWidget(widget)}
|
|
291
|
+
</div>
|
|
292
|
+
))}
|
|
293
|
+
</GridLayout>
|
|
294
|
+
)}
|
|
295
|
+
</AnimatePresence>
|
|
296
|
+
|
|
297
|
+
{/* Grid çizgileri (edit mode) */}
|
|
298
|
+
{editMode && !fullscreenWidget && (
|
|
299
|
+
<motion.div
|
|
300
|
+
initial={{ opacity: 0 }}
|
|
301
|
+
animate={{ opacity: 0.1 }}
|
|
302
|
+
className="absolute inset-0 pointer-events-none"
|
|
303
|
+
style={{
|
|
304
|
+
backgroundImage: `
|
|
305
|
+
repeating-linear-gradient(
|
|
306
|
+
0deg,
|
|
307
|
+
var(--border) 0px,
|
|
308
|
+
transparent 1px,
|
|
309
|
+
transparent ${rowHeight + margin[1]}px,
|
|
310
|
+
var(--border) ${rowHeight + margin[1]}px
|
|
311
|
+
),
|
|
312
|
+
repeating-linear-gradient(
|
|
313
|
+
90deg,
|
|
314
|
+
var(--border) 0px,
|
|
315
|
+
transparent 1px,
|
|
316
|
+
transparent ${100 / cols[currentBreakpoint as keyof typeof cols]}%,
|
|
317
|
+
var(--border) ${100 / cols[currentBreakpoint as keyof typeof cols]}%
|
|
318
|
+
)
|
|
319
|
+
`
|
|
320
|
+
}}
|
|
321
|
+
/>
|
|
322
|
+
)}
|
|
323
|
+
</div>
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export default DashboardGrid
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Dashboard } from './index'
|
|
5
|
+
import { Widget, MetricData, ChartData, ActivityItem } from './types'
|
|
6
|
+
import {
|
|
7
|
+
Users,
|
|
8
|
+
DollarSign,
|
|
9
|
+
ShoppingCart,
|
|
10
|
+
TrendingUp,
|
|
11
|
+
Package,
|
|
12
|
+
CreditCard,
|
|
13
|
+
Activity,
|
|
14
|
+
AlertCircle
|
|
15
|
+
} from 'lucide-react'
|
|
16
|
+
|
|
17
|
+
// Örnek metrik verileri
|
|
18
|
+
const sampleMetrics: MetricData[] = [
|
|
19
|
+
{
|
|
20
|
+
id: 'revenue',
|
|
21
|
+
title: 'Total Revenue',
|
|
22
|
+
value: 54234,
|
|
23
|
+
unit: '$',
|
|
24
|
+
change: { value: 12.5, type: 'increase', period: 'last month' },
|
|
25
|
+
icon: <DollarSign className="h-4 w-4" />,
|
|
26
|
+
color: 'success',
|
|
27
|
+
sparkline: [30, 40, 35, 50, 49, 60, 70, 91, 125],
|
|
28
|
+
target: 60000,
|
|
29
|
+
forecast: 58500
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'users',
|
|
33
|
+
title: 'Active Users',
|
|
34
|
+
value: 2453,
|
|
35
|
+
change: { value: 8.2, type: 'increase', period: 'last week' },
|
|
36
|
+
icon: <Users className="h-4 w-4" />,
|
|
37
|
+
color: 'primary',
|
|
38
|
+
sparkline: [200, 220, 210, 230, 225, 240, 254, 260, 245]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'orders',
|
|
42
|
+
title: 'New Orders',
|
|
43
|
+
value: 846,
|
|
44
|
+
change: { value: 3.1, type: 'decrease', period: 'yesterday' },
|
|
45
|
+
icon: <ShoppingCart className="h-4 w-4" />,
|
|
46
|
+
color: 'warning',
|
|
47
|
+
sparkline: [80, 85, 90, 88, 85, 82, 84, 86, 84]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'conversion',
|
|
51
|
+
title: 'Conversion Rate',
|
|
52
|
+
value: '3.24%',
|
|
53
|
+
change: { value: 0.5, type: 'increase', period: 'last month' },
|
|
54
|
+
icon: <TrendingUp className="h-4 w-4" />,
|
|
55
|
+
color: 'info',
|
|
56
|
+
sparkline: [2.8, 2.9, 3.0, 2.95, 3.1, 3.15, 3.2, 3.22, 3.24]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
// Örnek chart verileri
|
|
61
|
+
const revenueChartData: ChartData = {
|
|
62
|
+
type: 'area',
|
|
63
|
+
data: [
|
|
64
|
+
{ name: 'Jan', revenue: 4000, profit: 2400, expenses: 1600 },
|
|
65
|
+
{ name: 'Feb', revenue: 3000, profit: 1398, expenses: 1602 },
|
|
66
|
+
{ name: 'Mar', revenue: 5000, profit: 3200, expenses: 1800 },
|
|
67
|
+
{ name: 'Apr', revenue: 4500, profit: 2900, expenses: 1600 },
|
|
68
|
+
{ name: 'May', revenue: 6000, profit: 3800, expenses: 2200 },
|
|
69
|
+
{ name: 'Jun', revenue: 5500, profit: 3400, expenses: 2100 },
|
|
70
|
+
{ name: 'Jul', revenue: 7000, profit: 4200, expenses: 2800 }
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const salesByProductData: ChartData = {
|
|
75
|
+
type: 'pie',
|
|
76
|
+
data: [
|
|
77
|
+
{ name: 'Product A', value: 35 },
|
|
78
|
+
{ name: 'Product B', value: 25 },
|
|
79
|
+
{ name: 'Product C', value: 20 },
|
|
80
|
+
{ name: 'Product D', value: 15 },
|
|
81
|
+
{ name: 'Product E', value: 5 }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const performanceData: ChartData = {
|
|
86
|
+
type: 'radar',
|
|
87
|
+
data: [
|
|
88
|
+
{ subject: 'Sales', A: 120, B: 110, fullMark: 150 },
|
|
89
|
+
{ subject: 'Marketing', A: 98, B: 130, fullMark: 150 },
|
|
90
|
+
{ subject: 'Development', A: 86, B: 130, fullMark: 150 },
|
|
91
|
+
{ subject: 'Customer Support', A: 99, B: 100, fullMark: 150 },
|
|
92
|
+
{ subject: 'Technology', A: 85, B: 90, fullMark: 150 },
|
|
93
|
+
{ subject: 'HR', A: 65, B: 85, fullMark: 150 }
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Örnek aktivite verileri
|
|
98
|
+
const sampleActivities: ActivityItem[] = [
|
|
99
|
+
{
|
|
100
|
+
id: '1',
|
|
101
|
+
type: 'success',
|
|
102
|
+
title: 'completed a purchase',
|
|
103
|
+
description: 'Order #12345 - Premium Plan ($299)',
|
|
104
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 5),
|
|
105
|
+
user: { name: 'John Doe', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=John' },
|
|
106
|
+
icon: <CreditCard className="h-4 w-4" />
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: '2',
|
|
110
|
+
type: 'info',
|
|
111
|
+
title: 'deployed new version',
|
|
112
|
+
description: 'v2.1.0 deployed to production',
|
|
113
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 15),
|
|
114
|
+
user: { name: 'Sarah Chen' },
|
|
115
|
+
icon: <Package className="h-4 w-4" />
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: '3',
|
|
119
|
+
type: 'warning',
|
|
120
|
+
title: 'high server load detected',
|
|
121
|
+
description: 'CPU usage above 80% on server-02',
|
|
122
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 30),
|
|
123
|
+
icon: <AlertCircle className="h-4 w-4" />
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: '4',
|
|
127
|
+
type: 'success',
|
|
128
|
+
title: 'new user registered',
|
|
129
|
+
description: 'Welcome email sent',
|
|
130
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 45),
|
|
131
|
+
user: { name: 'Mike Johnson' }
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: '5',
|
|
135
|
+
type: 'error',
|
|
136
|
+
title: 'payment failed',
|
|
137
|
+
description: 'Card declined - Order #12346',
|
|
138
|
+
timestamp: new Date(Date.now() - 1000 * 60 * 60),
|
|
139
|
+
user: { name: 'Emma Wilson' }
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
// Örnek widget'lar
|
|
144
|
+
const sampleWidgets: Widget[] = [
|
|
145
|
+
// Metrik widget'ları
|
|
146
|
+
{
|
|
147
|
+
id: 'metric-revenue',
|
|
148
|
+
type: 'metric',
|
|
149
|
+
title: 'Revenue',
|
|
150
|
+
size: { w: 3, h: 2 },
|
|
151
|
+
position: { x: 0, y: 0 },
|
|
152
|
+
data: sampleMetrics[0]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'metric-users',
|
|
156
|
+
type: 'metric',
|
|
157
|
+
title: 'Users',
|
|
158
|
+
size: { w: 3, h: 2 },
|
|
159
|
+
position: { x: 3, y: 0 },
|
|
160
|
+
data: sampleMetrics[1]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'metric-orders',
|
|
164
|
+
type: 'metric',
|
|
165
|
+
title: 'Orders',
|
|
166
|
+
size: { w: 3, h: 2 },
|
|
167
|
+
position: { x: 6, y: 0 },
|
|
168
|
+
data: sampleMetrics[2]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'metric-conversion',
|
|
172
|
+
type: 'metric',
|
|
173
|
+
title: 'Conversion',
|
|
174
|
+
size: { w: 3, h: 2 },
|
|
175
|
+
position: { x: 9, y: 0 },
|
|
176
|
+
data: sampleMetrics[3]
|
|
177
|
+
},
|
|
178
|
+
// Chart widget'ları
|
|
179
|
+
{
|
|
180
|
+
id: 'chart-revenue-trend',
|
|
181
|
+
type: 'chart',
|
|
182
|
+
title: 'Revenue Trend',
|
|
183
|
+
description: 'Monthly revenue, profit and expenses',
|
|
184
|
+
size: { w: 8, h: 4 },
|
|
185
|
+
position: { x: 0, y: 2 },
|
|
186
|
+
data: revenueChartData
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'chart-sales-by-product',
|
|
190
|
+
type: 'chart',
|
|
191
|
+
title: 'Sales by Product',
|
|
192
|
+
description: 'Product distribution',
|
|
193
|
+
size: { w: 4, h: 4 },
|
|
194
|
+
position: { x: 8, y: 2 },
|
|
195
|
+
data: salesByProductData
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'chart-performance',
|
|
199
|
+
type: 'chart',
|
|
200
|
+
title: 'Team Performance',
|
|
201
|
+
description: 'Department comparison',
|
|
202
|
+
size: { w: 6, h: 4 },
|
|
203
|
+
position: { x: 0, y: 6 },
|
|
204
|
+
data: performanceData
|
|
205
|
+
},
|
|
206
|
+
// Activity feed
|
|
207
|
+
{
|
|
208
|
+
id: 'activity-feed',
|
|
209
|
+
type: 'activity',
|
|
210
|
+
title: 'Recent Activity',
|
|
211
|
+
size: { w: 6, h: 4 },
|
|
212
|
+
position: { x: 6, y: 6 },
|
|
213
|
+
data: {
|
|
214
|
+
items: sampleActivities,
|
|
215
|
+
realtime: true
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
export function DashboardDemo() {
|
|
221
|
+
return (
|
|
222
|
+
<div className="min-h-screen bg-background">
|
|
223
|
+
<Dashboard
|
|
224
|
+
title="Analytics Dashboard"
|
|
225
|
+
description="Real-time insights and performance metrics"
|
|
226
|
+
widgets={sampleWidgets}
|
|
227
|
+
editable={true}
|
|
228
|
+
realtime={true}
|
|
229
|
+
glassmorphism={true}
|
|
230
|
+
onWidgetAdd={(widget) => {
|
|
231
|
+
console.log('Widget added:', widget)
|
|
232
|
+
}}
|
|
233
|
+
onWidgetRemove={(widgetId) => {
|
|
234
|
+
console.log('Widget removed:', widgetId)
|
|
235
|
+
}}
|
|
236
|
+
onWidgetUpdate={(widgetId, updates) => {
|
|
237
|
+
console.log('Widget updated:', widgetId, updates)
|
|
238
|
+
}}
|
|
239
|
+
onExport={(format) => {
|
|
240
|
+
console.log('Export dashboard as:', format)
|
|
241
|
+
}}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Standalone widget demos
|
|
248
|
+
export function MetricCardDemo() {
|
|
249
|
+
return (
|
|
250
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 p-6">
|
|
251
|
+
{sampleMetrics.map((metric) => (
|
|
252
|
+
<MetricCard
|
|
253
|
+
key={metric.id}
|
|
254
|
+
data={metric}
|
|
255
|
+
showSparkline={true}
|
|
256
|
+
showForecast={true}
|
|
257
|
+
interactive={true}
|
|
258
|
+
glassmorphism={true}
|
|
259
|
+
onAction={(action) => console.log('Metric action:', action)}
|
|
260
|
+
/>
|
|
261
|
+
))}
|
|
262
|
+
</div>
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function ChartWidgetDemo() {
|
|
267
|
+
return (
|
|
268
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 p-6">
|
|
269
|
+
<ChartWidget
|
|
270
|
+
id="demo-1"
|
|
271
|
+
title="Revenue Analysis"
|
|
272
|
+
description="Monthly revenue breakdown"
|
|
273
|
+
data={revenueChartData}
|
|
274
|
+
height={300}
|
|
275
|
+
interactive={true}
|
|
276
|
+
glassmorphism={true}
|
|
277
|
+
onAction={(action, data) => console.log('Chart action:', action, data)}
|
|
278
|
+
/>
|
|
279
|
+
<ChartWidget
|
|
280
|
+
id="demo-2"
|
|
281
|
+
title="Product Sales"
|
|
282
|
+
description="Sales distribution by product"
|
|
283
|
+
data={salesByProductData}
|
|
284
|
+
height={300}
|
|
285
|
+
interactive={true}
|
|
286
|
+
glassmorphism={true}
|
|
287
|
+
/>
|
|
288
|
+
</div>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function ActivityFeedDemo() {
|
|
293
|
+
return (
|
|
294
|
+
<div className="max-w-2xl mx-auto p-6">
|
|
295
|
+
<ActivityFeed
|
|
296
|
+
items={sampleActivities}
|
|
297
|
+
title="System Activity"
|
|
298
|
+
height={400}
|
|
299
|
+
showFilters={true}
|
|
300
|
+
showNotifications={true}
|
|
301
|
+
glassmorphism={true}
|
|
302
|
+
realtime={true}
|
|
303
|
+
onItemClick={(item) => console.log('Activity clicked:', item)}
|
|
304
|
+
onAction={(action, data) => console.log('Feed action:', action, data)}
|
|
305
|
+
/>
|
|
306
|
+
</div>
|
|
307
|
+
)
|
|
308
|
+
}
|