@moontra/moonui-pro 2.20.2 → 2.20.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/package.json +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +191 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { motion, AnimatePresence } from 'framer-motion'
|
|
5
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../ui/card'
|
|
6
|
-
import { Button } from '../../ui/button'
|
|
7
|
-
import { cn } from '../../../lib/utils'
|
|
8
|
-
import {
|
|
9
|
-
LineChart,
|
|
10
|
-
Line,
|
|
11
|
-
BarChart,
|
|
12
|
-
Bar,
|
|
13
|
-
AreaChart,
|
|
14
|
-
Area,
|
|
15
|
-
PieChart,
|
|
16
|
-
Pie,
|
|
17
|
-
RadarChart,
|
|
18
|
-
PolarGrid,
|
|
19
|
-
PolarAngleAxis,
|
|
20
|
-
PolarRadiusAxis,
|
|
21
|
-
Radar,
|
|
22
|
-
XAxis,
|
|
23
|
-
YAxis,
|
|
24
|
-
CartesianGrid,
|
|
25
|
-
Tooltip,
|
|
26
|
-
Legend,
|
|
27
|
-
ResponsiveContainer,
|
|
28
|
-
Cell
|
|
29
|
-
} from 'recharts'
|
|
30
|
-
import {
|
|
31
|
-
MoreVertical,
|
|
32
|
-
Download,
|
|
33
|
-
Maximize2,
|
|
34
|
-
RefreshCw,
|
|
35
|
-
Palette,
|
|
36
|
-
Settings,
|
|
37
|
-
Filter
|
|
38
|
-
} from 'lucide-react'
|
|
39
|
-
import {
|
|
40
|
-
DropdownMenu,
|
|
41
|
-
DropdownMenuContent,
|
|
42
|
-
DropdownMenuItem,
|
|
43
|
-
DropdownMenuSeparator,
|
|
44
|
-
DropdownMenuTrigger,
|
|
45
|
-
DropdownMenuSub,
|
|
46
|
-
DropdownMenuSubContent,
|
|
47
|
-
DropdownMenuSubTrigger,
|
|
48
|
-
} from '../../ui/dropdown-menu'
|
|
49
|
-
import { ChartData } from '../types'
|
|
50
|
-
|
|
51
|
-
interface ChartWidgetProps {
|
|
52
|
-
id: string
|
|
53
|
-
title: string
|
|
54
|
-
description?: string
|
|
55
|
-
data: ChartData
|
|
56
|
-
className?: string
|
|
57
|
-
height?: number
|
|
58
|
-
onAction?: (action: string, data?: any) => void
|
|
59
|
-
loading?: boolean
|
|
60
|
-
refreshing?: boolean
|
|
61
|
-
glassmorphism?: boolean
|
|
62
|
-
interactive?: boolean
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Renk paleti
|
|
66
|
-
const CHART_COLORS = {
|
|
67
|
-
default: ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899'],
|
|
68
|
-
pastel: ['#93c5fd', '#86efac', '#fcd34d', '#fca5a5', '#c4b5fd', '#fbcfe8'],
|
|
69
|
-
vibrant: ['#2563eb', '#059669', '#d97706', '#dc2626', '#7c3aed', '#db2777'],
|
|
70
|
-
monochrome: ['#1f2937', '#374151', '#4b5563', '#6b7280', '#9ca3af', '#d1d5db']
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function ChartWidget({
|
|
74
|
-
id,
|
|
75
|
-
title,
|
|
76
|
-
description,
|
|
77
|
-
data,
|
|
78
|
-
className,
|
|
79
|
-
height = 300,
|
|
80
|
-
onAction,
|
|
81
|
-
loading = false,
|
|
82
|
-
refreshing = false,
|
|
83
|
-
glassmorphism = false,
|
|
84
|
-
interactive = true
|
|
85
|
-
}: ChartWidgetProps) {
|
|
86
|
-
const [colorScheme, setColorScheme] = React.useState<keyof typeof CHART_COLORS>('default')
|
|
87
|
-
const [isFullscreen, setIsFullscreen] = React.useState(false)
|
|
88
|
-
|
|
89
|
-
const colors = CHART_COLORS[colorScheme]
|
|
90
|
-
|
|
91
|
-
// Custom tooltip
|
|
92
|
-
const CustomTooltip = ({ active, payload, label }: any) => {
|
|
93
|
-
if (!active || !payload) return null
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<motion.div
|
|
97
|
-
initial={{ opacity: 0, scale: 0.9 }}
|
|
98
|
-
animate={{ opacity: 1, scale: 1 }}
|
|
99
|
-
className={cn(
|
|
100
|
-
"p-3 rounded-lg shadow-lg",
|
|
101
|
-
glassmorphism
|
|
102
|
-
? "bg-background/80 backdrop-blur-md border border-white/10"
|
|
103
|
-
: "bg-background border"
|
|
104
|
-
)}
|
|
105
|
-
>
|
|
106
|
-
<p className="text-sm font-medium">{label}</p>
|
|
107
|
-
{payload.map((entry: any, index: number) => (
|
|
108
|
-
<p key={index} className="text-sm mt-1">
|
|
109
|
-
<span
|
|
110
|
-
className="inline-block w-3 h-3 rounded-full mr-2"
|
|
111
|
-
style={{ backgroundColor: entry.color }}
|
|
112
|
-
/>
|
|
113
|
-
{entry.name}: {entry.value}
|
|
114
|
-
</p>
|
|
115
|
-
))}
|
|
116
|
-
</motion.div>
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Chart renderer
|
|
121
|
-
const renderChart = () => {
|
|
122
|
-
if (!data.data || data.data.length === 0) {
|
|
123
|
-
return (
|
|
124
|
-
<div className="flex items-center justify-center h-full text-muted-foreground">
|
|
125
|
-
No data available
|
|
126
|
-
</div>
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const chartProps = {
|
|
131
|
-
data: data.data,
|
|
132
|
-
margin: { top: 5, right: 30, left: 20, bottom: 5 }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
switch (data.type) {
|
|
136
|
-
case 'line':
|
|
137
|
-
return (
|
|
138
|
-
<ResponsiveContainer width="100%" height={height}>
|
|
139
|
-
<LineChart {...chartProps}>
|
|
140
|
-
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
|
|
141
|
-
<XAxis dataKey="name" />
|
|
142
|
-
<YAxis />
|
|
143
|
-
<Tooltip content={<CustomTooltip />} />
|
|
144
|
-
{interactive && <Legend />}
|
|
145
|
-
{Object.keys(data.data[0])
|
|
146
|
-
.filter(key => key !== 'name')
|
|
147
|
-
.map((key, index) => (
|
|
148
|
-
<Line
|
|
149
|
-
key={key}
|
|
150
|
-
type="monotone"
|
|
151
|
-
dataKey={key}
|
|
152
|
-
stroke={colors[index % colors.length]}
|
|
153
|
-
strokeWidth={2}
|
|
154
|
-
dot={{ r: 4 }}
|
|
155
|
-
activeDot={{ r: 6 }}
|
|
156
|
-
/>
|
|
157
|
-
))}
|
|
158
|
-
</LineChart>
|
|
159
|
-
</ResponsiveContainer>
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
case 'bar':
|
|
163
|
-
return (
|
|
164
|
-
<ResponsiveContainer width="100%" height={height}>
|
|
165
|
-
<BarChart {...chartProps}>
|
|
166
|
-
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
|
|
167
|
-
<XAxis dataKey="name" />
|
|
168
|
-
<YAxis />
|
|
169
|
-
<Tooltip content={<CustomTooltip />} />
|
|
170
|
-
{interactive && <Legend />}
|
|
171
|
-
{Object.keys(data.data[0])
|
|
172
|
-
.filter(key => key !== 'name')
|
|
173
|
-
.map((key, index) => (
|
|
174
|
-
<Bar
|
|
175
|
-
key={key}
|
|
176
|
-
dataKey={key}
|
|
177
|
-
fill={colors[index % colors.length]}
|
|
178
|
-
radius={[4, 4, 0, 0]}
|
|
179
|
-
/>
|
|
180
|
-
))}
|
|
181
|
-
</BarChart>
|
|
182
|
-
</ResponsiveContainer>
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
case 'area':
|
|
186
|
-
return (
|
|
187
|
-
<ResponsiveContainer width="100%" height={height}>
|
|
188
|
-
<AreaChart {...chartProps}>
|
|
189
|
-
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
|
|
190
|
-
<XAxis dataKey="name" />
|
|
191
|
-
<YAxis />
|
|
192
|
-
<Tooltip content={<CustomTooltip />} />
|
|
193
|
-
{interactive && <Legend />}
|
|
194
|
-
{Object.keys(data.data[0])
|
|
195
|
-
.filter(key => key !== 'name')
|
|
196
|
-
.map((key, index) => (
|
|
197
|
-
<Area
|
|
198
|
-
key={key}
|
|
199
|
-
type="monotone"
|
|
200
|
-
dataKey={key}
|
|
201
|
-
stroke={colors[index % colors.length]}
|
|
202
|
-
fill={colors[index % colors.length]}
|
|
203
|
-
fillOpacity={0.2}
|
|
204
|
-
/>
|
|
205
|
-
))}
|
|
206
|
-
</AreaChart>
|
|
207
|
-
</ResponsiveContainer>
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
case 'pie':
|
|
211
|
-
case 'donut':
|
|
212
|
-
return (
|
|
213
|
-
<ResponsiveContainer width="100%" height={height}>
|
|
214
|
-
<PieChart>
|
|
215
|
-
<Pie
|
|
216
|
-
data={data.data}
|
|
217
|
-
cx="50%"
|
|
218
|
-
cy="50%"
|
|
219
|
-
labelLine={false}
|
|
220
|
-
label={interactive}
|
|
221
|
-
outerRadius={data.type === 'donut' ? 80 : 100}
|
|
222
|
-
innerRadius={data.type === 'donut' ? 50 : 0}
|
|
223
|
-
fill="#8884d8"
|
|
224
|
-
dataKey="value"
|
|
225
|
-
>
|
|
226
|
-
{data.data.map((entry: any, index: number) => (
|
|
227
|
-
<Cell key={`cell-${index}`} fill={colors[index % colors.length]} />
|
|
228
|
-
))}
|
|
229
|
-
</Pie>
|
|
230
|
-
<Tooltip content={<CustomTooltip />} />
|
|
231
|
-
{interactive && <Legend />}
|
|
232
|
-
</PieChart>
|
|
233
|
-
</ResponsiveContainer>
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
case 'radar':
|
|
237
|
-
return (
|
|
238
|
-
<ResponsiveContainer width="100%" height={height}>
|
|
239
|
-
<RadarChart data={data.data}>
|
|
240
|
-
<PolarGrid />
|
|
241
|
-
<PolarAngleAxis dataKey="subject" />
|
|
242
|
-
<PolarRadiusAxis />
|
|
243
|
-
{Object.keys(data.data[0])
|
|
244
|
-
.filter(key => key !== 'subject')
|
|
245
|
-
.map((key, index) => (
|
|
246
|
-
<Radar
|
|
247
|
-
key={key}
|
|
248
|
-
name={key}
|
|
249
|
-
dataKey={key}
|
|
250
|
-
stroke={colors[index % colors.length]}
|
|
251
|
-
fill={colors[index % colors.length]}
|
|
252
|
-
fillOpacity={0.3}
|
|
253
|
-
/>
|
|
254
|
-
))}
|
|
255
|
-
<Tooltip content={<CustomTooltip />} />
|
|
256
|
-
{interactive && <Legend />}
|
|
257
|
-
</RadarChart>
|
|
258
|
-
</ResponsiveContainer>
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
default:
|
|
262
|
-
return null
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Card variants
|
|
267
|
-
const cardVariants = {
|
|
268
|
-
initial: { opacity: 0, scale: 0.95 },
|
|
269
|
-
animate: {
|
|
270
|
-
opacity: 1,
|
|
271
|
-
scale: 1,
|
|
272
|
-
transition: { duration: 0.3, ease: "easeOut" }
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return (
|
|
277
|
-
<motion.div
|
|
278
|
-
variants={cardVariants}
|
|
279
|
-
initial="initial"
|
|
280
|
-
animate="animate"
|
|
281
|
-
className={cn(isFullscreen && "fixed inset-4 z-50")}
|
|
282
|
-
>
|
|
283
|
-
<Card className={cn(
|
|
284
|
-
"relative overflow-hidden transition-all duration-300",
|
|
285
|
-
glassmorphism && "bg-background/60 backdrop-blur-md border-white/10",
|
|
286
|
-
isFullscreen && "h-full",
|
|
287
|
-
className
|
|
288
|
-
)}>
|
|
289
|
-
{/* Header */}
|
|
290
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
291
|
-
<div>
|
|
292
|
-
<CardTitle className="text-base font-semibold">{title}</CardTitle>
|
|
293
|
-
{description && (
|
|
294
|
-
<p className="text-sm text-muted-foreground mt-1">{description}</p>
|
|
295
|
-
)}
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
{/* Actions */}
|
|
299
|
-
<div className="flex items-center gap-2">
|
|
300
|
-
{refreshing && (
|
|
301
|
-
<motion.div
|
|
302
|
-
animate={{ rotate: 360 }}
|
|
303
|
-
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
|
|
304
|
-
>
|
|
305
|
-
<RefreshCw className="h-4 w-4 text-muted-foreground" />
|
|
306
|
-
</motion.div>
|
|
307
|
-
)}
|
|
308
|
-
|
|
309
|
-
{interactive && (
|
|
310
|
-
<DropdownMenu>
|
|
311
|
-
<DropdownMenuTrigger asChild>
|
|
312
|
-
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
|
313
|
-
<MoreVertical className="h-4 w-4" />
|
|
314
|
-
</Button>
|
|
315
|
-
</DropdownMenuTrigger>
|
|
316
|
-
<DropdownMenuContent align="end" className="w-48">
|
|
317
|
-
<DropdownMenuItem onClick={() => {
|
|
318
|
-
setIsFullscreen(!isFullscreen)
|
|
319
|
-
onAction?.('fullscreen', { fullscreen: !isFullscreen })
|
|
320
|
-
}}>
|
|
321
|
-
<Maximize2 className="mr-2 h-4 w-4" />
|
|
322
|
-
{isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
|
|
323
|
-
</DropdownMenuItem>
|
|
324
|
-
|
|
325
|
-
<DropdownMenuSub>
|
|
326
|
-
<DropdownMenuSubTrigger>
|
|
327
|
-
<Palette className="mr-2 h-4 w-4" />
|
|
328
|
-
Color Scheme
|
|
329
|
-
</DropdownMenuSubTrigger>
|
|
330
|
-
<DropdownMenuSubContent>
|
|
331
|
-
{Object.keys(CHART_COLORS).map((scheme) => (
|
|
332
|
-
<DropdownMenuItem
|
|
333
|
-
key={scheme}
|
|
334
|
-
onClick={() => setColorScheme(scheme as keyof typeof CHART_COLORS)}
|
|
335
|
-
>
|
|
336
|
-
<div className="flex items-center gap-2">
|
|
337
|
-
<div className="flex gap-1">
|
|
338
|
-
{CHART_COLORS[scheme as keyof typeof CHART_COLORS].slice(0, 3).map((color, i) => (
|
|
339
|
-
<div
|
|
340
|
-
key={i}
|
|
341
|
-
className="w-3 h-3 rounded-full"
|
|
342
|
-
style={{ backgroundColor: color }}
|
|
343
|
-
/>
|
|
344
|
-
))}
|
|
345
|
-
</div>
|
|
346
|
-
<span className="capitalize">{scheme}</span>
|
|
347
|
-
</div>
|
|
348
|
-
</DropdownMenuItem>
|
|
349
|
-
))}
|
|
350
|
-
</DropdownMenuSubContent>
|
|
351
|
-
</DropdownMenuSub>
|
|
352
|
-
|
|
353
|
-
<DropdownMenuSeparator />
|
|
354
|
-
|
|
355
|
-
<DropdownMenuItem onClick={() => onAction?.('export', { format: 'png' })}>
|
|
356
|
-
<Download className="mr-2 h-4 w-4" />
|
|
357
|
-
Export as PNG
|
|
358
|
-
</DropdownMenuItem>
|
|
359
|
-
|
|
360
|
-
<DropdownMenuItem onClick={() => onAction?.('export', { format: 'csv' })}>
|
|
361
|
-
<Download className="mr-2 h-4 w-4" />
|
|
362
|
-
Export Data
|
|
363
|
-
</DropdownMenuItem>
|
|
364
|
-
|
|
365
|
-
<DropdownMenuSeparator />
|
|
366
|
-
|
|
367
|
-
<DropdownMenuItem onClick={() => onAction?.('filter')}>
|
|
368
|
-
<Filter className="mr-2 h-4 w-4" />
|
|
369
|
-
Filter Data
|
|
370
|
-
</DropdownMenuItem>
|
|
371
|
-
|
|
372
|
-
<DropdownMenuItem onClick={() => onAction?.('settings')}>
|
|
373
|
-
<Settings className="mr-2 h-4 w-4" />
|
|
374
|
-
Chart Settings
|
|
375
|
-
</DropdownMenuItem>
|
|
376
|
-
</DropdownMenuContent>
|
|
377
|
-
</DropdownMenu>
|
|
378
|
-
)}
|
|
379
|
-
</div>
|
|
380
|
-
</CardHeader>
|
|
381
|
-
|
|
382
|
-
{/* Chart Content */}
|
|
383
|
-
<CardContent className="p-0 px-4 pb-4">
|
|
384
|
-
<AnimatePresence mode="wait">
|
|
385
|
-
{loading ? (
|
|
386
|
-
<motion.div
|
|
387
|
-
key="loading"
|
|
388
|
-
initial={{ opacity: 0 }}
|
|
389
|
-
animate={{ opacity: 1 }}
|
|
390
|
-
exit={{ opacity: 0 }}
|
|
391
|
-
className="flex items-center justify-center"
|
|
392
|
-
style={{ height }}
|
|
393
|
-
>
|
|
394
|
-
<div className="space-y-2">
|
|
395
|
-
<div className="animate-pulse bg-muted h-4 w-32 rounded" />
|
|
396
|
-
<div className="animate-pulse bg-muted h-4 w-24 rounded" />
|
|
397
|
-
<div className="animate-pulse bg-muted h-4 w-28 rounded" />
|
|
398
|
-
</div>
|
|
399
|
-
</motion.div>
|
|
400
|
-
) : (
|
|
401
|
-
<motion.div
|
|
402
|
-
key="chart"
|
|
403
|
-
initial={{ opacity: 0, y: 10 }}
|
|
404
|
-
animate={{ opacity: 1, y: 0 }}
|
|
405
|
-
exit={{ opacity: 0, y: -10 }}
|
|
406
|
-
transition={{ duration: 0.3 }}
|
|
407
|
-
>
|
|
408
|
-
{renderChart()}
|
|
409
|
-
</motion.div>
|
|
410
|
-
)}
|
|
411
|
-
</AnimatePresence>
|
|
412
|
-
</CardContent>
|
|
413
|
-
</Card>
|
|
414
|
-
</motion.div>
|
|
415
|
-
)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
export default ChartWidget
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import { motion } from 'framer-motion'
|
|
5
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../ui/card'
|
|
6
|
-
import { cn } from '../../../lib/utils'
|
|
7
|
-
import { ComparisonData } from '../types'
|
|
8
|
-
import { ArrowUpRight, ArrowDownRight, Minus, BarChart3 } from 'lucide-react'
|
|
9
|
-
import { Badge } from '../../ui/badge'
|
|
10
|
-
|
|
11
|
-
interface ComparisonWidgetProps {
|
|
12
|
-
data: ComparisonData
|
|
13
|
-
title?: string
|
|
14
|
-
className?: string
|
|
15
|
-
glassmorphism?: boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function ComparisonWidget({
|
|
19
|
-
data,
|
|
20
|
-
title = "Comparison",
|
|
21
|
-
className,
|
|
22
|
-
glassmorphism = false
|
|
23
|
-
}: ComparisonWidgetProps) {
|
|
24
|
-
const [isMounted, setIsMounted] = React.useState(false)
|
|
25
|
-
|
|
26
|
-
React.useEffect(() => {
|
|
27
|
-
setIsMounted(true)
|
|
28
|
-
}, [])
|
|
29
|
-
|
|
30
|
-
// En yüksek değeri bul
|
|
31
|
-
const maxValue = Math.max(...data.periods.map(p => p.value))
|
|
32
|
-
|
|
33
|
-
// İlk iki period arasındaki değişimi hesapla
|
|
34
|
-
const change = data.periods.length >= 2
|
|
35
|
-
? ((data.periods[0].value - data.periods[1].value) / data.periods[1].value) * 100
|
|
36
|
-
: 0
|
|
37
|
-
|
|
38
|
-
const changeType = change > 0 ? 'increase' : change < 0 ? 'decrease' : 'neutral'
|
|
39
|
-
|
|
40
|
-
// Değer formatla
|
|
41
|
-
const formatValue = (value: number) => {
|
|
42
|
-
if (!isMounted) return value.toString()
|
|
43
|
-
|
|
44
|
-
if (value >= 1000000) {
|
|
45
|
-
return (value / 1000000).toFixed(1) + 'M'
|
|
46
|
-
} else if (value >= 1000) {
|
|
47
|
-
return (value / 1000).toFixed(0) + 'K'
|
|
48
|
-
}
|
|
49
|
-
return value.toLocaleString()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<Card className={cn(
|
|
54
|
-
"h-full flex flex-col overflow-hidden",
|
|
55
|
-
glassmorphism && "bg-background/60 backdrop-blur-md border-white/10",
|
|
56
|
-
className
|
|
57
|
-
)}>
|
|
58
|
-
<CardHeader className="flex-shrink-0 pb-3 px-4">
|
|
59
|
-
<div className="flex items-start justify-between gap-2">
|
|
60
|
-
<div className="flex-1 min-w-0">
|
|
61
|
-
<CardTitle className="text-sm font-semibold flex items-center gap-2">
|
|
62
|
-
<BarChart3 className="h-4 w-4 text-muted-foreground flex-shrink-0" />
|
|
63
|
-
<span className="truncate">{title}</span>
|
|
64
|
-
</CardTitle>
|
|
65
|
-
{data.metric && (
|
|
66
|
-
<p className="text-xs text-muted-foreground mt-1 truncate">{data.metric}</p>
|
|
67
|
-
)}
|
|
68
|
-
</div>
|
|
69
|
-
{change !== 0 && (
|
|
70
|
-
<div
|
|
71
|
-
className={cn(
|
|
72
|
-
"inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs font-medium flex-shrink-0",
|
|
73
|
-
changeType === 'increase' && "bg-green-500/10 text-green-600 dark:text-green-400 border border-green-500/20",
|
|
74
|
-
changeType === 'decrease' && "bg-red-500/10 text-red-600 dark:text-red-400 border border-red-500/20",
|
|
75
|
-
changeType === 'neutral' && "bg-muted text-muted-foreground border border-border"
|
|
76
|
-
)}
|
|
77
|
-
>
|
|
78
|
-
{changeType === 'increase' ? <ArrowUpRight className="h-3 w-3" /> :
|
|
79
|
-
changeType === 'decrease' ? <ArrowDownRight className="h-3 w-3" /> :
|
|
80
|
-
<Minus className="h-3 w-3" />}
|
|
81
|
-
<span>
|
|
82
|
-
{Math.abs(change).toFixed(1)}%
|
|
83
|
-
</span>
|
|
84
|
-
</div>
|
|
85
|
-
)}
|
|
86
|
-
</div>
|
|
87
|
-
</CardHeader>
|
|
88
|
-
|
|
89
|
-
<CardContent className="flex-1 px-4 pb-4 overflow-hidden flex flex-col gap-3">
|
|
90
|
-
{/* Bar list görünümü - scrollable area */}
|
|
91
|
-
<div className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden pr-2 -mr-2">
|
|
92
|
-
<div className="space-y-2.5">
|
|
93
|
-
{data.periods.map((period, index) => {
|
|
94
|
-
const percentage = (period.value / maxValue) * 100
|
|
95
|
-
const isHighest = period.value === maxValue
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<motion.div
|
|
99
|
-
key={period.label}
|
|
100
|
-
initial={{ opacity: 0, x: -20 }}
|
|
101
|
-
animate={{ opacity: 1, x: 0 }}
|
|
102
|
-
transition={{ delay: index * 0.05 }}
|
|
103
|
-
>
|
|
104
|
-
{/* Label ve değer */}
|
|
105
|
-
<div className="flex items-center justify-between gap-3 mb-1">
|
|
106
|
-
<span className={cn(
|
|
107
|
-
"text-xs font-medium truncate flex-1",
|
|
108
|
-
isHighest ? "text-foreground" : "text-muted-foreground"
|
|
109
|
-
)}>
|
|
110
|
-
{period.label}
|
|
111
|
-
</span>
|
|
112
|
-
<span className={cn(
|
|
113
|
-
"text-xs font-semibold tabular-nums flex-shrink-0",
|
|
114
|
-
isHighest && "text-primary"
|
|
115
|
-
)}>
|
|
116
|
-
{formatValue(period.value)}
|
|
117
|
-
</span>
|
|
118
|
-
</div>
|
|
119
|
-
|
|
120
|
-
{/* Progress bar */}
|
|
121
|
-
<div className="w-full bg-muted rounded-full h-1.5 overflow-hidden">
|
|
122
|
-
<motion.div
|
|
123
|
-
className={cn(
|
|
124
|
-
"h-full rounded-full transition-colors",
|
|
125
|
-
isHighest ? "bg-primary" : "bg-primary/60"
|
|
126
|
-
)}
|
|
127
|
-
initial={{ width: 0 }}
|
|
128
|
-
animate={{ width: `${Math.min(percentage, 100)}%` }}
|
|
129
|
-
transition={{ duration: 0.5, delay: index * 0.05 }}
|
|
130
|
-
/>
|
|
131
|
-
</div>
|
|
132
|
-
</motion.div>
|
|
133
|
-
)
|
|
134
|
-
})}
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
|
|
138
|
-
{/* Compact chart view - fixed height */}
|
|
139
|
-
{data.showChart && (
|
|
140
|
-
<div className="flex-shrink-0 pt-3 border-t">
|
|
141
|
-
<div className="h-14 flex items-end justify-between gap-1">
|
|
142
|
-
{data.periods.slice(0, 5).map((period, index) => {
|
|
143
|
-
const height = (period.value / maxValue) * 100
|
|
144
|
-
const isHighest = period.value === maxValue
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<motion.div
|
|
148
|
-
key={period.label}
|
|
149
|
-
className="flex-1 flex flex-col items-center gap-0.5"
|
|
150
|
-
initial={{ opacity: 0 }}
|
|
151
|
-
animate={{ opacity: 1 }}
|
|
152
|
-
transition={{ delay: index * 0.05 }}
|
|
153
|
-
>
|
|
154
|
-
<div className="w-full h-10 flex items-end justify-center px-0.5">
|
|
155
|
-
<motion.div
|
|
156
|
-
className={cn(
|
|
157
|
-
"w-full max-w-[20px] rounded-t transition-colors",
|
|
158
|
-
isHighest ? "bg-primary" : "bg-primary/40"
|
|
159
|
-
)}
|
|
160
|
-
initial={{ height: 0 }}
|
|
161
|
-
animate={{ height: `${height}%` }}
|
|
162
|
-
transition={{ duration: 0.5, delay: index * 0.05 }}
|
|
163
|
-
/>
|
|
164
|
-
</div>
|
|
165
|
-
<span className="text-[9px] text-muted-foreground">
|
|
166
|
-
{period.label.slice(0, 3)}
|
|
167
|
-
</span>
|
|
168
|
-
</motion.div>
|
|
169
|
-
)
|
|
170
|
-
})}
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
)}
|
|
174
|
-
</CardContent>
|
|
175
|
-
</Card>
|
|
176
|
-
)
|
|
177
|
-
}
|