<SectionTitle
icon={}
title="New Reservation"
sub="Select a vehicle and fill in your booking details"
/>
<div style={{ display: "grid", gridTemplateColumns: "1fr minmax(300px, 400px)",
gap: 24, alignItems: "start" }}>
{/* LEFT — vehicle selector */}
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<div style={{ fontSize: 12, fontWeight: 700, color: "#6B7280",
textTransform: "uppercase", letterSpacing: "0.5px", marginBottom: 4 }}>
Choose Your Vehicle
</div>
{VEHICLES.map(v => {
const s = STATUS[v.status];
const isAvail = v.status === "available";
const isSel = selected === v.id;
return (
<div key={v.id}
onClick={() => isAvail && setSelected(v.id)}
style={{
background: isSel
? `linear-gradient(90deg, ${C.blue}, ${C.steel})`
: "#fff",
borderRadius: 16,
border: isSel ? "none" : `1.5px solid ${isAvail ? "#E5E7EB" : "#F3F4F6"}`,
padding: "16px 20px",
display: "flex", alignItems: "center", gap: 16,
cursor: isAvail ? "pointer" : "default",
opacity: isAvail ? 1 : 0.7,
boxShadow: isSel ? `0 8px 28px ${C.blue}44` : "0 1px 6px #0001",
transition: "all .25s ease",
}}>
<div style={{ width: 44, height: 44, borderRadius: 12, flexShrink: 0,
background: isSel ? "#ffffff22" : C.ice,
display: "flex", alignItems: "center", justifyContent: "center" }}>
<CarIcon stroke={isSel ? "#fff" : C.mid} />
</div>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 800, fontSize: 14,
color: isSel ? "#fff" : C.navy }}>{v.name}</div>
<div style={{ fontSize: 11, marginTop: 2,
color: isSel ? "#BAE6FD" : "#6B7280" }}>{v.location}</div>
<div style={{ marginTop: 8 }}>
<Battery pct={v.battery} />
</div>
</div>
<div style={{ textAlign: "right", flexShrink: 0 }}>
<span style={{
display: "inline-flex", alignItems: "center", gap: 5,
background: isSel ? "#ffffff22" : s.bg,
color: isSel ? "#fff" : s.txt,
padding: "3px 10px", borderRadius: 99, fontSize: 11, fontWeight: 700,
}}>
<span style={{ width: 6, height: 6, borderRadius: "50%",
background: isSel ? "#A7F3D0" : s.dot }} />
{s.label}
</span>
<div style={{ fontSize: 11, marginTop: 8,
color: isSel ? "#93C5FD" : "#9CA3AF" }}>
{v.nextAvail}
</div>
</div>
</div>
);
})}
</div>
{/* RIGHT — booking form */}
<div style={{ background: "#fff", borderRadius: 20, padding: 26,
border: "1.5px solid #E5E7EB", boxShadow: "0 4px 20px #0001" }}>
{/* Selected vehicle chip */}
<div style={{ background: C.ice, borderRadius: 12, padding: "12px 16px",
marginBottom: 20, border: `1.5px solid ${C.steel}22` }}>
<div style={{ fontSize: 10, fontWeight: 700, color: C.mid,
textTransform: "uppercase", letterSpacing: "0.5px", marginBottom: 6 }}>
Selected Vehicle
</div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div>
<div style={{ fontWeight: 800, fontSize: 14, color: C.navy }}>{selVeh?.name}</div>
<div style={{ fontSize: 11, color: "#6B7280", marginTop: 1 }}>{selVeh?.location}</div>
</div>
<span style={{ fontSize: 11, fontWeight: 700, padding: "3px 10px", borderRadius: 99,
background: STATUS[selVeh?.status]?.bg,
color: STATUS[selVeh?.status]?.txt }}>
{STATUS[selVeh?.status]?.label}
</span>
</div>
</div>
{confirmed ? (
/* ── Confirmation state ───────────────────────────── */
<div style={{ textAlign: "center", padding: "16px 0" }}>
<div style={{ width: 64, height: 64, borderRadius: "50%",
background: "#D1FAE5", margin: "0 auto 16px",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 28 }}>
✓
</div>
<div style={{ fontSize: 18, fontWeight: 900, color: "#065F46", marginBottom: 10 }}>
Reservation Confirmed!
</div>
<div style={{ fontSize: 13, color: "#374151", lineHeight: 1.7,
background: "#F9FAFB", borderRadius: 10, padding: "12px 16px",
marginBottom: 14, textAlign: "left" }}>
<div><strong>Vehicle:</strong> {selVeh?.name}</div>
<div><strong>Location:</strong> {selVeh?.location}</div>
<div><strong>Date:</strong> {date}</div>
<div><strong>Time:</strong> {pickup} → {ret}</div>
<div><strong>Purpose:</strong> {purpose}</div>
</div>
<div style={{ background: "#FEF3C7", border: "1px solid #FDE68A",
borderRadius: 10, padding: "10px 14px", fontSize: 12,
color: "#92400E", fontWeight: 600, marginBottom: 18 }}>
A confirmation email has been sent to your AUI address.
Don't forget to post with <strong>#VolvoLovers</strong>!
</div>
<button onClick={handleReset}
style={{ padding: "11px 28px", borderRadius: 11, border: "none",
background: `linear-gradient(90deg, ${C.blue}, ${C.steel})`,
color: "#fff", fontWeight: 800, fontSize: 13,
cursor: "pointer", fontFamily: "inherit" }}>
New Reservation
</button>
</div>
) : (
/* ── Form ─────────────────────────────────────────── */
<div style={{ display: "flex", flexDirection: "column", gap: 15 }}>
<div>
<label style={labelStyle}>Date</label>
<input type="date" value={date}
onChange={e => setDate(e.target.value)} style={inputStyle} />
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
<div>
<label style={labelStyle}>Pickup Time</label>
<input type="time" value={pickup}
onChange={e => setPickup(e.target.value)} style={inputStyle} />
</div>
<div>
<label style={labelStyle}>Return Time</label>
<input type="time" value={ret}
onChange={e => setRet(e.target.value)} style={inputStyle} />
</div>
</div>
<div>
<label style={labelStyle}>Trip Purpose</label>
<select value={purpose}
onChange={e => setPurpose(e.target.value)} style={inputStyle}>
<option value="">Select a purpose...</option>
<option value="Campus Commute">Campus Commute</option>
<option value="Academic Field Trip">Academic Field Trip</option>
<option value="Personal Errand">Personal Errand</option>
<option value="University Event">University Event</option>
<option value="Other">Other</option>
</select>
</div>
<div style={{ fontSize: 11, color: "#9CA3AF", lineHeight: 1.6,
background: "#F9FAFB", borderRadius: 8, padding: "10px 12px",
border: "1px solid #F3F4F6" }}>
By confirming, you agree to AUI-VolvoLovers terms and certify you hold
a valid driving licence. The debit card guarantee is managed by AUI.
</div>
{/* Unavailable warning */}
{selVeh?.status !== "available" && (
<div style={{ background: "#FEF3C7", border: "1px solid #FDE68A",
borderRadius: 10, padding: "10px 14px",
fontSize: 12, color: "#92400E", fontWeight: 600 }}>
This vehicle is currently {selVeh?.status}.
Please select an available vehicle on the left.
</div>
)}
<button onClick={() => canBook && setConfirmed(true)}
disabled={!canBook}
style={{
padding: "13px", borderRadius: 11, border: "none",
fontFamily: "inherit", fontWeight: 800, fontSize: 14,
cursor: canBook ? "pointer" : "not-allowed",
transition: "all .2s",
background: canBook
? `linear-gradient(90deg, ${C.blue} 0%, ${C.steel} 100%)`
: "#E5E7EB",
color: canBook ? "#fff" : "#9CA3AF",
boxShadow: canBook ? `0 4px 18px ${C.mid}44` : "none",
}}>
Confirm Reservation
</button>
</div>
)}
</div>
</div>
</div>
);
}
// ══════════════════════════════════════════════════════════════════
// GAMIFICATION DATA
// ══════════════════════════════════════════════════════════════════
const LEADERBOARD = [
{ rank: 1, name: "Yasmine B.", pts: 1240, badge: "Platinum", trips: 22, avatar: "YB" },
{ rank: 2, name: "Mehdi O.", pts: 1095, badge: "Gold", trips: 19, avatar: "MO" },
{ rank: 3, name: "Sara A.", pts: 980, badge: "Gold", trips: 17, avatar: "SA" },
{ rank: 4, name: "Adam K.", pts: 760, badge: "Silver", trips: 13, avatar: "AK" },
{ rank: 5, name: "You", pts: 620, badge: "Silver", trips: 11, avatar: "AU", isUser: true },
];
const POINT_ACTIONS = [
{ icon: "↩", label: "On-time return", pts: "+30 pts", color: "#10B981", bg: "#D1FAE5" },
{ icon: "✓", label: "Clean vehicle handback", pts: "+25 pts", color: "#3B82F6", bg: "#DBEAFE" },
{ icon: "⚡", label: "Return with 50%+ battery", pts: "+20 pts", color: "#F59E0B", bg: "#FEF3C7" },
{ icon: "📱", label: "#VolvoLovers social post", pts: "+15 pts", color: "#8B5CF6", bg: "#EDE9FE" },
{ icon: "⭐", label: "5-star staff rating", pts: "+10 pts", color: "#EC4899", bg: "#FCE7F3" },
{ icon: "🚫", label: "Late / damaged return", pts: "−50 pts", color: "#EF4444", bg: "#FEE2E2" },
];
const BADGE_TIERS = [
{ tier: "Bronze", min: 0, max: 299, color: "#CD7F32", bg: "#FEF3E2" },
{ tier: "Silver", min: 300, max: 699, color: "#94A3B8", bg: "#F1F5F9" },
{ tier: "Gold", min: 700, max: 1099, color: "#F59E0B", bg: "#FEF3C7" },
{ tier: "Platinum", min: 1100, max: 9999, color: "#2E86C1", bg: "#E8F4FD" },
];
const CAMPUS_REWARDS = [
{ pts: 500, reward: "AUI Café discount voucher (10%)", claimed: true },
{ pts: 800, reward: "Priority booking slot for 1 week", claimed: true },
{ pts: 1000, reward: "Free campus printing credits (100 pages)", claimed: false },
{ pts: 1500, reward: "AUI Bookstore gift card — MAD 100", claimed: false },
{ pts: 2000, reward: "Volvo Maroc branded kit (cap + bag)", claimed: false },
];
// ══════════════════════════════════════════════════════════════════
// GAMIFICATION SECTION COMPONENT
// ══════════════════════════════════════════════════════════════════
function GamificationSection() {
const [activeReward, setActiveReward] = useState(null);
const userPts = 620;
const userBadge = BADGE_TIERS.find(b => userPts >= b.min && userPts <= b.max);
const nextBadge = BADGE_TIERS.find(b => b.min > userPts);
const pctToNext = nextBadge
? Math.round(((userPts - userBadge.min) / (nextBadge.min - userBadge.min)) * 100)
: 100;
const rankColors = ["#F59E0B", "#94A3B8", "#CD7F32"];
return (
<div style={{ display: "flex", flexDirection: "column", gap: 20 }}>
{/* ── Section header ────────────────────────────────────── */}
<div style={{
display: "flex", alignItems: "center", gap: 12,
paddingTop: 8, borderTop: "1.5px solid #E5E7EB",
}}>
<div style={{
width: 40, height: 40, borderRadius: 12, flexShrink: 0,
background: "linear-gradient(135deg, #7C3AED, #A855F7)",
display: "flex", alignItems: "center", justifyContent: "center", fontSize: 20,
}}>🏆</div>
<div>
<div style={{ fontSize: 18, fontWeight: 900, color: "#001833", letterSpacing: "-0.4px" }}>
VolvoLovers Gamification
</div>
<div style={{ fontSize: 12, color: "#6B7280", marginTop: 1 }}>
Earn points for responsible EV use · Redeem campus rewards
</div>
</div>
</div>
{/* ── Row 1: My Points + Earn/Lose actions ──────────────── */}
<div style={{ display: "grid", gridTemplateColumns: "300px 1fr", gap: 20 }}>
{/* My Points card */}
<div style={{
background: "linear-gradient(145deg, #4C1D95, #7C3AED)",
borderRadius: 20, padding: 24, color: "#fff",
boxShadow: "0 8px 32px #7C3AED44",
}}>
<div style={{ fontSize: 11, fontWeight: 700, color: "#C4B5FD",
textTransform: "uppercase", letterSpacing: "0.5px", marginBottom: 16 }}>
My EV Points
</div>
<div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 20 }}>
<div style={{
width: 52, height: 52, borderRadius: "50%",
background: "linear-gradient(135deg, #2E86C1, #1565A0)",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 18, fontWeight: 900, color: "#fff",
boxShadow: `0 0 0 3px ${userBadge.color}`,
}}>AU</div>
<div>
<div style={{ fontWeight: 800, fontSize: 15 }}>AUI Student</div>
<div style={{
display: "inline-flex", alignItems: "center", gap: 5, marginTop: 4,
background: userBadge.bg, color: userBadge.color,
padding: "2px 10px", borderRadius: 99, fontSize: 11, fontWeight: 800,
}}>★ {userBadge.tier}</div>
</div>
</div>
<div style={{ textAlign: "center", marginBottom: 16 }}>
<div style={{ fontSize: 52, fontWeight: 900, lineHeight: 1,
letterSpacing: "-2px" }}>
{userPts.toLocaleString()}
</div>
<div style={{ fontSize: 12, color: "#C4B5FD", marginTop: 4 }}>total points earned</div>
</div>
{nextBadge && (
<div>
<div style={{ display: "flex", justifyContent: "space-between",
fontSize: 11, color: "#DDD6FE", marginBottom: 6, fontWeight: 600 }}>
<span>{userBadge.tier}</span>
<span>{nextBadge.tier} at {nextBadge.min} pts</span>
</div>
<div style={{ height: 8, background: "#ffffff22", borderRadius: 99, overflow: "hidden" }}>
<div style={{
width: `${pctToNext}%`, height: "100%", borderRadius: 99,
background: "linear-gradient(90deg, #A78BFA, #7C3AED)",
transition: "width .8s ease",
}} />
</div>
<div style={{ fontSize: 10, color: "#C4B5FD", marginTop: 5, textAlign: "right" }}>
{nextBadge.min - userPts} pts to {nextBadge.tier}
</div>
</div>
)}
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginTop: 18 }}>
{[
{ label: "Trips", value: "11" },
{ label: "On-time", value: "10/11" },
{ label: "Clean returns", value: "9/11" },
{ label: "Social posts", value: "7" },
].map(s => (
<div key={s.label} style={{
background: "#ffffff18", borderRadius: 10,
padding: "10px 12px", textAlign: "center",
}}>
<div style={{ fontSize: 16, fontWeight: 900, color: "#fff" }}>{s.value}</div>
<div style={{ fontSize: 10, color: "#C4B5FD", marginTop: 2 }}>{s.label}</div>
</div>
))}
</div>
</div>
{/* How to earn/lose points */}
<div style={{
background: "#fff", borderRadius: 20, padding: 24,
border: "1.5px solid #E5E7EB", boxShadow: "0 2px 10px #0001",
}}>
<div style={{ fontSize: 15, fontWeight: 800, color: "#001833", marginBottom: 18 }}>
How to Earn (and Lose) Points
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
{POINT_ACTIONS.map(a => (
<div key={a.label} style={{
display: "flex", alignItems: "center", gap: 12,
background: a.bg, borderRadius: 12, padding: "12px 14px",
border: `1.5px solid ${a.color}22`,
}}>
<div style={{
width: 36, height: 36, borderRadius: 10, flexShrink: 0,
background: "#fff", display: "flex", alignItems: "center",
justifyContent: "center", fontSize: 16,
boxShadow: `0 2px 8px ${a.color}33`,
}}>{a.icon}</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 12, fontWeight: 700, color: "#111827", lineHeight: 1.3 }}>
{a.label}
</div>
<div style={{ fontSize: 13, fontWeight: 900, color: a.color, marginTop: 3 }}>
{a.pts}
</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* ── Row 2: Badge tiers + Leaderboard ──────────────────── */}
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
{/* Badge tiers */}
<div style={{
background: "#fff", borderRadius: 20, padding: 24,
border: "1.5px solid #E5E7EB", boxShadow: "0 2px 10px #0001",
}}>
<div style={{ fontSize: 15, fontWeight: 800, color: "#001833", marginBottom: 18 }}>
Membership Tiers
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
{BADGE_TIERS.map(b => {
const isActive = b.tier === userBadge.tier;
return (
<div key={b.tier} style={{
display: "flex", alignItems: "center", gap: 14,
background: isActive ? b.bg : "#FAFAFA",
borderRadius: 12, padding: "12px 16px",
border: isActive ? `2px solid ${b.color}` : "1.5px solid #E5E7EB",
transition: "all .2s",
}}>
<div style={{
width: 38, height: 38, borderRadius: "50%", flexShrink: 0,
background: b.bg, display: "flex", alignItems: "center",
justifyContent: "center", fontSize: 18,
boxShadow: isActive ? `0 0 0 3px ${b.color}` : "none",
}}>
{b.tier === "Bronze" ? "🥉" : b.tier === "Silver" ? "🥈"
: b.tier === "Gold" ? "🥇" : "💎"}
</div>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 800, fontSize: 14, color: b.color }}>
{b.tier}
{isActive && (
<span style={{
marginLeft: 8, fontSize: 10, background: b.color,
color: "#fff", padding: "1px 7px", borderRadius: 99, fontWeight: 700,
}}>YOUR TIER</span>
)}
</div>
<div style={{ fontSize: 11, color: "#6B7280", marginTop: 2 }}>
{b.max === 9999 ? `${b.min}+ points` : `${b.min} – ${b.max} points`}
</div>
</div>
<div style={{ width: 60, height: 6, background: "#E5E7EB",
borderRadius: 99, overflow: "hidden" }}>
<div style={{
width: isActive ? `${pctToNext}%`
: b.min <= userPts ? "100%" : "0%",
height: "100%", background: b.color, borderRadius: 99,
}} />
</div>
</div>
);
})}
</div>
</div>
{/* Leaderboard */}
<div style={{
background: "linear-gradient(145deg, #0F172A, #1E3A5F)",
borderRadius: 20, padding: 24, color: "#fff",
}}>
<div style={{ fontSize: 15, fontWeight: 800, marginBottom: 18,
display: "flex", alignItems: "center", gap: 8 }}>
<span style={{ fontSize: 18 }}>🏆</span> Campus Leaderboard
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
{LEADERBOARD.map((u, i) => (
<div key={u.rank} style={{
display: "flex", alignItems: "center", gap: 12,
background: u.isUser ? "#2E86C133" : "#ffffff0D",
borderRadius: 12, padding: "10px 14px",
border: u.isUser ? "1.5px solid #2E86C1" : "1.5px solid #ffffff11",
}}>
<div style={{
width: 28, height: 28, borderRadius: "50%", flexShrink: 0,
background: i < 3 ? rankColors[i] : "#ffffff18",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: i < 3 ? 14 : 11, fontWeight: 900,
color: i < 3 ? "#fff" : "#94A3B8",
}}>
{i < 3 ? ["🥇","🥈","🥉"][i] : `#${u.rank}`}
</div>
<div style={{
width: 32, height: 32, borderRadius: "50%", flexShrink: 0,
background: u.isUser
? "linear-gradient(135deg, #2E86C1, #1565A0)"
: `hsl(${u.rank * 60}, 55%, 45%)`,
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 11, fontWeight: 800, color: "#fff",
}}>{u.avatar}</div>
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 700, fontSize: 13,
color: u.isUser ? "#93C5FD" : "#F1F5F9" }}>
{u.name}{u.isUser && <span style={{ fontSize: 10, color: "#93C5FD" }}> (you)</span>}
</div>
<div style={{ fontSize: 10, color: "#64748B", marginTop: 1 }}>{u.trips} trips</div>
</div>
<div style={{ textAlign: "right" }}>
<div style={{ fontWeight: 900, fontSize: 14,
color: i === 0 ? "#FCD34D" : "#F1F5F9" }}>
{u.pts.toLocaleString()}
</div>
<div style={{ fontSize: 10, color: "#64748B" }}>pts</div>
</div>
</div>
))}
</div>
<div style={{ marginTop: 14, fontSize: 10, color: "#475569", textAlign: "center" }}>
Updated weekly · Spring 2026 semester
</div>
</div>
</div>
{/* ── Row 3: Campus Rewards Store ────────────────────────── */}
<div style={{
background: "#fff", borderRadius: 20, padding: 24,
border: "1.5px solid #E5E7EB", boxShadow: "0 2px 10px #0001",
}}>
<div style={{ display: "flex", justifyContent: "space-between",
alignItems: "center", marginBottom: 18 }}>
<div style={{ fontSize: 15, fontWeight: 800, color: "#001833" }}>
Campus Rewards Store
</div>
<div style={{ fontSize: 12, color: "#6B7280" }}>
Your balance: <strong style={{ color: "#7C3AED" }}>{userPts} pts</strong>
</div>
</div>
<div style={{ display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(190px, 1fr))", gap: 12 }}>
{CAMPUS_REWARDS.map((r, i) => {
const canClaim = userPts >= r.pts && !r.claimed;
const locked = userPts < r.pts;
return (
<div key={i}
onClick={() => canClaim && setActiveReward(activeReward === i ? null : i)}
style={{
borderRadius: 14, padding: "16px 16px 14px",
border: r.claimed ? "1.5px solid #D1FAE5"
: canClaim ? "1.5px solid #7C3AED"
: "1.5px solid #E5E7EB",
background: r.claimed ? "#F0FDF4"
: canClaim ? "#FAF5FF"
: "#FAFAFA",
cursor: canClaim ? "pointer" : "default",
transition: "all .2s",
opacity: locked ? 0.6 : 1,
position: "relative", overflow: "hidden",
}}>
<div style={{
display: "inline-flex", alignItems: "center", gap: 4,
background: r.claimed ? "#D1FAE5" : canClaim ? "#EDE9FE" : "#F3F4F6",
color: r.claimed ? "#065F46" : canClaim ? "#7C3AED" : "#9CA3AF",
padding: "3px 9px", borderRadius: 99,
fontSize: 11, fontWeight: 800, marginBottom: 10,
}}>
{r.claimed ? "✓ Claimed" : locked ? `🔒 ${r.pts} pts` : `★ ${r.pts} pts`}
</div>
<div style={{ fontSize: 13, fontWeight: 700, color: "#111827", lineHeight: 1.4 }}>
{r.reward}
</div>
{canClaim && activeReward !== i && (
<div style={{ marginTop: 10, fontSize: 11, fontWeight: 700, color: "#7C3AED" }}>
Tap to redeem →
</div>
)}
{activeReward === i && (
<div style={{
position: "absolute", inset: 0, background: "#7C3AED",
borderRadius: 14, display: "flex", alignItems: "center",
justifyContent: "center", flexDirection: "column", gap: 6,
cursor: "pointer",
}}>
<span style={{ fontSize: 28 }}>🎉</span>
<span style={{ fontSize: 12, fontWeight: 800, color: "#fff" }}>Redeemed!</span>
<span style={{ fontSize: 10, color: "#C4B5FD" }}>Show this at AUI admin</span>
</div>
)}
</div>
);
})}
</div>
</div>
</div>
);
}
// ══════════════════════════════════════════════════════════════════
// STATS TAB
// ══════════════════════════════════════════════════════════════════
function StatsTab() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
<SectionTitle
icon={<svg width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="#fff" strokeWidth="2.5"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>}
title="Sustainability & Usage Stats"
sub="AUI Campus EV Programme · Spring 2026"
/>
{/* Stat cards */}
<div style={{ display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: 16 }}>
{STATS.map(s => (
<div key={s.label} style={{
background: "linear-gradient(145deg, #F0FDF4, #DCFCE7)",
borderRadius: 18, padding: "26px 20px",
border: "1.5px solid #A7F3D0", textAlign: "center",
}}>
<div style={{ fontSize: 32, marginBottom: 10 }}>{s.icon}</div>
<div style={{ fontSize: 30, fontWeight: 900, color: "#065F46",
letterSpacing: "-1px", lineHeight: 1 }}>{s.value}</div>
<div style={{ fontSize: 13, fontWeight: 700, color: "#047857", marginTop: 8 }}>
{s.label}
</div>
<div style={{ fontSize: 11, color: "#6EE7B7", marginTop: 4 }}>{s.sub}</div>
</div>
))}
</div>
{/* Usage + Instructions */}
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
{/* Fleet usage breakdown */}
<div style={{ background: "#fff", borderRadius: 20, padding: 24,
border: "1.5px solid #E5E7EB", boxShadow: "0 2px 10px #0001" }}>
<div style={{ fontSize: 15, fontWeight: 800, color: C.navy, marginBottom: 20 }}>
Fleet Usage Breakdown
</div>
{[
{ label: "EX30 #1 — NAB Parking", pct: 78, col: "#3B82F6" },
{ label: "EX30 #2 — Building 38 Parking", pct: 55, col: "#8B5CF6" },
{ label: "EX30 #3 — Building 9 Parking", pct: 42, col: "#10B981" },
].map(r => (
<div key={r.label} style={{ marginBottom: 18 }}>
<div style={{ display: "flex", justifyContent: "space-between",
fontSize: 12, fontWeight: 600, color: "#374151", marginBottom: 6 }}>
<span>{r.label}</span>
<span style={{ color: r.col, fontWeight: 800 }}>{r.pct}%</span>
</div>
<div style={{ height: 8, background: "#F3F4F6",
borderRadius: 99, overflow: "hidden" }}>
<div style={{ width: `${r.pct}%`, height: "100%", background: r.col,
borderRadius: 99, transition: "width .8s ease" }} />
</div>
</div>
))}
<div style={{ marginTop: 10, padding: "12px 14px", background: C.ice,
borderRadius: 10, fontSize: 12, color: C.mid, fontWeight: 600 }}>
Total: 318 trips · 4,240 km driven on campus
</div>
</div>
{/* How to use */}
<div style={{ background: `linear-gradient(145deg, ${C.navy}, ${C.blue})`,
borderRadius: 20, padding: 24 }}>
<div style={{ display: "flex", alignItems: "center", gap: 10,
marginBottom: 20 }}>
<CheckIcon />
<div style={{ fontSize: 15, fontWeight: 800, color: "#fff" }}>How to Use</div>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
{STEPS.map((step, i) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12 }}>
<div style={{
width: 30, height: 30, borderRadius: "50%", flexShrink: 0,
background: i === 4 ? "linear-gradient(135deg, #F59E0B, #FBBF24)" : "#ffffff18",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 10, fontWeight: 800,
color: i === 4 ? C.navy : "#93C5FD",
border: i === 4 ? "none" : "1px solid #ffffff33",
}}>
{step.n}
</div>
<div style={{ fontSize: 12.5, lineHeight: 1.4,
color: i === 4 ? "#FDE68A" : "#E0F2FE",
fontWeight: i === 4 ? 700 : 400 }}>
{step.text}
</div>
</div>
))}
</div>
<div style={{ marginTop: 18, padding: "10px 14px", borderRadius: 10,
background: "#F59E0B22", border: "1px solid #F59E0B44",
fontSize: 11, color: "#FDE68A", fontWeight: 600,
display: "flex", alignItems: "center", gap: 6 }}>
<span style={{ fontSize: 14 }}>★</span>
Tag your photos with{" "}
<strong style={{ color: "#FBBF24" }}>#VolvoLovers</strong>
{" "}on Instagram & TikTok
</div>
</div>
</div>
{/* ── Gamification ──────────────────────────────────────── */}
<GamificationSection />
</div>
);
}
// ══════════════════════════════════════════════════════════════════
// ROOT APP
// ══════════════════════════════════════════════════════════════════
export default function App() {
const [tab, setTab] = useState("fleet");
const [selected, setSelected] = useState(1);
const goToReserve = () => setTab("reserve");
const TABS = [
{ key: "fleet", label: "Fleet", icon: "🚗" },
{ key: "reserve", label: "Reserve", icon: "📅" },
{ key: "stats", label: "Stats", icon: "📊" },
];
return (
<div style={{ minHeight: "100vh", background: "#F1F5FB",
fontFamily: "'DM Sans', 'Segoe UI', system-ui, sans-serif",
color: C.navy }}>
{/* ── HEADER ──────────────────────────────────────────────── */}
<header style={{
background: `linear-gradient(110deg, ${C.navy} 0%, ${C.blue} 100%)`,
padding: "0 32px", height: 62,
display: "flex", alignItems: "center", justifyContent: "space-between",
boxShadow: "0 4px 24px #00183344",
position: "sticky", top: 0, zIndex: 100,
gap: 16,
}}>
{/* Logo */}
<div style={{ display: "flex", alignItems: "center", gap: 12, flexShrink: 0 }}>
<div style={{ width: 36, height: 36, borderRadius: "50%", flexShrink: 0,
background: "linear-gradient(135deg, #fff 0%, #CBD5E1 100%)",
display: "flex", alignItems: "center", justifyContent: "center",
boxShadow: "0 2px 8px #00000033" }}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9" stroke={C.navy} strokeWidth="2"/>
<path d="M8 12 L12 8 L16 12" stroke={C.navy} strokeWidth="2" strokeLinecap="round"/>
<line x1="12" y1="8" x2="12" y2="16" stroke={C.navy} strokeWidth="2" strokeLinecap="round"/>
</svg>
</div>
<div>
<div style={{ fontSize: 15, fontWeight: 900, color: "#fff",
letterSpacing: "-0.3px", lineHeight: 1.15 }}>
AUI-VolvoLovers
</div>
<div style={{ fontSize: 10, color: "#93C5FD", fontWeight: 500, letterSpacing: "0.4px" }}>
Campus EV Sharing
</div>
</div>
</div>
{/* Navigation tabs */}
<nav style={{ display: "flex", gap: 4, background: "#ffffff15",
borderRadius: 13, padding: 4 }}>
{TABS.map(t => (
<button key={t.key} onClick={() => setTab(t.key)}
style={{
padding: "7px 20px", borderRadius: 10, border: "none",
cursor: "pointer", fontFamily: "inherit",
background: tab === t.key ? "#fff" : "transparent",
color: tab === t.key ? C.navy : "#93C5FD",
fontWeight: 700, fontSize: 13,
boxShadow: tab === t.key ? "0 2px 8px #00000022" : "none",
transition: "all .2s ease",
display: "flex", alignItems: "center", gap: 6,
}}>
<span>{t.icon}</span>
{t.label}
</button>
))}
</nav>
{/* User */}
<div style={{ display: "flex", alignItems: "center", gap: 12, flexShrink: 0 }}>
<div style={{ background: "#1565A022", border: "1px solid #93C5FD55",
color: "#93C5FD", padding: "4px 12px", borderRadius: 99,
fontSize: 11, fontWeight: 700 }}>
#VolvoLovers
</div>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<div style={{ width: 33, height: 33, borderRadius: "50%", flexShrink: 0,
background: `linear-gradient(135deg, ${C.steel}, ${C.mid})`,
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 13, fontWeight: 800, color: "#fff",
boxShadow: "0 0 0 2px #ffffff44" }}>
AU
</div>
<div>
<div style={{ fontSize: 12, fontWeight: 700, color: "#fff" }}>AUI Student</div>
<div style={{ fontSize: 10, color: "#93C5FD" }}>ID: 82405</div>
</div>
</div>
</div>
</header>
{/* ── PAGE CONTENT ────────────────────────────────────────── */}
<main style={{ maxWidth: 1200, margin: "0 auto", padding: "30px 24px 48px" }}>
{tab === "fleet" && <FleetTab selected={selected} setSelected={setSelected} goToReserve={goToReserve} />}
{tab === "reserve" && <ReserveTab selected={selected} setSelected={setSelected} />}
{tab === "stats" && <StatsTab />}
</main>
{/* ── FOOTER ──────────────────────────────────────────────── */}
<footer style={{ textAlign: "center", paddingBottom: 28,
fontSize: 11, color: "#9CA3AF" }}>
AUI-VolvoLovers Campus EV Sharing · Al Akhawayn University in Ifrane ·
MIS Course — Prof. M. Morad MOUHSINE · Spring 2026 ·{" "}
<span style={{ color: C.steel, fontWeight: 600 }}>
Powered by Volvo Maroc × AUI
</span>
</footer>
</div>
);
}
import { useState } from "react";
// ── Brand tokens ─────────────────────────────────────────────────
const C = {
navy: "#1833",
blue: "#003A6C",
mid: "#1565A0",
steel: "#2E86C1",
ice: "#E8F4FD",
white: "#FFFFFF",
};
// ── Data ─────────────────────────────────────────────────────────
const VEHICLES = [
{ id: 1, name: "Volvo EX30 #1", location: "NAB Parking", status: "available", battery: 91, nextAvail: "Available now" },
{ id: 2, name: "Volvo EX30 #2", location: "Building 38 Parking", status: "reserved", battery: 67, nextAvail: "Available 14:30" },
{ id: 3, name: "Volvo EX30 #3", location: "Building 9 Parking", status: "charging", battery: 43, nextAvail: "Available 16:00" },
];
const STATUS = {
available: { label: "Available", bg: "#D1FAE5", txt: "#065F46", dot: "#10B981" },
reserved: { label: "Reserved", bg: "#FEF3C7", txt: "#92400E", dot: "#F59E0B" },
charging: { label: "Charging", bg: "#DBEAFE", txt: "#1E40AF", dot: "#3B82F6" },
};
const STATS = [
{ icon: "🌿", value: "2.4 t", label: "CO₂ Saved", sub: "This semester" },
{ icon: "⚡", value: "318", label: "EV Trips Completed", sub: "Since Jan 2026" },
{ icon: "⛽", value: "MAD 4,100", label: "Fuel Savings", sub: "Estimated total"},
];
const STEPS = [
{ n: "01", text: "Pick up vehicle at assigned campus parking spot" },
{ n: "02", text: "Check battery level before departure — minimum 30%" },
{ n: "03", text: "Drive responsibly within and around campus" },
{ n: "04", text: "Return to the exact same parking location" },
{ n: "05", text: "Share your experience with #VolvoLovers" },
];
// ── Shared UI primitives ─────────────────────────────────────────
function Battery({ pct }) {
const col = pct > 60 ? "#10B981" : pct > 30 ? "#F59E0B" : "#EF4444";
return (
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<div style={{ flex: 1, height: 6, background: "#E5E7EB", borderRadius: 99, overflow: "hidden" }}>
<div style={{ width:
${pct}%, height: "100%", background: col,borderRadius: 99, transition: "width .5s ease" }} />
<span style={{ fontSize: 12, fontWeight: 700, color: col, minWidth: 32 }}>{pct}%
);
}
function CarSVG({ tint = C.blue, opacity = 0.10 }) {
return (
<svg viewBox="0 0 220 72" style={{ width: "100%", display: "block", pointerEvents: "none" }}>
);
}
function SectionTitle({ icon, title, sub }) {
return (
<div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 24 }}>
<div style={{ width: 40, height: 40, borderRadius: 12, flexShrink: 0,
background:
linear-gradient(135deg, ${C.blue}, ${C.steel}),display: "flex", alignItems: "center", justifyContent: "center" }}>
{icon}
<div style={{ fontSize: 18, fontWeight: 900, color: C.navy, letterSpacing: "-0.4px" }}>{title}
<div style={{ fontSize: 12, color: "#6B7280", marginTop: 1 }}>{sub}
);
}
const CarIcon = ({ stroke = "#fff" }) => (
);
const CalIcon = ({ stroke = "#fff" }) => (
);
const CheckIcon = ({ stroke = "#93C5FD" }) => (
);
// ══════════════════════════════════════════════════════════════════
// FLEET TAB
// ══════════════════════════════════════════════════════════════════
function FleetTab({ selected, setSelected, goToReserve }) {
return (
<div style={{ display: "flex", alignItems: "center",
justifyContent: "space-between", flexWrap: "wrap", gap: 12, marginBottom: 24 }}>
<SectionTitle
icon={}
title="Campus EV Fleet"
sub="Al Akhawayn University · Ifrane Campus · Spring 2026"
/>
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
{Object.entries(STATUS).map(([k, s]) => (
<span key={k} style={{ background: s.bg, color: s.txt,
padding: "5px 14px", borderRadius: 99,
fontSize: 11, fontWeight: 700 }}>
{VEHICLES.filter(v => v.status === k).length} {s.label}
))}
);
}
// ══════════════════════════════════════════════════════════════════
// RESERVE TAB
// ══════════════════════════════════════════════════════════════════
function ReserveTab({ selected, setSelected }) {
const [date, setDate] = useState("");
const [pickup, setPickup] = useState("");
const [ret, setRet] = useState("");
const [purpose, setPurpose] = useState("");
const [confirmed, setConfirmed] = useState(false);
const selVeh = VEHICLES.find(v => v.id === selected);
const canBook = selVeh?.status === "available" && date && pickup && ret && purpose;
const handleReset = () => {
setConfirmed(false);
setDate(""); setPickup(""); setRet(""); setPurpose("");
};
const inputStyle = {
width: "100%", padding: "11px 14px", borderRadius: 10, boxSizing: "border-box",
border: "1.5px solid #E5E7EB", fontSize: 13, color: C.navy,
background: "#FAFBFF", outline: "none", fontFamily: "inherit", appearance: "none",
};
const labelStyle = {
fontSize: 11, fontWeight: 700, color: "#6B7280", display: "block",
textTransform: "uppercase", letterSpacing: "0.5px", marginBottom: 6,
};
return (
<SectionTitle
icon={}
title="New Reservation"
sub="Select a vehicle and fill in your booking details"
/>
);
}
// ══════════════════════════════════════════════════════════════════
// GAMIFICATION DATA
// ══════════════════════════════════════════════════════════════════
const LEADERBOARD = [
{ rank: 1, name: "Yasmine B.", pts: 1240, badge: "Platinum", trips: 22, avatar: "YB" },
{ rank: 2, name: "Mehdi O.", pts: 1095, badge: "Gold", trips: 19, avatar: "MO" },
{ rank: 3, name: "Sara A.", pts: 980, badge: "Gold", trips: 17, avatar: "SA" },
{ rank: 4, name: "Adam K.", pts: 760, badge: "Silver", trips: 13, avatar: "AK" },
{ rank: 5, name: "You", pts: 620, badge: "Silver", trips: 11, avatar: "AU", isUser: true },
];
const POINT_ACTIONS = [
{ icon: "↩", label: "On-time return", pts: "+30 pts", color: "#10B981", bg: "#D1FAE5" },
{ icon: "✓", label: "Clean vehicle handback", pts: "+25 pts", color: "#3B82F6", bg: "#DBEAFE" },
{ icon: "⚡", label: "Return with 50%+ battery", pts: "+20 pts", color: "#F59E0B", bg: "#FEF3C7" },
{ icon: "📱", label: "#VolvoLovers social post", pts: "+15 pts", color: "#8B5CF6", bg: "#EDE9FE" },
{ icon: "⭐", label: "5-star staff rating", pts: "+10 pts", color: "#EC4899", bg: "#FCE7F3" },
{ icon: "🚫", label: "Late / damaged return", pts: "−50 pts", color: "#EF4444", bg: "#FEE2E2" },
];
const BADGE_TIERS = [
{ tier: "Bronze", min: 0, max: 299, color: "#CD7F32", bg: "#FEF3E2" },
{ tier: "Silver", min: 300, max: 699, color: "#94A3B8", bg: "#F1F5F9" },
{ tier: "Gold", min: 700, max: 1099, color: "#F59E0B", bg: "#FEF3C7" },
{ tier: "Platinum", min: 1100, max: 9999, color: "#2E86C1", bg: "#E8F4FD" },
];
const CAMPUS_REWARDS = [
{ pts: 500, reward: "AUI Café discount voucher (10%)", claimed: true },
{ pts: 800, reward: "Priority booking slot for 1 week", claimed: true },
{ pts: 1000, reward: "Free campus printing credits (100 pages)", claimed: false },
{ pts: 1500, reward: "AUI Bookstore gift card — MAD 100", claimed: false },
{ pts: 2000, reward: "Volvo Maroc branded kit (cap + bag)", claimed: false },
];
// ══════════════════════════════════════════════════════════════════
// GAMIFICATION SECTION COMPONENT
// ══════════════════════════════════════════════════════════════════
function GamificationSection() {
const [activeReward, setActiveReward] = useState(null);
const userPts = 620;
const userBadge = BADGE_TIERS.find(b => userPts >= b.min && userPts <= b.max);
const nextBadge = BADGE_TIERS.find(b => b.min > userPts);
const pctToNext = nextBadge
? Math.round(((userPts - userBadge.min) / (nextBadge.min - userBadge.min)) * 100)
: 100;
const rankColors = ["#F59E0B", "#94A3B8", "#CD7F32"];
return (
<div style={{ display: "flex", flexDirection: "column", gap: 20 }}>
);
}
// ══════════════════════════════════════════════════════════════════
// STATS TAB
// ══════════════════════════════════════════════════════════════════
function StatsTab() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
);
}
// ══════════════════════════════════════════════════════════════════
// ROOT APP
// ══════════════════════════════════════════════════════════════════
export default function App() {
const [tab, setTab] = useState("fleet");
const [selected, setSelected] = useState(1);
const goToReserve = () => setTab("reserve");
const TABS = [
{ key: "fleet", label: "Fleet", icon: "🚗" },
{ key: "reserve", label: "Reserve", icon: "📅" },
{ key: "stats", label: "Stats", icon: "📊" },
];
return (
<div style={{ minHeight: "100vh", background: "#F1F5FB",
fontFamily: "'DM Sans', 'Segoe UI', system-ui, sans-serif",
color: C.navy }}>
);
}