Skip to content

AUI x VolvoLovers #3638

@asoulaimaneel7-alt

Description

@asoulaimaneel7-alt

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}

))}

  <div style={{ display: "grid",
                gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))", gap: 20 }}>
    {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(150deg, ${C.blue} 0%, ${C.steel} 100%)`
              : "#fff",
            borderRadius: 20,
            border: isSel ? "none" : "1.5px solid #E5E7EB",
            boxShadow: isSel ? `0 12px 40px ${C.blue}44` : "0 2px 12px #0001",
            padding: "20px 20px 18px",
            cursor: isAvail ? "pointer" : "default",
            transition: "all .3s ease",
            transform: isSel ? "translateY(-4px)" : "none",
            opacity: isAvail ? 1 : 0.76,
          }}>

          <div style={{ margin: "-6px -8px 8px" }}>
            <CarSVG tint={isSel ? "#fff" : C.blue} opacity={isSel ? 0.18 : 0.09} />
          </div>

          <div style={{ display: "flex", justifyContent: "space-between",
                        alignItems: "flex-start", marginBottom: 12 }}>
            <div>
              <div style={{ fontSize: 15, fontWeight: 800, letterSpacing: "-0.3px",
                            color: isSel ? "#fff" : C.navy }}>
                {v.name}
              </div>
              <div style={{ fontSize: 11, fontWeight: 500, marginTop: 2,
                            color: isSel ? "#93C5FD" : "#6B7280" }}>
                {v.location}
              </div>
            </div>
            <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>

          <div style={{ marginBottom: 10 }}>
            <div style={{ fontSize: 10, fontWeight: 700, textTransform: "uppercase",
                          letterSpacing: "0.5px", marginBottom: 5,
                          color: isSel ? "#93C5FD" : "#9CA3AF" }}>
              Battery
            </div>
            <Battery pct={v.battery} />
          </div>

          <div style={{ fontSize: 11, marginBottom: 16,
                        display: "flex", alignItems: "center", gap: 5,
                        color: isSel ? "#BAE6FD" : "#6B7280" }}>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
                 stroke={isSel ? "#BAE6FD" : "#9CA3AF"} strokeWidth="2.5">
              <circle cx="12" cy="12" r="10"/>
              <path d="M12 6v6l4 2"/>
            </svg>
            {v.nextAvail}
          </div>

          <button
            onClick={e => {
              e.stopPropagation();
              if (isAvail) { setSelected(v.id); goToReserve(); }
            }}
            disabled={!isAvail}
            style={{
              width: "100%", padding: "10px 0", borderRadius: 11, border: "none",
              fontFamily: "inherit", fontWeight: 800, fontSize: 13,
              cursor: isAvail ? "pointer" : "not-allowed",
              transition: "all .2s",
              background: isAvail
                ? (isSel ? "#fff" : `linear-gradient(90deg, ${C.blue}, ${C.steel})`)
                : "#E5E7EB",
              color: isAvail ? (isSel ? C.navy : "#fff") : "#9CA3AF",
              boxShadow: isAvail && !isSel ? `0 4px 14px ${C.mid}44` : "none",
            }}>
            {isAvail ? "Reserve Now →"
                     : v.status === "reserved" ? "Currently Reserved" : "Charging…"}
          </button>
        </div>
      );
    })}
  </div>
</div>

);
}

// ══════════════════════════════════════════════════════════════════
// 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"
/>

  <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 &amp; 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>

);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions