Skip to content

Commit 82c2e3f

Browse files
committed
UI: updated landing page hero section with demo
1 parent 3468236 commit 82c2e3f

10 files changed

Lines changed: 3729 additions & 1868 deletions

File tree

apps/web-dashboard/dist/assets/index-BN6ITIw1.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

apps/web-dashboard/dist/assets/index-Bvk-7TBs.js

Lines changed: 0 additions & 1664 deletions
This file was deleted.

apps/web-dashboard/dist/assets/index-DAhCOP-a.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/web-dashboard/dist/assets/index-DGiRUfHn.js

Lines changed: 1672 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/web-dashboard/dist/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap"
1313
rel="stylesheet"
1414
/>
15-
<script type="module" crossorigin src="/assets/index-Bvk-7TBs.js"></script>
16-
<link rel="stylesheet" crossorigin href="/assets/index-BN6ITIw1.css">
15+
<script type="module" crossorigin src="/assets/index-DGiRUfHn.js"></script>
16+
<link rel="stylesheet" crossorigin href="/assets/index-DAhCOP-a.css">
1717
</head>
1818
<body>
1919
<div id="root"></div>

apps/web-dashboard/src/components/Layout/Footer.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function Footer() {
1313
{/* Brand / Newsletter Column */}
1414
<div className="footer-brand-col">
1515
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
16-
<img src="https://cdn.jsdelivr.net/gh/yash-pouranik/urBackend@main/frontend/public/logo.png" alt="urBackend Logo" style={{ height: '80px', width: 'auto' }} />
16+
<img src="https://cdn.jsdelivr.net/gh/yash-pouranik/urBackend/apps/web-dashboard/public/logo.png" alt="urBackend Logo" style={{ height: '80px', width: 'auto' }} />
1717
</div>
1818
<p style={{ color: 'var(--color-text-muted)', marginBottom: '1.5rem', maxWidth: '300px' }}>
1919
The instant Backend-as-a-Service for frontend developers. Ship faster.
@@ -227,4 +227,4 @@ export default function Footer() {
227227
`}</style>
228228
</footer>
229229
);
230-
}
230+
}
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import { useState, useEffect, useRef } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { motion, AnimatePresence } from 'framer-motion';
4+
import { ArrowRight, Zap, Check, Database, Code, Terminal, Plus } from 'lucide-react';
5+
import './style.css';
6+
7+
const ENDPOINTS = [
8+
{ method: 'GET', path: '/api/users', status: '200 OK' },
9+
{ method: 'POST', path: '/api/users', status: '201 Created' },
10+
{ method: 'GET', path: '/api/users/:id', status: '200 OK' },
11+
{ method: 'PUT', path: '/api/users/:id', status: '200 OK' },
12+
{ method: 'DELETE', path: '/api/users/:id', status: '200 OK' },
13+
];
14+
15+
const CLICK_STEPS = [
16+
{ name: 'name', type: 'String', required: true },
17+
{ name: 'email', type: 'String', required: true },
18+
{ name: 'role', type: 'String', required: false },
19+
];
20+
21+
function InteractiveLanding() {
22+
const timersRef = useRef([]);
23+
const [collectionName, setCollectionName] = useState('');
24+
const [fields, setFields] = useState([]);
25+
const [isBuildingUi, setIsBuildingUi] = useState(false);
26+
const [showDeploying, setShowDeploying] = useState(false);
27+
const [showEndpoints, setShowEndpoints] = useState(false);
28+
const [activeEndpoints, setActiveEndpoints] = useState([]);
29+
30+
const clearAllTimers = () => {
31+
timersRef.current.forEach(clearTimeout);
32+
timersRef.current = [];
33+
};
34+
35+
const runDemo = () => {
36+
clearAllTimers();
37+
setCollectionName('');
38+
setFields([]);
39+
setIsBuildingUi(true);
40+
setShowDeploying(false);
41+
setShowEndpoints(false);
42+
setActiveEndpoints([]);
43+
44+
timersRef.current.push(setTimeout(() => setCollectionName('users'), 600));
45+
timersRef.current.push(setTimeout(() => setFields([CLICK_STEPS[0]]), 1200));
46+
timersRef.current.push(setTimeout(() => setFields([CLICK_STEPS[0], CLICK_STEPS[1]]), 1800));
47+
timersRef.current.push(setTimeout(() => setFields(CLICK_STEPS), 2400));
48+
timersRef.current.push(setTimeout(() => setIsBuildingUi(false), 2700));
49+
50+
timersRef.current.push(setTimeout(() => {
51+
setShowDeploying(true);
52+
}, 3100));
53+
54+
timersRef.current.push(setTimeout(() => {
55+
setShowDeploying(false);
56+
setShowEndpoints(true);
57+
ENDPOINTS.forEach((_, index) => {
58+
const t = setTimeout(() => {
59+
setActiveEndpoints(prev => [...prev, index]);
60+
}, index * 180);
61+
timersRef.current.push(t);
62+
});
63+
}, 4600));
64+
};
65+
66+
useEffect(() => {
67+
runDemo();
68+
return () => clearAllTimers();
69+
}, []);
70+
71+
const resetAnimation = () => {
72+
runDemo();
73+
};
74+
75+
return (
76+
<div className="interactive-landing">
77+
{/* Minimal Navbar */}
78+
<nav className="minimal-nav">
79+
<div className="nav-content">
80+
<Link to="/" className="logo-link">
81+
<img
82+
src="https://cdn.jsdelivr.net/gh/yash-pouranik/urBackend@main/frontend/public/logo.png"
83+
alt="urBackend"
84+
className="nav-logo"
85+
/>
86+
</Link>
87+
<div className="nav-actions">
88+
<Link to="/login" className="nav-link">Login</Link>
89+
<Link to="/signup" className="nav-btn">
90+
Get Started
91+
<ArrowRight size={16} />
92+
</Link>
93+
</div>
94+
</div>
95+
</nav>
96+
97+
{/* Hero Section */}
98+
<div className="hero-container">
99+
<motion.div
100+
initial={{ opacity: 0, y: 20 }}
101+
animate={{ opacity: 1, y: 0 }}
102+
transition={{ duration: 0.6 }}
103+
className="hero-text"
104+
>
105+
<div className="hero-badge">
106+
<Zap size={14} />
107+
<span>Zero Config. Zero Boilerplate.</span>
108+
</div>
109+
<h1 className="hero-title">
110+
From Clicks to <span className="gradient-text">Secure APIs</span> in Seconds
111+
</h1>
112+
<p className="hero-description">
113+
Build collections from UI clicks just like urBackend studio, then instantly get production-ready REST APIs with auth and CRUD.
114+
</p>
115+
</motion.div>
116+
117+
{/* Interactive Demo Window */}
118+
<motion.div
119+
initial={{ opacity: 0, y: 40 }}
120+
animate={{ opacity: 1, y: 0 }}
121+
transition={{ duration: 0.8, delay: 0.3 }}
122+
className="demo-window"
123+
>
124+
<div className="window-header">
125+
<div className="window-dots">
126+
<span className="dot dot-red"></span>
127+
<span className="dot dot-yellow"></span>
128+
<span className="dot dot-green"></span>
129+
</div>
130+
<div className="window-title">
131+
<Terminal size={14} />
132+
<span>urBackend Studio</span>
133+
</div>
134+
<button className="replay-btn" onClick={resetAnimation}>
135+
<span>↻ Replay</span>
136+
</button>
137+
</div>
138+
139+
<div className="window-content">
140+
{/* Left Pane: CreateCollection-style UI */}
141+
<div className="pane left-pane">
142+
<div className="pane-header">
143+
<Database size={14} />
144+
<span>Collection Builder</span>
145+
<span className="pane-label">UI Mode</span>
146+
</div>
147+
<div className="builder-pane">
148+
<div className="builder-group">
149+
<label className="builder-label">Name</label>
150+
<div className="builder-input">{collectionName || 'users'}</div>
151+
</div>
152+
153+
<div className="builder-header-row">
154+
<span>NAME</span>
155+
<span>TYPE</span>
156+
<span>REQ</span>
157+
</div>
158+
159+
<div className="builder-table">
160+
{fields.map((field, index) => (
161+
<motion.div
162+
key={field.name}
163+
className="builder-row"
164+
initial={{ opacity: 0, x: -12 }}
165+
animate={{ opacity: 1, x: 0 }}
166+
transition={{ duration: 0.25, delay: index * 0.08 }}
167+
>
168+
<span className="builder-field-name">{field.name}</span>
169+
<span className="builder-type-chip">{field.type}</span>
170+
<span className={`builder-req ${field.required ? 'on' : 'off'}`}>
171+
{field.required ? <Check size={12} /> : '—'}
172+
</span>
173+
</motion.div>
174+
))}
175+
</div>
176+
177+
<div className="builder-actions">
178+
<button type="button" className="builder-add-btn">
179+
<Plus size={12} />
180+
Add Column
181+
</button>
182+
{isBuildingUi && <span className="builder-live">Auto-clicking...</span>}
183+
</div>
184+
</div>
185+
</div>
186+
187+
{/* Middle: Animated Flow */}
188+
<div className="flow-middle">
189+
<div className="flow-line">
190+
<motion.div
191+
className="flow-pulse"
192+
animate={{
193+
x: showDeploying ? [0, 100] : 0,
194+
opacity: showDeploying ? [1, 0] : 0.3,
195+
}}
196+
transition={{
197+
duration: 1.5,
198+
repeat: showDeploying ? 0 : Infinity,
199+
repeatDelay: 1,
200+
}}
201+
/>
202+
</div>
203+
<AnimatePresence>
204+
{showDeploying && (
205+
<motion.div
206+
className="deploying-badge"
207+
initial={{ scale: 0, opacity: 0 }}
208+
animate={{ scale: 1, opacity: 1 }}
209+
exit={{ scale: 0, opacity: 0 }}
210+
>
211+
<Zap size={12} />
212+
<span>Deploying...</span>
213+
</motion.div>
214+
)}
215+
</AnimatePresence>
216+
<div className="flow-icon">
217+
<Zap size={20} />
218+
</div>
219+
</div>
220+
221+
{/* Right Pane: Generated APIs */}
222+
<div className="pane right-pane">
223+
<div className="pane-header">
224+
<Code size={14} />
225+
<span>Generated APIs</span>
226+
<span className="pane-label">endpoints</span>
227+
</div>
228+
<div className="endpoints-container">
229+
{showEndpoints ? (
230+
ENDPOINTS.map((endpoint, index) => (
231+
<motion.div
232+
key={index}
233+
className={`endpoint-item ${activeEndpoints.includes(index) ? 'active' : ''}`}
234+
initial={{ opacity: 0, x: 20 }}
235+
animate={{ opacity: 1, x: 0 }}
236+
transition={{ delay: index * 0.2 }}
237+
>
238+
<span className={`method-tag ${endpoint.method.toLowerCase()}`}>
239+
{endpoint.method}
240+
</span>
241+
<code className="endpoint-path">{endpoint.path}</code>
242+
<span className="status-badge">
243+
<Check size={12} />
244+
{endpoint.status}
245+
</span>
246+
</motion.div>
247+
))
248+
) : (
249+
<div className="empty-state">
250+
<Terminal size={32} className="empty-icon" />
251+
<p>Waiting for UI actions...</p>
252+
</div>
253+
)}
254+
</div>
255+
</div>
256+
</div>
257+
</motion.div>
258+
259+
{/* Trust Indicators */}
260+
<motion.div
261+
className="trust-indicators"
262+
initial={{ opacity: 0 }}
263+
animate={{ opacity: 1 }}
264+
transition={{ delay: 1.2 }}
265+
>
266+
<div className="trust-item">
267+
<Check size={16} />
268+
<span>JWT Auth Included</span>
269+
</div>
270+
<div className="trust-item">
271+
<Check size={16} />
272+
<span>Auto Validation</span>
273+
</div>
274+
<div className="trust-item">
275+
<Check size={16} />
276+
<span>Role-Based Access</span>
277+
</div>
278+
<div className="trust-item">
279+
<Check size={16} />
280+
<span>Open Source</span>
281+
</div>
282+
</motion.div>
283+
284+
{/* CTA Buttons */}
285+
<motion.div
286+
className="cta-group"
287+
initial={{ opacity: 0, y: 20 }}
288+
animate={{ opacity: 1, y: 0 }}
289+
transition={{ delay: 1.4 }}
290+
>
291+
<Link to="/signup" className="cta-primary">
292+
<span>Start Building Free</span>
293+
<ArrowRight size={18} />
294+
</Link>
295+
<Link to="/docs" className="cta-secondary">
296+
View Documentation
297+
</Link>
298+
</motion.div>
299+
</div>
300+
</div>
301+
);
302+
}
303+
304+
export default InteractiveLanding;

0 commit comments

Comments
 (0)