nextworks 0.2.0-alpha.11 → 0.2.0-alpha.13
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/README.md +283 -282
- package/dist/cli_manifests/blocks_manifest.json +198 -175
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +101 -100
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +105 -104
- package/dist/kits/blocks/.nextworks/docs/THEME_GUIDE.md +1 -1
- package/dist/kits/blocks/app/templates/aiworkflow/PresetThemeVars.tsx +58 -0
- package/dist/kits/blocks/app/templates/aiworkflow/README.md +46 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/CTA.tsx +44 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Contact.tsx +105 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/FAQ.tsx +63 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Features.tsx +65 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Footer.tsx +109 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Hero.tsx +636 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Navbar.tsx +90 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Pricing.tsx +86 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/ProcessTimeline.tsx +103 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/Testimonials.tsx +56 -0
- package/dist/kits/blocks/app/templates/aiworkflow/components/TrustBadges.tsx +59 -0
- package/dist/kits/blocks/app/templates/aiworkflow/page.tsx +43 -0
- package/dist/kits/blocks/app/templates/digitalagency/PresetThemeVars.tsx +80 -80
- package/dist/kits/blocks/app/templates/digitalagency/README.md +42 -42
- package/dist/kits/blocks/app/templates/digitalagency/components/Pricing.tsx +114 -114
- package/dist/kits/blocks/app/templates/digitalagency/components/Process.tsx +59 -59
- package/dist/kits/blocks/app/templates/digitalagency/components/Services.tsx +55 -55
- package/dist/kits/blocks/app/templates/digitalagency/components/Team.tsx +28 -28
- package/dist/kits/blocks/app/templates/digitalagency/components/Testimonials.tsx +65 -65
- package/dist/kits/blocks/app/templates/digitalagency/page.tsx +38 -38
- package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +84 -84
- package/dist/kits/blocks/app/templates/productlaunch/PresetThemeVars.tsx +75 -75
- package/dist/kits/blocks/app/templates/productlaunch/README.md +62 -62
- package/dist/kits/blocks/app/templates/productlaunch/components/About.tsx +84 -84
- package/dist/kits/blocks/app/templates/productlaunch/components/CTA.tsx +50 -50
- package/dist/kits/blocks/app/templates/productlaunch/components/Contact.tsx +231 -231
- package/dist/kits/blocks/app/templates/productlaunch/components/FAQ.tsx +86 -86
- package/dist/kits/blocks/app/templates/productlaunch/components/Features.tsx +83 -83
- package/dist/kits/blocks/app/templates/productlaunch/components/Footer.tsx +132 -132
- package/dist/kits/blocks/app/templates/productlaunch/components/Hero.tsx +88 -88
- package/dist/kits/blocks/app/templates/productlaunch/components/Navbar.tsx +116 -116
- package/dist/kits/blocks/app/templates/productlaunch/components/Pricing.tsx +106 -106
- package/dist/kits/blocks/app/templates/productlaunch/components/ProcessTimeline.tsx +110 -110
- package/dist/kits/blocks/app/templates/productlaunch/components/ServicesGrid.tsx +68 -68
- package/dist/kits/blocks/app/templates/productlaunch/components/Team.tsx +104 -104
- package/dist/kits/blocks/app/templates/productlaunch/components/Testimonials.tsx +90 -90
- package/dist/kits/blocks/app/templates/productlaunch/components/TrustBadges.tsx +76 -76
- package/dist/kits/blocks/app/templates/productlaunch/page.tsx +43 -43
- package/dist/kits/blocks/app/templates/saasdashboard/PresetThemeVars.tsx +80 -80
- package/dist/kits/blocks/app/templates/saasdashboard/README.md +44 -44
- package/dist/kits/blocks/app/templates/saasdashboard/components/Contact.tsx +129 -129
- package/dist/kits/blocks/app/templates/saasdashboard/components/Dashboard.tsx +293 -293
- package/dist/kits/blocks/app/templates/saasdashboard/components/FAQ.tsx +55 -55
- package/dist/kits/blocks/app/templates/saasdashboard/components/Features.tsx +90 -90
- package/dist/kits/blocks/app/templates/saasdashboard/components/Footer.tsx +77 -77
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero.tsx +104 -104
- package/dist/kits/blocks/app/templates/saasdashboard/components/Hero_mask.tsx +126 -126
- package/dist/kits/blocks/app/templates/saasdashboard/components/Navbar.tsx +117 -117
- package/dist/kits/blocks/app/templates/saasdashboard/components/Pricing.tsx +90 -90
- package/dist/kits/blocks/app/templates/saasdashboard/components/SmoothScroll.tsx +96 -96
- package/dist/kits/blocks/app/templates/saasdashboard/components/Testimonials.tsx +72 -72
- package/dist/kits/blocks/app/templates/saasdashboard/components/TrustBadges.tsx +53 -53
- package/dist/kits/blocks/app/templates/saasdashboard/page.tsx +39 -39
- package/dist/kits/blocks/components/enhanced-theme-provider.tsx +195 -195
- package/dist/kits/blocks/components/providers/BlocksAppProviders.tsx +27 -27
- package/dist/kits/blocks/components/sections/About.tsx +291 -291
- package/dist/kits/blocks/components/sections/CTA.tsx +257 -257
- package/dist/kits/blocks/components/sections/Contact.tsx +267 -267
- package/dist/kits/blocks/components/sections/FAQ.tsx +214 -214
- package/dist/kits/blocks/components/sections/Features.tsx +268 -268
- package/dist/kits/blocks/components/sections/Footer.tsx +302 -302
- package/dist/kits/blocks/components/sections/HeroMotion.tsx +308 -308
- package/dist/kits/blocks/components/sections/HeroOverlay.tsx +358 -358
- package/dist/kits/blocks/components/sections/HeroProductDemo.tsx +236 -0
- package/dist/kits/blocks/components/sections/HeroSplit.tsx +352 -352
- package/dist/kits/blocks/components/sections/Navbar.tsx +350 -350
- package/dist/kits/blocks/components/sections/PortfolioSimple.tsx +549 -549
- package/dist/kits/blocks/components/sections/Pricing.tsx +264 -264
- package/dist/kits/blocks/components/sections/ProcessTimeline.tsx +325 -325
- package/dist/kits/blocks/components/sections/ServicesGrid.tsx +210 -210
- package/dist/kits/blocks/components/sections/Team.tsx +309 -309
- package/dist/kits/blocks/components/sections/Testimonials.tsx +158 -158
- package/dist/kits/blocks/components/sections/TrustBadges.tsx +162 -162
- package/dist/kits/blocks/components/sections/product-demo/ApprovalInboxPanel.tsx +125 -0
- package/dist/kits/blocks/components/sections/product-demo/DemoStage.tsx +397 -0
- package/dist/kits/blocks/components/sections/product-demo/DemoWindow.tsx +128 -0
- package/dist/kits/blocks/components/sections/product-demo/KnowledgePanel.tsx +127 -0
- package/dist/kits/blocks/components/sections/product-demo/RunConsolePanel.tsx +150 -0
- package/dist/kits/blocks/components/sections/product-demo/WorkflowStudioPanel.tsx +191 -0
- package/dist/kits/blocks/components/sections/product-demo/types.ts +193 -0
- package/dist/kits/blocks/components/theme-provider.tsx +1 -1
- package/dist/kits/blocks/components/ui/alert-dialog.tsx +134 -134
- package/dist/kits/blocks/components/ui/brand-node.tsx +121 -121
- package/dist/kits/blocks/components/ui/button.tsx +122 -122
- package/dist/kits/blocks/components/ui/card.tsx +95 -95
- package/dist/kits/blocks/components/ui/checkbox.tsx +30 -30
- package/dist/kits/blocks/components/ui/cta-button.tsx +125 -125
- package/dist/kits/blocks/components/ui/dropdown-menu.tsx +201 -201
- package/dist/kits/blocks/components/ui/feature-card.tsx +91 -91
- package/dist/kits/blocks/components/ui/input.tsx +27 -27
- package/dist/kits/blocks/components/ui/label.tsx +29 -29
- package/dist/kits/blocks/components/ui/pricing-card.tsx +120 -120
- package/dist/kits/blocks/components/ui/select.tsx +25 -25
- package/dist/kits/blocks/components/ui/skeleton.tsx +13 -13
- package/dist/kits/blocks/components/ui/table.tsx +98 -98
- package/dist/kits/blocks/components/ui/testimonial-card.tsx +108 -108
- package/dist/kits/blocks/components/ui/textarea.tsx +26 -26
- package/dist/kits/blocks/components/ui/theme-selector.tsx +243 -243
- package/dist/kits/blocks/components/ui/theme-toggle.tsx +74 -74
- package/dist/kits/blocks/components/ui/toaster.tsx +7 -7
- package/dist/kits/blocks/lib/themes.ts +400 -400
- package/dist/kits/blocks/lib/utils.ts +6 -6
- package/dist/kits/blocks/package-deps.json +3 -3
- package/package.json +1 -1
|
@@ -1,293 +1,293 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
4
|
-
|
|
5
|
-
type FloatingCard = {
|
|
6
|
-
position: string;
|
|
7
|
-
icon: string;
|
|
8
|
-
text: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
type Stat = {
|
|
12
|
-
value: string;
|
|
13
|
-
label: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
interface DashboardProps {
|
|
17
|
-
stats: Stat[];
|
|
18
|
-
projects: string[];
|
|
19
|
-
floatingCards: FloatingCard[];
|
|
20
|
-
activeTab?: string;
|
|
21
|
-
navItems?: string[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function Dashboard({
|
|
25
|
-
stats,
|
|
26
|
-
projects,
|
|
27
|
-
floatingCards,
|
|
28
|
-
activeTab = "Analytics",
|
|
29
|
-
navItems = ["Analytics", "Projects", "Team"],
|
|
30
|
-
}: DashboardProps) {
|
|
31
|
-
return (
|
|
32
|
-
<div className="relative isolate">
|
|
33
|
-
<DashboardMockup
|
|
34
|
-
stats={stats}
|
|
35
|
-
projects={projects}
|
|
36
|
-
activeTab={activeTab}
|
|
37
|
-
navItems={navItems}
|
|
38
|
-
/>
|
|
39
|
-
|
|
40
|
-
{floatingCards.map(({ position, icon, text }) => (
|
|
41
|
-
<FloatingCard
|
|
42
|
-
key={position}
|
|
43
|
-
position={position}
|
|
44
|
-
icon={icon}
|
|
45
|
-
text={text}
|
|
46
|
-
/>
|
|
47
|
-
))}
|
|
48
|
-
|
|
49
|
-
<style jsx>{`
|
|
50
|
-
.dashboard-mockup {
|
|
51
|
-
transform: perspective(1000px) rotateY(-5deg) rotateX(5deg);
|
|
52
|
-
transition: transform 0.3s ease;
|
|
53
|
-
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
|
|
54
|
-
}
|
|
55
|
-
.dashboard-mockup:hover {
|
|
56
|
-
transform: perspective(1000px) rotateY(-2deg) rotateX(2deg);
|
|
57
|
-
}
|
|
58
|
-
.trend-line {
|
|
59
|
-
clip-path: polygon(
|
|
60
|
-
0 100%,
|
|
61
|
-
20% 80%,
|
|
62
|
-
40% 85%,
|
|
63
|
-
60% 60%,
|
|
64
|
-
80% 50%,
|
|
65
|
-
100% 20%
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
@keyframes growUp {
|
|
69
|
-
0% {
|
|
70
|
-
height: 0;
|
|
71
|
-
opacity: 0;
|
|
72
|
-
}
|
|
73
|
-
100% {
|
|
74
|
-
opacity: 1;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
@keyframes floatAnim {
|
|
78
|
-
0%,
|
|
79
|
-
100% {
|
|
80
|
-
transform: translateY(0px);
|
|
81
|
-
}
|
|
82
|
-
50% {
|
|
83
|
-
transform: translateY(-20px);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
`}</style>
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function DashboardMockup({
|
|
92
|
-
stats,
|
|
93
|
-
projects,
|
|
94
|
-
activeTab,
|
|
95
|
-
navItems,
|
|
96
|
-
}: {
|
|
97
|
-
stats: Stat[];
|
|
98
|
-
projects: string[];
|
|
99
|
-
activeTab?: string;
|
|
100
|
-
navItems?: string[];
|
|
101
|
-
}) {
|
|
102
|
-
const [tilt, setTilt] = useState({ rx: 0, ry: 0 });
|
|
103
|
-
const targetTilt = useRef({ rx: 0, ry: 0 });
|
|
104
|
-
const rafId = useRef<number | null>(null);
|
|
105
|
-
|
|
106
|
-
const animate = () => {
|
|
107
|
-
setTilt((prev) => {
|
|
108
|
-
const smoothing = 0.15;
|
|
109
|
-
const nextRx = prev.rx + (targetTilt.current.rx - prev.rx) * smoothing;
|
|
110
|
-
const nextRy = prev.ry + (targetTilt.current.ry - prev.ry) * smoothing;
|
|
111
|
-
return { rx: nextRx, ry: nextRy };
|
|
112
|
-
});
|
|
113
|
-
rafId.current = requestAnimationFrame(animate);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const handleMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
|
|
117
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
118
|
-
const x = e.clientX - rect.left;
|
|
119
|
-
const y = e.clientY - rect.top;
|
|
120
|
-
const rx = (y / rect.height - 0.5) * -6;
|
|
121
|
-
const ry = (x / rect.width - 0.5) * 6;
|
|
122
|
-
targetTilt.current = { rx, ry };
|
|
123
|
-
if (rafId.current == null) rafId.current = requestAnimationFrame(animate);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const handleLeave = () => {
|
|
127
|
-
targetTilt.current = { rx: 0, ry: 0 };
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
return () => {
|
|
132
|
-
if (rafId.current != null) cancelAnimationFrame(rafId.current);
|
|
133
|
-
};
|
|
134
|
-
}, []);
|
|
135
|
-
return (
|
|
136
|
-
<div
|
|
137
|
-
className={
|
|
138
|
-
"dashboard-mockup transform-gpu rounded-2xl border border-blue-500/20 p-6 shadow-2xl ring-1 ring-blue-500/10 backdrop-blur transition-transform duration-150 ease-out motion-reduce:transition-none " +
|
|
139
|
-
"bg-slate-900/80 dark:bg-slate-900/80"
|
|
140
|
-
}
|
|
141
|
-
style={{
|
|
142
|
-
willChange: "transform",
|
|
143
|
-
transform: `perspective(1000px) rotateX(${tilt.rx}deg) rotateY(${tilt.ry}deg)`,
|
|
144
|
-
}}
|
|
145
|
-
onMouseMove={handleMove}
|
|
146
|
-
onMouseLeave={handleLeave}
|
|
147
|
-
>
|
|
148
|
-
<DashboardHeader activeTab={activeTab} navItems={navItems} />
|
|
149
|
-
<DashboardGrid stats={stats} projects={projects} />
|
|
150
|
-
</div>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function DashboardHeader({
|
|
155
|
-
activeTab = "Analytics",
|
|
156
|
-
navItems = ["Analytics", "Projects", "Team"],
|
|
157
|
-
}: {
|
|
158
|
-
activeTab?: string;
|
|
159
|
-
navItems?: string[];
|
|
160
|
-
}) {
|
|
161
|
-
return (
|
|
162
|
-
<div className="mb-6 flex items-center justify-between border-b border-blue-500/10 pb-4">
|
|
163
|
-
<div className="text-[1.1rem] font-semibold text-slate-200">
|
|
164
|
-
Business Overview
|
|
165
|
-
</div>
|
|
166
|
-
<div className="hidden gap-3 md:flex">
|
|
167
|
-
{navItems.map((item) => (
|
|
168
|
-
<div
|
|
169
|
-
key={item}
|
|
170
|
-
className={
|
|
171
|
-
"rounded-md px-3 py-2 text-sm " +
|
|
172
|
-
(item === activeTab
|
|
173
|
-
? "bg-gradient-to-br from-blue-500 to-purple-500 text-white"
|
|
174
|
-
: "bg-blue-500/10 text-blue-400")
|
|
175
|
-
}
|
|
176
|
-
>
|
|
177
|
-
{item}
|
|
178
|
-
</div>
|
|
179
|
-
))}
|
|
180
|
-
</div>
|
|
181
|
-
</div>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function DashboardGrid({
|
|
186
|
-
stats,
|
|
187
|
-
projects,
|
|
188
|
-
}: {
|
|
189
|
-
stats: Stat[];
|
|
190
|
-
projects: string[];
|
|
191
|
-
}) {
|
|
192
|
-
return (
|
|
193
|
-
<div className="mb-4 grid grid-cols-[2fr_1fr] gap-4">
|
|
194
|
-
<ChartArea />
|
|
195
|
-
<StatsPanel stats={stats} />
|
|
196
|
-
<ProjectList projects={projects} />
|
|
197
|
-
</div>
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function ChartArea() {
|
|
202
|
-
const barHeights = [30, 45, 35, 60, 55, 75, 70, 85, 90, 100];
|
|
203
|
-
return (
|
|
204
|
-
<div className="relative overflow-hidden rounded-lg border border-blue-500/10 bg-slate-900/50 p-4">
|
|
205
|
-
<div className="mb-2 text-sm text-slate-300">Revenue Growth</div>
|
|
206
|
-
<div
|
|
207
|
-
className="relative h-32 overflow-hidden rounded-md"
|
|
208
|
-
style={{
|
|
209
|
-
background:
|
|
210
|
-
"linear-gradient(45deg, transparent 20%, rgba(59,130,246,0.1) 20%, rgba(59,130,246,0.1) 40%, transparent 40%, transparent 60%, rgba(139,92,246,0.1) 60%, rgba(139,92,246,0.1) 80%, transparent 80%)",
|
|
211
|
-
}}
|
|
212
|
-
>
|
|
213
|
-
<div className="absolute inset-[10px] flex items-end gap-[3px]">
|
|
214
|
-
{barHeights.map((h, i) => (
|
|
215
|
-
<div
|
|
216
|
-
key={i}
|
|
217
|
-
className="flex-1 rounded-t bg-gradient-to-b from-blue-500 to-purple-500"
|
|
218
|
-
style={{
|
|
219
|
-
minHeight: "20px",
|
|
220
|
-
height: `${h}%`,
|
|
221
|
-
animation: "growUp 2s ease-out both",
|
|
222
|
-
animationDelay: `${(i + 1) * 0.1}s`,
|
|
223
|
-
}}
|
|
224
|
-
/>
|
|
225
|
-
))}
|
|
226
|
-
</div>
|
|
227
|
-
<div className="trend-line absolute top-5 right-[5%] left-[5%] h-[2px] rounded bg-gradient-to-r from-emerald-500 to-blue-500 opacity-80" />
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function StatsPanel({ stats }: { stats: Stat[] }) {
|
|
234
|
-
return (
|
|
235
|
-
<div className="flex flex-col gap-2">
|
|
236
|
-
{stats.map((s, i) => (
|
|
237
|
-
<div
|
|
238
|
-
key={i}
|
|
239
|
-
className="rounded-lg border border-blue-500/10 bg-slate-900/50 p-3"
|
|
240
|
-
>
|
|
241
|
-
<div className="text-lg font-bold text-blue-500">{s.value}</div>
|
|
242
|
-
<div className="text-[0.7rem] text-slate-400 uppercase">
|
|
243
|
-
{s.label}
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
))}
|
|
247
|
-
</div>
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function ProjectList({ projects }: { projects: string[] }) {
|
|
252
|
-
return (
|
|
253
|
-
<div className="col-span-2 rounded-lg border border-blue-500/10 bg-slate-900/50 p-4">
|
|
254
|
-
<div className="mb-2 text-sm text-slate-300">Active Projects</div>
|
|
255
|
-
<div className="flex flex-wrap gap-2">
|
|
256
|
-
{projects.map((p, i) => (
|
|
257
|
-
<div
|
|
258
|
-
key={i}
|
|
259
|
-
className="rounded border border-blue-500/30 bg-gradient-to-br from-blue-500/20 to-purple-500/10 px-2 py-2 text-[0.8rem] text-slate-200"
|
|
260
|
-
>
|
|
261
|
-
{p}
|
|
262
|
-
</div>
|
|
263
|
-
))}
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function FloatingCard({ position, icon, text }: FloatingCard) {
|
|
270
|
-
const positionClasses =
|
|
271
|
-
position === "card-1"
|
|
272
|
-
? "top-[10%] right-[6%]"
|
|
273
|
-
: position === "card-2"
|
|
274
|
-
? "bottom-[20%] left-[2%]"
|
|
275
|
-
: "top-0 left-0";
|
|
276
|
-
|
|
277
|
-
return (
|
|
278
|
-
<div
|
|
279
|
-
className={[
|
|
280
|
-
"absolute z-10 transform-gpu rounded-lg border border-blue-500/20 p-4 shadow-lg",
|
|
281
|
-
"animate-[floatAnim_6s_ease-in-out_infinite]",
|
|
282
|
-
"bg-slate-800 dark:bg-slate-900",
|
|
283
|
-
positionClasses,
|
|
284
|
-
].join(" ")}
|
|
285
|
-
style={{ willChange: "transform" }}
|
|
286
|
-
>
|
|
287
|
-
<div className="mb-2 flex h-[30px] w-[30px] items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-purple-500 text-sm text-white">
|
|
288
|
-
{icon}
|
|
289
|
-
</div>
|
|
290
|
-
<div className="text-sm text-slate-300">{text}</div>
|
|
291
|
-
</div>
|
|
292
|
-
);
|
|
293
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
type FloatingCard = {
|
|
6
|
+
position: string;
|
|
7
|
+
icon: string;
|
|
8
|
+
text: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type Stat = {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface DashboardProps {
|
|
17
|
+
stats: Stat[];
|
|
18
|
+
projects: string[];
|
|
19
|
+
floatingCards: FloatingCard[];
|
|
20
|
+
activeTab?: string;
|
|
21
|
+
navItems?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function Dashboard({
|
|
25
|
+
stats,
|
|
26
|
+
projects,
|
|
27
|
+
floatingCards,
|
|
28
|
+
activeTab = "Analytics",
|
|
29
|
+
navItems = ["Analytics", "Projects", "Team"],
|
|
30
|
+
}: DashboardProps) {
|
|
31
|
+
return (
|
|
32
|
+
<div className="relative isolate">
|
|
33
|
+
<DashboardMockup
|
|
34
|
+
stats={stats}
|
|
35
|
+
projects={projects}
|
|
36
|
+
activeTab={activeTab}
|
|
37
|
+
navItems={navItems}
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
{floatingCards.map(({ position, icon, text }) => (
|
|
41
|
+
<FloatingCard
|
|
42
|
+
key={position}
|
|
43
|
+
position={position}
|
|
44
|
+
icon={icon}
|
|
45
|
+
text={text}
|
|
46
|
+
/>
|
|
47
|
+
))}
|
|
48
|
+
|
|
49
|
+
<style jsx>{`
|
|
50
|
+
.dashboard-mockup {
|
|
51
|
+
transform: perspective(1000px) rotateY(-5deg) rotateX(5deg);
|
|
52
|
+
transition: transform 0.3s ease;
|
|
53
|
+
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
|
|
54
|
+
}
|
|
55
|
+
.dashboard-mockup:hover {
|
|
56
|
+
transform: perspective(1000px) rotateY(-2deg) rotateX(2deg);
|
|
57
|
+
}
|
|
58
|
+
.trend-line {
|
|
59
|
+
clip-path: polygon(
|
|
60
|
+
0 100%,
|
|
61
|
+
20% 80%,
|
|
62
|
+
40% 85%,
|
|
63
|
+
60% 60%,
|
|
64
|
+
80% 50%,
|
|
65
|
+
100% 20%
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
@keyframes growUp {
|
|
69
|
+
0% {
|
|
70
|
+
height: 0;
|
|
71
|
+
opacity: 0;
|
|
72
|
+
}
|
|
73
|
+
100% {
|
|
74
|
+
opacity: 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
@keyframes floatAnim {
|
|
78
|
+
0%,
|
|
79
|
+
100% {
|
|
80
|
+
transform: translateY(0px);
|
|
81
|
+
}
|
|
82
|
+
50% {
|
|
83
|
+
transform: translateY(-20px);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`}</style>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function DashboardMockup({
|
|
92
|
+
stats,
|
|
93
|
+
projects,
|
|
94
|
+
activeTab,
|
|
95
|
+
navItems,
|
|
96
|
+
}: {
|
|
97
|
+
stats: Stat[];
|
|
98
|
+
projects: string[];
|
|
99
|
+
activeTab?: string;
|
|
100
|
+
navItems?: string[];
|
|
101
|
+
}) {
|
|
102
|
+
const [tilt, setTilt] = useState({ rx: 0, ry: 0 });
|
|
103
|
+
const targetTilt = useRef({ rx: 0, ry: 0 });
|
|
104
|
+
const rafId = useRef<number | null>(null);
|
|
105
|
+
|
|
106
|
+
const animate = () => {
|
|
107
|
+
setTilt((prev) => {
|
|
108
|
+
const smoothing = 0.15;
|
|
109
|
+
const nextRx = prev.rx + (targetTilt.current.rx - prev.rx) * smoothing;
|
|
110
|
+
const nextRy = prev.ry + (targetTilt.current.ry - prev.ry) * smoothing;
|
|
111
|
+
return { rx: nextRx, ry: nextRy };
|
|
112
|
+
});
|
|
113
|
+
rafId.current = requestAnimationFrame(animate);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
|
|
117
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
118
|
+
const x = e.clientX - rect.left;
|
|
119
|
+
const y = e.clientY - rect.top;
|
|
120
|
+
const rx = (y / rect.height - 0.5) * -6;
|
|
121
|
+
const ry = (x / rect.width - 0.5) * 6;
|
|
122
|
+
targetTilt.current = { rx, ry };
|
|
123
|
+
if (rafId.current == null) rafId.current = requestAnimationFrame(animate);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const handleLeave = () => {
|
|
127
|
+
targetTilt.current = { rx: 0, ry: 0 };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
return () => {
|
|
132
|
+
if (rafId.current != null) cancelAnimationFrame(rafId.current);
|
|
133
|
+
};
|
|
134
|
+
}, []);
|
|
135
|
+
return (
|
|
136
|
+
<div
|
|
137
|
+
className={
|
|
138
|
+
"dashboard-mockup transform-gpu rounded-2xl border border-blue-500/20 p-6 shadow-2xl ring-1 ring-blue-500/10 backdrop-blur transition-transform duration-150 ease-out motion-reduce:transition-none " +
|
|
139
|
+
"bg-slate-900/80 dark:bg-slate-900/80"
|
|
140
|
+
}
|
|
141
|
+
style={{
|
|
142
|
+
willChange: "transform",
|
|
143
|
+
transform: `perspective(1000px) rotateX(${tilt.rx}deg) rotateY(${tilt.ry}deg)`,
|
|
144
|
+
}}
|
|
145
|
+
onMouseMove={handleMove}
|
|
146
|
+
onMouseLeave={handleLeave}
|
|
147
|
+
>
|
|
148
|
+
<DashboardHeader activeTab={activeTab} navItems={navItems} />
|
|
149
|
+
<DashboardGrid stats={stats} projects={projects} />
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function DashboardHeader({
|
|
155
|
+
activeTab = "Analytics",
|
|
156
|
+
navItems = ["Analytics", "Projects", "Team"],
|
|
157
|
+
}: {
|
|
158
|
+
activeTab?: string;
|
|
159
|
+
navItems?: string[];
|
|
160
|
+
}) {
|
|
161
|
+
return (
|
|
162
|
+
<div className="mb-6 flex items-center justify-between border-b border-blue-500/10 pb-4">
|
|
163
|
+
<div className="text-[1.1rem] font-semibold text-slate-200">
|
|
164
|
+
Business Overview
|
|
165
|
+
</div>
|
|
166
|
+
<div className="hidden gap-3 md:flex">
|
|
167
|
+
{navItems.map((item) => (
|
|
168
|
+
<div
|
|
169
|
+
key={item}
|
|
170
|
+
className={
|
|
171
|
+
"rounded-md px-3 py-2 text-sm " +
|
|
172
|
+
(item === activeTab
|
|
173
|
+
? "bg-gradient-to-br from-blue-500 to-purple-500 text-white"
|
|
174
|
+
: "bg-blue-500/10 text-blue-400")
|
|
175
|
+
}
|
|
176
|
+
>
|
|
177
|
+
{item}
|
|
178
|
+
</div>
|
|
179
|
+
))}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function DashboardGrid({
|
|
186
|
+
stats,
|
|
187
|
+
projects,
|
|
188
|
+
}: {
|
|
189
|
+
stats: Stat[];
|
|
190
|
+
projects: string[];
|
|
191
|
+
}) {
|
|
192
|
+
return (
|
|
193
|
+
<div className="mb-4 grid grid-cols-[2fr_1fr] gap-4">
|
|
194
|
+
<ChartArea />
|
|
195
|
+
<StatsPanel stats={stats} />
|
|
196
|
+
<ProjectList projects={projects} />
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function ChartArea() {
|
|
202
|
+
const barHeights = [30, 45, 35, 60, 55, 75, 70, 85, 90, 100];
|
|
203
|
+
return (
|
|
204
|
+
<div className="relative overflow-hidden rounded-lg border border-blue-500/10 bg-slate-900/50 p-4">
|
|
205
|
+
<div className="mb-2 text-sm text-slate-300">Revenue Growth</div>
|
|
206
|
+
<div
|
|
207
|
+
className="relative h-32 overflow-hidden rounded-md"
|
|
208
|
+
style={{
|
|
209
|
+
background:
|
|
210
|
+
"linear-gradient(45deg, transparent 20%, rgba(59,130,246,0.1) 20%, rgba(59,130,246,0.1) 40%, transparent 40%, transparent 60%, rgba(139,92,246,0.1) 60%, rgba(139,92,246,0.1) 80%, transparent 80%)",
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
<div className="absolute inset-[10px] flex items-end gap-[3px]">
|
|
214
|
+
{barHeights.map((h, i) => (
|
|
215
|
+
<div
|
|
216
|
+
key={i}
|
|
217
|
+
className="flex-1 rounded-t bg-gradient-to-b from-blue-500 to-purple-500"
|
|
218
|
+
style={{
|
|
219
|
+
minHeight: "20px",
|
|
220
|
+
height: `${h}%`,
|
|
221
|
+
animation: "growUp 2s ease-out both",
|
|
222
|
+
animationDelay: `${(i + 1) * 0.1}s`,
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
))}
|
|
226
|
+
</div>
|
|
227
|
+
<div className="trend-line absolute top-5 right-[5%] left-[5%] h-[2px] rounded bg-gradient-to-r from-emerald-500 to-blue-500 opacity-80" />
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function StatsPanel({ stats }: { stats: Stat[] }) {
|
|
234
|
+
return (
|
|
235
|
+
<div className="flex flex-col gap-2">
|
|
236
|
+
{stats.map((s, i) => (
|
|
237
|
+
<div
|
|
238
|
+
key={i}
|
|
239
|
+
className="rounded-lg border border-blue-500/10 bg-slate-900/50 p-3"
|
|
240
|
+
>
|
|
241
|
+
<div className="text-lg font-bold text-blue-500">{s.value}</div>
|
|
242
|
+
<div className="text-[0.7rem] text-slate-400 uppercase">
|
|
243
|
+
{s.label}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function ProjectList({ projects }: { projects: string[] }) {
|
|
252
|
+
return (
|
|
253
|
+
<div className="col-span-2 rounded-lg border border-blue-500/10 bg-slate-900/50 p-4">
|
|
254
|
+
<div className="mb-2 text-sm text-slate-300">Active Projects</div>
|
|
255
|
+
<div className="flex flex-wrap gap-2">
|
|
256
|
+
{projects.map((p, i) => (
|
|
257
|
+
<div
|
|
258
|
+
key={i}
|
|
259
|
+
className="rounded border border-blue-500/30 bg-gradient-to-br from-blue-500/20 to-purple-500/10 px-2 py-2 text-[0.8rem] text-slate-200"
|
|
260
|
+
>
|
|
261
|
+
{p}
|
|
262
|
+
</div>
|
|
263
|
+
))}
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function FloatingCard({ position, icon, text }: FloatingCard) {
|
|
270
|
+
const positionClasses =
|
|
271
|
+
position === "card-1"
|
|
272
|
+
? "top-[10%] right-[6%]"
|
|
273
|
+
: position === "card-2"
|
|
274
|
+
? "bottom-[20%] left-[2%]"
|
|
275
|
+
: "top-0 left-0";
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<div
|
|
279
|
+
className={[
|
|
280
|
+
"absolute z-10 transform-gpu rounded-lg border border-blue-500/20 p-4 shadow-lg",
|
|
281
|
+
"animate-[floatAnim_6s_ease-in-out_infinite]",
|
|
282
|
+
"bg-slate-800 dark:bg-slate-900",
|
|
283
|
+
positionClasses,
|
|
284
|
+
].join(" ")}
|
|
285
|
+
style={{ willChange: "transform" }}
|
|
286
|
+
>
|
|
287
|
+
<div className="mb-2 flex h-[30px] w-[30px] items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-purple-500 text-sm text-white">
|
|
288
|
+
{icon}
|
|
289
|
+
</div>
|
|
290
|
+
<div className="text-sm text-slate-300">{text}</div>
|
|
291
|
+
</div>
|
|
292
|
+
);
|
|
293
|
+
}
|