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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions