gradient-forge 1.0.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/.eslintrc.json +3 -0
- package/.github/FUNDING.yml +2 -0
- package/README.md +140 -0
- package/app/docs/page.tsx +417 -0
- package/app/gallery/page.tsx +398 -0
- package/app/globals.css +1155 -0
- package/app/layout.tsx +36 -0
- package/app/page.tsx +600 -0
- package/app/showcase/page.tsx +730 -0
- package/app/studio/page.tsx +1310 -0
- package/cli/index.mjs +1141 -0
- package/cli/templates/theme-context.tsx +120 -0
- package/cli/templates/theme-engine.ts +237 -0
- package/cli/templates/themes.css +512 -0
- package/components/site/component-showcase.tsx +623 -0
- package/components/site/site-data.ts +103 -0
- package/components/site/site-header.tsx +270 -0
- package/components/templates/blog.tsx +198 -0
- package/components/templates/components-showcase.tsx +298 -0
- package/components/templates/dashboard.tsx +246 -0
- package/components/templates/ecommerce.tsx +199 -0
- package/components/templates/mail.tsx +275 -0
- package/components/templates/saas-landing.tsx +169 -0
- package/components/theme/studio-code-panel.tsx +485 -0
- package/components/theme/theme-context.tsx +120 -0
- package/components/theme/theme-engine.ts +237 -0
- package/components/theme/theme-exporter.tsx +369 -0
- package/components/theme/theme-panel.tsx +268 -0
- package/components/theme/token-export-utils.ts +1211 -0
- package/components/ui/animated.tsx +55 -0
- package/components/ui/avatar.tsx +38 -0
- package/components/ui/badge.tsx +32 -0
- package/components/ui/button.tsx +65 -0
- package/components/ui/card.tsx +56 -0
- package/components/ui/checkbox.tsx +19 -0
- package/components/ui/command-palette.tsx +245 -0
- package/components/ui/gsap-animated.tsx +436 -0
- package/components/ui/input.tsx +17 -0
- package/components/ui/select.tsx +176 -0
- package/components/ui/skeleton.tsx +102 -0
- package/components/ui/switch.tsx +43 -0
- package/components/ui/tabs.tsx +115 -0
- package/components/ui/toast.tsx +119 -0
- package/gradient-forge/theme-context.tsx +119 -0
- package/gradient-forge/theme-engine.ts +236 -0
- package/gradient-forge/themes.css +556 -0
- package/lib/animations.ts +50 -0
- package/lib/gsap.ts +426 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +6 -0
- package/package.json +53 -0
- package/postcss.config.mjs +5 -0
- package/tailwind.config.ts +15 -0
- package/tsconfig.json +43 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
5
|
+
import { Badge } from "@/components/ui/badge";
|
|
6
|
+
import { Input } from "@/components/ui/input";
|
|
7
|
+
import { Switch } from "@/components/ui/switch";
|
|
8
|
+
import {
|
|
9
|
+
Tabs,
|
|
10
|
+
TabsContent,
|
|
11
|
+
TabsList,
|
|
12
|
+
TabsTrigger
|
|
13
|
+
} from "@/components/ui/tabs";
|
|
14
|
+
import {
|
|
15
|
+
Bell,
|
|
16
|
+
Check,
|
|
17
|
+
ChevronRight,
|
|
18
|
+
Copy,
|
|
19
|
+
Moon,
|
|
20
|
+
Search,
|
|
21
|
+
Settings,
|
|
22
|
+
Sun,
|
|
23
|
+
User,
|
|
24
|
+
Mail,
|
|
25
|
+
Loader2
|
|
26
|
+
} from "lucide-react";
|
|
27
|
+
import { cn } from "@/lib/utils";
|
|
28
|
+
|
|
29
|
+
interface ComponentsTemplateProps {
|
|
30
|
+
preview?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ComponentsTemplate({ preview }: ComponentsTemplateProps) {
|
|
34
|
+
return (
|
|
35
|
+
<div className="min-h-full bg-background">
|
|
36
|
+
{/* Header */}
|
|
37
|
+
<header className="border-b border-border/50 bg-background/80 backdrop-blur-xl">
|
|
38
|
+
<div className="max-w-6xl mx-auto px-4 sm:px-6 h-14 flex items-center justify-between">
|
|
39
|
+
<h1 className="font-semibold text-base sm:text-lg">Component Library</h1>
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
<div className="relative hidden sm:block">
|
|
42
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
43
|
+
<input
|
|
44
|
+
type="text"
|
|
45
|
+
placeholder="Search components..."
|
|
46
|
+
className="pl-9 pr-4 py-2 rounded-full border border-border/50 bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary/20 w-56"
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
<Button variant="outline" size="sm" className="gap-2 text-xs">
|
|
50
|
+
<Copy className="h-3.5 w-3.5" />
|
|
51
|
+
<span className="hidden sm:inline">Copy All</span>
|
|
52
|
+
</Button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</header>
|
|
56
|
+
|
|
57
|
+
<main className="max-w-6xl mx-auto px-4 sm:px-6 py-6 sm:py-8">
|
|
58
|
+
<Tabs defaultValue="buttons" className="space-y-6">
|
|
59
|
+
<TabsList className="w-full justify-start overflow-x-auto">
|
|
60
|
+
<TabsTrigger value="buttons" className="text-xs">Buttons</TabsTrigger>
|
|
61
|
+
<TabsTrigger value="badges" className="text-xs">Badges</TabsTrigger>
|
|
62
|
+
<TabsTrigger value="inputs" className="text-xs">Inputs</TabsTrigger>
|
|
63
|
+
<TabsTrigger value="cards" className="text-xs">Cards</TabsTrigger>
|
|
64
|
+
<TabsTrigger value="feedback" className="text-xs">Feedback</TabsTrigger>
|
|
65
|
+
</TabsList>
|
|
66
|
+
|
|
67
|
+
{/* Buttons Section */}
|
|
68
|
+
<TabsContent value="buttons" className="space-y-6">
|
|
69
|
+
<div>
|
|
70
|
+
<h2 className="text-lg font-semibold mb-4">Button Variants</h2>
|
|
71
|
+
<Card>
|
|
72
|
+
<CardContent className="p-6">
|
|
73
|
+
<div className="flex flex-wrap gap-3">
|
|
74
|
+
<Button variant="default" size="sm">Default</Button>
|
|
75
|
+
<Button variant="secondary" size="sm">Secondary</Button>
|
|
76
|
+
<Button variant="outline" size="sm">Outline</Button>
|
|
77
|
+
<Button variant="ghost" size="sm">Ghost</Button>
|
|
78
|
+
<Button variant="glow" size="sm">Glow</Button>
|
|
79
|
+
</div>
|
|
80
|
+
</CardContent>
|
|
81
|
+
</Card>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div>
|
|
85
|
+
<h2 className="text-lg font-semibold mb-4">Button Sizes</h2>
|
|
86
|
+
<Card>
|
|
87
|
+
<CardContent className="p-6">
|
|
88
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
89
|
+
<Button size="sm" variant="glow">Small</Button>
|
|
90
|
+
<Button size="md" variant="glow">Medium</Button>
|
|
91
|
+
<Button size="lg" variant="glow">Large</Button>
|
|
92
|
+
</div>
|
|
93
|
+
</CardContent>
|
|
94
|
+
</Card>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div>
|
|
98
|
+
<h2 className="text-lg font-semibold mb-4">Button States</h2>
|
|
99
|
+
<Card>
|
|
100
|
+
<CardContent className="p-6">
|
|
101
|
+
<div className="flex flex-wrap gap-3">
|
|
102
|
+
<Button variant="glow" size="sm">
|
|
103
|
+
<Check className="h-4 w-4 mr-2" />
|
|
104
|
+
With Icon
|
|
105
|
+
</Button>
|
|
106
|
+
<Button variant="glow" size="sm" disabled>
|
|
107
|
+
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
108
|
+
Loading
|
|
109
|
+
</Button>
|
|
110
|
+
<Button variant="outline" size="sm" disabled>
|
|
111
|
+
Disabled
|
|
112
|
+
</Button>
|
|
113
|
+
</div>
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
116
|
+
</div>
|
|
117
|
+
</TabsContent>
|
|
118
|
+
|
|
119
|
+
{/* Badges Section */}
|
|
120
|
+
<TabsContent value="badges" className="space-y-6">
|
|
121
|
+
<Card>
|
|
122
|
+
<CardHeader>
|
|
123
|
+
<CardTitle className="text-base">Badge Variants</CardTitle>
|
|
124
|
+
<CardDescription>Different badge styles for various use cases.</CardDescription>
|
|
125
|
+
</CardHeader>
|
|
126
|
+
<CardContent className="space-y-4">
|
|
127
|
+
<div className="flex flex-wrap gap-2">
|
|
128
|
+
<Badge>Default</Badge>
|
|
129
|
+
<Badge variant="outline">Outline</Badge>
|
|
130
|
+
<Badge variant="glass">Glass</Badge>
|
|
131
|
+
</div>
|
|
132
|
+
<div className="flex flex-wrap gap-2">
|
|
133
|
+
<Badge className="gap-1">
|
|
134
|
+
<Check className="h-3 w-3" />
|
|
135
|
+
Success
|
|
136
|
+
</Badge>
|
|
137
|
+
<Badge variant="outline" className="gap-1">
|
|
138
|
+
<Bell className="h-3 w-3" />
|
|
139
|
+
Notification
|
|
140
|
+
</Badge>
|
|
141
|
+
<Badge variant="glass" className="gap-1">
|
|
142
|
+
<Settings className="h-3 w-3" />
|
|
143
|
+
Settings
|
|
144
|
+
</Badge>
|
|
145
|
+
</div>
|
|
146
|
+
</CardContent>
|
|
147
|
+
</Card>
|
|
148
|
+
</TabsContent>
|
|
149
|
+
|
|
150
|
+
{/* Inputs Section */}
|
|
151
|
+
<TabsContent value="inputs" className="space-y-6">
|
|
152
|
+
<Card>
|
|
153
|
+
<CardHeader>
|
|
154
|
+
<CardTitle className="text-base">Input Fields</CardTitle>
|
|
155
|
+
<CardDescription>Text inputs with various states and icons.</CardDescription>
|
|
156
|
+
</CardHeader>
|
|
157
|
+
<CardContent className="space-y-4">
|
|
158
|
+
<div className="grid sm:grid-cols-2 gap-4">
|
|
159
|
+
<div className="space-y-2">
|
|
160
|
+
<label className="text-sm font-medium">Default Input</label>
|
|
161
|
+
<Input placeholder="Enter text..." />
|
|
162
|
+
</div>
|
|
163
|
+
<div className="space-y-2">
|
|
164
|
+
<label className="text-sm font-medium">With Icon</label>
|
|
165
|
+
<div className="relative">
|
|
166
|
+
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
167
|
+
<Input placeholder="Email address" className="pl-10" />
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div className="space-y-2">
|
|
171
|
+
<label className="text-sm font-medium">Disabled</label>
|
|
172
|
+
<Input placeholder="Disabled input" disabled />
|
|
173
|
+
</div>
|
|
174
|
+
<div className="space-y-2">
|
|
175
|
+
<label className="text-sm font-medium">With Button</label>
|
|
176
|
+
<div className="flex gap-2">
|
|
177
|
+
<Input placeholder="Search..." className="flex-1" />
|
|
178
|
+
<Button size="sm" variant="glow">
|
|
179
|
+
<Search className="h-4 w-4" />
|
|
180
|
+
</Button>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</CardContent>
|
|
185
|
+
</Card>
|
|
186
|
+
|
|
187
|
+
<Card>
|
|
188
|
+
<CardHeader>
|
|
189
|
+
<CardTitle className="text-base">Switches</CardTitle>
|
|
190
|
+
</CardHeader>
|
|
191
|
+
<CardContent>
|
|
192
|
+
<div className="flex items-center justify-between max-w-sm">
|
|
193
|
+
<div className="flex items-center gap-3">
|
|
194
|
+
<Sun className="h-5 w-5" />
|
|
195
|
+
<span className="text-sm">Light Mode</span>
|
|
196
|
+
</div>
|
|
197
|
+
<Switch />
|
|
198
|
+
</div>
|
|
199
|
+
<div className="flex items-center justify-between max-w-sm mt-4">
|
|
200
|
+
<div className="flex items-center gap-3">
|
|
201
|
+
<Bell className="h-5 w-5" />
|
|
202
|
+
<span className="text-sm">Notifications</span>
|
|
203
|
+
</div>
|
|
204
|
+
<Switch defaultChecked />
|
|
205
|
+
</div>
|
|
206
|
+
</CardContent>
|
|
207
|
+
</Card>
|
|
208
|
+
</TabsContent>
|
|
209
|
+
|
|
210
|
+
{/* Cards Section */}
|
|
211
|
+
<TabsContent value="cards" className="space-y-6">
|
|
212
|
+
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
213
|
+
<Card>
|
|
214
|
+
<CardHeader>
|
|
215
|
+
<CardTitle className="text-sm">Simple Card</CardTitle>
|
|
216
|
+
</CardHeader>
|
|
217
|
+
<CardContent>
|
|
218
|
+
<p className="text-xs text-muted-foreground">
|
|
219
|
+
A basic card with header and content.
|
|
220
|
+
</p>
|
|
221
|
+
</CardContent>
|
|
222
|
+
</Card>
|
|
223
|
+
|
|
224
|
+
<Card className="border-primary/30 bg-primary/5">
|
|
225
|
+
<CardHeader>
|
|
226
|
+
<CardTitle className="text-sm flex items-center gap-2">
|
|
227
|
+
<Check className="h-4 w-4 text-primary" />
|
|
228
|
+
Featured Card
|
|
229
|
+
</CardTitle>
|
|
230
|
+
</CardHeader>
|
|
231
|
+
<CardContent>
|
|
232
|
+
<p className="text-xs text-muted-foreground">
|
|
233
|
+
Card with highlighted border and background.
|
|
234
|
+
</p>
|
|
235
|
+
</CardContent>
|
|
236
|
+
</Card>
|
|
237
|
+
|
|
238
|
+
<Card>
|
|
239
|
+
<CardHeader className="pb-3">
|
|
240
|
+
<CardTitle className="text-sm">Card with Actions</CardTitle>
|
|
241
|
+
</CardHeader>
|
|
242
|
+
<CardContent className="space-y-3">
|
|
243
|
+
<p className="text-xs text-muted-foreground">
|
|
244
|
+
Card containing interactive elements.
|
|
245
|
+
</p>
|
|
246
|
+
<div className="flex gap-2">
|
|
247
|
+
<Button size="sm" variant="outline" className="text-xs flex-1">
|
|
248
|
+
Cancel
|
|
249
|
+
</Button>
|
|
250
|
+
<Button size="sm" variant="glow" className="text-xs flex-1">
|
|
251
|
+
Confirm
|
|
252
|
+
</Button>
|
|
253
|
+
</div>
|
|
254
|
+
</CardContent>
|
|
255
|
+
</Card>
|
|
256
|
+
</div>
|
|
257
|
+
</TabsContent>
|
|
258
|
+
|
|
259
|
+
{/* Feedback Section */}
|
|
260
|
+
<TabsContent value="feedback" className="space-y-6">
|
|
261
|
+
<Card>
|
|
262
|
+
<CardHeader>
|
|
263
|
+
<CardTitle className="text-base">Loading States</CardTitle>
|
|
264
|
+
</CardHeader>
|
|
265
|
+
<CardContent className="space-y-4">
|
|
266
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
267
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
268
|
+
Loading content...
|
|
269
|
+
</div>
|
|
270
|
+
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
271
|
+
<div className="h-full w-2/3 bg-primary rounded-full animate-pulse" />
|
|
272
|
+
</div>
|
|
273
|
+
</CardContent>
|
|
274
|
+
</Card>
|
|
275
|
+
|
|
276
|
+
<Card>
|
|
277
|
+
<CardHeader>
|
|
278
|
+
<CardTitle className="text-base">Empty States</CardTitle>
|
|
279
|
+
</CardHeader>
|
|
280
|
+
<CardContent className="text-center py-8">
|
|
281
|
+
<div className="h-12 w-12 rounded-full bg-muted flex items-center justify-center mx-auto mb-3">
|
|
282
|
+
<Mail className="h-6 w-6 text-muted-foreground" />
|
|
283
|
+
</div>
|
|
284
|
+
<h3 className="font-medium mb-1">No messages yet</h3>
|
|
285
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
286
|
+
Start a conversation to see messages here.
|
|
287
|
+
</p>
|
|
288
|
+
<Button size="sm" variant="outline">
|
|
289
|
+
Start Conversation
|
|
290
|
+
</Button>
|
|
291
|
+
</CardContent>
|
|
292
|
+
</Card>
|
|
293
|
+
</TabsContent>
|
|
294
|
+
</Tabs>
|
|
295
|
+
</main>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Badge } from "@/components/ui/badge";
|
|
6
|
+
import {
|
|
7
|
+
BarChart3,
|
|
8
|
+
Users,
|
|
9
|
+
DollarSign,
|
|
10
|
+
TrendingUp,
|
|
11
|
+
LayoutDashboard,
|
|
12
|
+
Settings,
|
|
13
|
+
Bell,
|
|
14
|
+
Search,
|
|
15
|
+
Download,
|
|
16
|
+
MoreHorizontal
|
|
17
|
+
} from "lucide-react";
|
|
18
|
+
import { cn } from "@/lib/utils";
|
|
19
|
+
|
|
20
|
+
interface DashboardTemplateProps {
|
|
21
|
+
preview?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function DashboardTemplate({ preview }: DashboardTemplateProps) {
|
|
25
|
+
const stats = [
|
|
26
|
+
{
|
|
27
|
+
title: "Total Revenue",
|
|
28
|
+
value: "$45,231.89",
|
|
29
|
+
change: "+20.1%",
|
|
30
|
+
icon: DollarSign,
|
|
31
|
+
trend: "up"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: "Active Users",
|
|
35
|
+
value: "2,345",
|
|
36
|
+
change: "+15.2%",
|
|
37
|
+
icon: Users,
|
|
38
|
+
trend: "up"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
title: "Sales",
|
|
42
|
+
value: "+12,234",
|
|
43
|
+
change: "+8.4%",
|
|
44
|
+
icon: BarChart3,
|
|
45
|
+
trend: "up"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: "Active Now",
|
|
49
|
+
value: "573",
|
|
50
|
+
change: "+201",
|
|
51
|
+
icon: TrendingUp,
|
|
52
|
+
trend: "up"
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const recentSales = [
|
|
57
|
+
{ name: "Olivia Martin", email: "olivia.martin@email.com", amount: "+$1,999.00" },
|
|
58
|
+
{ name: "Jackson Lee", email: "jackson.lee@email.com", amount: "+$39.00" },
|
|
59
|
+
{ name: "Isabella Nguyen", email: "isabella.nguyen@email.com", amount: "+$299.00" },
|
|
60
|
+
{ name: "William Kim", email: "will@email.com", amount: "+$99.00" },
|
|
61
|
+
{ name: "Sofia Davis", email: "sofia.davis@email.com", amount: "+$39.00" },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className="min-h-full bg-background flex">
|
|
66
|
+
{/* Sidebar */}
|
|
67
|
+
<aside className="w-14 sm:w-64 border-r border-border/50 bg-muted/30 flex-shrink-0 hidden sm:flex flex-col">
|
|
68
|
+
<div className="h-14 border-b border-border/50 flex items-center px-4">
|
|
69
|
+
<div className="h-8 w-8 rounded-lg bg-primary/20 flex items-center justify-center">
|
|
70
|
+
<LayoutDashboard className="h-4 w-4 text-primary" />
|
|
71
|
+
</div>
|
|
72
|
+
<span className="ml-3 font-semibold hidden lg:inline">Dashboard</span>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<nav className="flex-1 p-3 space-y-1">
|
|
76
|
+
{["Overview", "Analytics", "Customers", "Products", "Settings"].map((item, i) => (
|
|
77
|
+
<Button
|
|
78
|
+
key={item}
|
|
79
|
+
variant={i === 0 ? "secondary" : "ghost"}
|
|
80
|
+
className="w-full justify-start gap-3 text-xs sm:text-sm h-9"
|
|
81
|
+
size="sm"
|
|
82
|
+
>
|
|
83
|
+
<LayoutDashboard className="h-4 w-4" />
|
|
84
|
+
<span className="hidden sm:inline">{item}</span>
|
|
85
|
+
</Button>
|
|
86
|
+
))}
|
|
87
|
+
</nav>
|
|
88
|
+
</aside>
|
|
89
|
+
|
|
90
|
+
{/* Main Content */}
|
|
91
|
+
<main className="flex-1 min-w-0">
|
|
92
|
+
{/* Header */}
|
|
93
|
+
<header className="h-14 border-b border-border/50 bg-background/80 backdrop-blur-xl flex items-center justify-between px-4 sm:px-6">
|
|
94
|
+
<div className="flex items-center gap-4 flex-1">
|
|
95
|
+
<div className="relative max-w-md flex-1 hidden sm:block">
|
|
96
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
97
|
+
<input
|
|
98
|
+
type="text"
|
|
99
|
+
placeholder="Search..."
|
|
100
|
+
className="w-full pl-9 pr-4 py-2 rounded-lg border border-border/50 bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div className="flex items-center gap-2">
|
|
105
|
+
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
|
106
|
+
<Bell className="h-4 w-4" />
|
|
107
|
+
</Button>
|
|
108
|
+
<Button variant="outline" size="sm" className="gap-2 text-xs">
|
|
109
|
+
<Download className="h-3.5 w-3.5" />
|
|
110
|
+
<span className="hidden sm:inline">Export</span>
|
|
111
|
+
</Button>
|
|
112
|
+
</div>
|
|
113
|
+
</header>
|
|
114
|
+
|
|
115
|
+
{/* Dashboard Content */}
|
|
116
|
+
<div className="p-4 sm:p-6 space-y-4 sm:space-y-6">
|
|
117
|
+
{/* Page Header */}
|
|
118
|
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
|
119
|
+
<div>
|
|
120
|
+
<h1 className="text-xl sm:text-2xl font-bold">Dashboard</h1>
|
|
121
|
+
<p className="text-sm text-muted-foreground">Welcome back! Here's what's happening.</p>
|
|
122
|
+
</div>
|
|
123
|
+
<Button variant="glow" size="sm" className="w-fit text-xs">
|
|
124
|
+
Download Report
|
|
125
|
+
</Button>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
{/* Stats Grid */}
|
|
129
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4">
|
|
130
|
+
{stats.map((stat, i) => (
|
|
131
|
+
<Card key={i} className="border-border/50 bg-background/60">
|
|
132
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
133
|
+
<CardTitle className="text-xs sm:text-sm font-medium text-muted-foreground">
|
|
134
|
+
{stat.title}
|
|
135
|
+
</CardTitle>
|
|
136
|
+
<stat.icon className="h-3.5 w-3.5 sm:h-4 sm:w-4 text-muted-foreground" />
|
|
137
|
+
</CardHeader>
|
|
138
|
+
<CardContent>
|
|
139
|
+
<div className="text-lg sm:text-2xl font-bold">{stat.value}</div>
|
|
140
|
+
<p className="text-[10px] sm:text-xs text-emerald-500">
|
|
141
|
+
{stat.change} from last month
|
|
142
|
+
</p>
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
))}
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
{/* Main Grid */}
|
|
149
|
+
<div className="grid lg:grid-cols-7 gap-4 sm:gap-6">
|
|
150
|
+
{/* Chart Area */}
|
|
151
|
+
<Card className="lg:col-span-4 border-border/50">
|
|
152
|
+
<CardHeader className="flex flex-row items-center justify-between">
|
|
153
|
+
<CardTitle className="text-sm sm:text-base">Overview</CardTitle>
|
|
154
|
+
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
|
155
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
156
|
+
</Button>
|
|
157
|
+
</CardHeader>
|
|
158
|
+
<CardContent>
|
|
159
|
+
{/* Chart Placeholder */}
|
|
160
|
+
<div className="h-[200px] sm:h-[300px] flex items-end gap-2">
|
|
161
|
+
{[40, 30, 50, 45, 60, 55, 70, 65, 80, 75, 85, 90].map((h, i) => (
|
|
162
|
+
<div
|
|
163
|
+
key={i}
|
|
164
|
+
className="flex-1 bg-primary/20 rounded-t-sm relative group"
|
|
165
|
+
style={{ height: `${h}%` }}
|
|
166
|
+
>
|
|
167
|
+
<div
|
|
168
|
+
className="absolute bottom-0 left-0 right-0 bg-primary rounded-t-sm transition-all duration-300 group-hover:bg-primary/80"
|
|
169
|
+
style={{ height: `${h * 0.7}%` }}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
</CardContent>
|
|
175
|
+
</Card>
|
|
176
|
+
|
|
177
|
+
{/* Recent Sales */}
|
|
178
|
+
<Card className="lg:col-span-3 border-border/50">
|
|
179
|
+
<CardHeader>
|
|
180
|
+
<CardTitle className="text-sm sm:text-base">Recent Sales</CardTitle>
|
|
181
|
+
<p className="text-xs text-muted-foreground">You made 265 sales this month.</p>
|
|
182
|
+
</CardHeader>
|
|
183
|
+
<CardContent className="space-y-3 sm:space-y-4">
|
|
184
|
+
{recentSales.map((sale, i) => (
|
|
185
|
+
<div key={i} className="flex items-center gap-3">
|
|
186
|
+
<div className="h-8 w-8 sm:h-9 sm:w-9 rounded-full bg-muted flex items-center justify-center text-xs font-medium">
|
|
187
|
+
{sale.name.split(" ").map(n => n[0]).join("")}
|
|
188
|
+
</div>
|
|
189
|
+
<div className="flex-1 min-w-0">
|
|
190
|
+
<p className="text-xs sm:text-sm font-medium truncate">{sale.name}</p>
|
|
191
|
+
<p className="text-[10px] sm:text-xs text-muted-foreground truncate">{sale.email}</p>
|
|
192
|
+
</div>
|
|
193
|
+
<div className="text-xs sm:text-sm font-medium text-emerald-500">{sale.amount}</div>
|
|
194
|
+
</div>
|
|
195
|
+
))}
|
|
196
|
+
</CardContent>
|
|
197
|
+
</Card>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
{/* Recent Orders Table */}
|
|
201
|
+
<Card className="border-border/50">
|
|
202
|
+
<CardHeader>
|
|
203
|
+
<CardTitle className="text-sm sm:text-base">Recent Orders</CardTitle>
|
|
204
|
+
</CardHeader>
|
|
205
|
+
<CardContent>
|
|
206
|
+
<div className="overflow-x-auto">
|
|
207
|
+
<table className="w-full">
|
|
208
|
+
<thead>
|
|
209
|
+
<tr className="border-b border-border/50">
|
|
210
|
+
<th className="text-left py-2 px-2 text-xs font-medium text-muted-foreground">Order</th>
|
|
211
|
+
<th className="text-left py-2 px-2 text-xs font-medium text-muted-foreground hidden sm:table-cell">Status</th>
|
|
212
|
+
<th className="text-left py-2 px-2 text-xs font-medium text-muted-foreground hidden md:table-cell">Date</th>
|
|
213
|
+
<th className="text-right py-2 px-2 text-xs font-medium text-muted-foreground">Amount</th>
|
|
214
|
+
</tr>
|
|
215
|
+
</thead>
|
|
216
|
+
<tbody>
|
|
217
|
+
{[
|
|
218
|
+
{ id: "#3210", status: "Completed", date: "2024-01-15", amount: "$42.50" },
|
|
219
|
+
{ id: "#3209", status: "Pending", date: "2024-01-14", amount: "$125.00" },
|
|
220
|
+
{ id: "#3208", status: "Processing", date: "2024-01-14", amount: "$75.25" },
|
|
221
|
+
{ id: "#3207", status: "Completed", date: "2024-01-13", amount: "$32.00" },
|
|
222
|
+
].map((order, i) => (
|
|
223
|
+
<tr key={i} className="border-b border-border/50 last:border-0">
|
|
224
|
+
<td className="py-3 px-2 text-xs sm:text-sm">{order.id}</td>
|
|
225
|
+
<td className="py-3 px-2 hidden sm:table-cell">
|
|
226
|
+
<Badge
|
|
227
|
+
variant={order.status === "Completed" ? "default" : order.status === "Pending" ? "glass" : "outline"}
|
|
228
|
+
className="text-[10px]"
|
|
229
|
+
>
|
|
230
|
+
{order.status}
|
|
231
|
+
</Badge>
|
|
232
|
+
</td>
|
|
233
|
+
<td className="py-3 px-2 text-xs text-muted-foreground hidden md:table-cell">{order.date}</td>
|
|
234
|
+
<td className="py-3 px-2 text-xs sm:text-sm text-right font-medium">{order.amount}</td>
|
|
235
|
+
</tr>
|
|
236
|
+
))}
|
|
237
|
+
</tbody>
|
|
238
|
+
</table>
|
|
239
|
+
</div>
|
|
240
|
+
</CardContent>
|
|
241
|
+
</Card>
|
|
242
|
+
</div>
|
|
243
|
+
</main>
|
|
244
|
+
</div>
|
|
245
|
+
);
|
|
246
|
+
}
|