/* ============================================================
Rent-a-Car — Mobility & Fleet service page
============================================================ */
/* date helper — YYYY-MM-DD with day offset */
function nigerToday(offsetDays = 0) {
const d = new Date(Date.now() + offsetDays * 86400000);
return d.toISOString().slice(0, 10);
}
const FLEET_CATS = [
{ k: "all", label: { en: "All", fr: "Tous" } },
{ k: "sedan", label: { en: "Sedans", fr: "Berlines" } },
{ k: "suv", label: { en: "SUVs & 4x4", fr: "SUV & 4x4" } },
{ k: "van", label: { en: "Vans", fr: "Vans" } },
{ k: "pickup", label: { en: "Pickups", fr: "Pickups" } },
];
/* Fleet now loads live from Supabase (see gg-supabase.js).
window.GG_DATA.fleet is filled before React mounts. */
const FLEET = (window.GG_DATA && window.GG_DATA.fleet) || [];
/* Local fleet images — exact match per model */
/* Fleet images: local fallback merged live with Supabase paths via Proxy */
const RAC_VEHICLE_PHOTOS_LOCAL = {
"Toyota Corolla": "assets/fleet/toyota-corolla.jpg",
"Hyundai Elantra": "assets/fleet/elantra.jpg",
"Toyota RAV4": "assets/fleet/toyota-rav4.webp",
"Toyota Land Cruiser": "assets/fleet/land-cruiser.jpg",
"Mercedes Sprinter": "assets/fleet/mercedes-sprinter.png",
"Toyota HiAce": "assets/fleet/toyota-hiace.webp",
"Toyota Hilux D/Cab": "assets/fleet/toyota-hilux.jpg",
"Ford Ranger": "assets/fleet/ford-ranger.jpg",
"Kia Sportage": "assets/fleet/kia-sportage.jpg",
"Toyota Prado": "assets/fleet/toyota-prado.jpg",
"Mitsubishi L200": "assets/fleet/mitsubishi-triton.jpg",
"Toyota Coaster": "assets/fleet/toyota-coaster.jpg",
};
const RAC_VEHICLE_PHOTOS = new Proxy(RAC_VEHICLE_PHOTOS_LOCAL, {
get(local, key) {
const live = (window.GG_DATA && window.GG_DATA.fleetImgs) || {};
return live[key] || local[key];
},
});
function RACImg({ name, cover }) {
const [err, setErr] = useState(false);
const src = RAC_VEHICLE_PHOTOS[name] || "assets/fleet/land-cruiser.jpg";
if (err) return
;
return (
setErr(true)} loading="lazy" />
);
}
/* ---------- Google-Maps-style pick-up autocomplete ---------- */
const NIGER_PLACES = [
{ main: "A\u00e9roport International Diori Hamani", sec: "Niamey · Airport pick-up", branch: true },
{ main: "Guero\u2019s Group \u2014 Niamey Centre", sec: "Bd de l\u2019Ind\u00e9pendance, Niamey", branch: true },
{ main: "Guero\u2019s Group \u2014 Rive Droite", sec: "Route de Torodi, Niamey", branch: true },
{ main: "Plateau", sec: "Niamey, Niger" },
{ main: "Yantala", sec: "Niamey, Niger" },
{ main: "Lazaret", sec: "Niamey, Niger" },
{ main: "Kouara Kano", sec: "Niamey, Niger" },
{ main: "Gamkall\u00e9", sec: "Niamey, Niger" },
{ main: "Maradi", sec: "R\u00e9gion de Maradi, Niger" },
{ main: "Zinder", sec: "R\u00e9gion de Zinder, Niger" },
{ main: "Agadez", sec: "R\u00e9gion d\u2019Agadez, Niger" },
{ main: "Tahoua", sec: "R\u00e9gion de Tahoua, Niger" },
{ main: "Dosso", sec: "R\u00e9gion de Dosso, Niger" },
{ main: "Arlit", sec: "R\u00e9gion d\u2019Agadez, Niger" },
{ main: "Tillab\u00e9ri", sec: "R\u00e9gion de Tillab\u00e9ri, Niger" },
{ main: "Diffa", sec: "R\u00e9gion de Diffa, Niger" },
];
function GoogleAttribution() {
return (
{L("powered by", "fourni par")}
G o o g l e
);
}
function PlacesAutocomplete({ defaultValue }) {
const [value, setValue] = useState(defaultValue || "");
const [open, setOpen] = useState(false);
const [active, setActive] = useState(0);
const boxRef = useRef(null);
useEffect(() => {
const h = (e) => { if (boxRef.current && !boxRef.current.contains(e.target)) setOpen(false); };
document.addEventListener("mousedown", h);
return () => document.removeEventListener("mousedown", h);
}, []);
const q = value.trim().toLowerCase();
const matches = (q === "" ? NIGER_PLACES : NIGER_PLACES.filter((p) => (p.main + " " + p.sec).toLowerCase().includes(q))).slice(0, 6);
const choose = (p) => { setValue(p.main); setOpen(false); };
const onKey = (e) => {
if (!open || !matches.length) return;
if (e.key === "ArrowDown") { e.preventDefault(); setActive((a) => Math.min(a + 1, matches.length - 1)); }
else if (e.key === "ArrowUp") { e.preventDefault(); setActive((a) => Math.max(a - 1, 0)); }
else if (e.key === "Enter") { e.preventDefault(); if (matches[active]) choose(matches[active]); }
else if (e.key === "Escape") setOpen(false);
};
return (
{ setValue(e.target.value); setOpen(true); setActive(0); }} onFocus={() => setOpen(true)} onKeyDown={onKey} placeholder={L("Search a city, branch or airport", "Ville, agence ou a\u00e9roport")} style={inpStyle} autoComplete="off" />
{open && (
{matches.length === 0 ? (
{L("No matching place", "Aucun lieu trouv\u00e9")}
) : matches.map((p, i) => (
setActive(i)} onClick={() => choose(p)} style={{ display: "flex", alignItems: "center", gap: 12, width: "100%", textAlign: "left", padding: "11px 16px", background: i === active ? "var(--paper-2)" : "transparent", borderBottom: "1px solid var(--line)" }}>
{p.main}
{p.sec}
))}
)}
);
}
/* ---------- Corporate / fleet-sales contact modal ---------- */
function FleetSalesModal({ onClose }) {
const [done, setDone] = useState(false);
useEffect(() => {
document.body.classList.add("no-scroll");
return () => document.body.classList.remove("no-scroll");
}, []);
return ReactDOM.createPortal(
e.stopPropagation()} style={{ background: "var(--paper)", borderRadius: 6, width: "min(560px,100%)", maxHeight: "90vh", overflow: "auto", boxShadow: "0 40px 80px -30px rgba(0,0,0,.6)" }}>
{done ? (
{L("Request received!", "Demande envoy\u00e9e !")}
{L("Our fleet-sales team will get back to you within one business day to build a tailored proposal.", "Notre \u00e9quipe flotte vous recontacte sous un jour ouvr\u00e9 pour b\u00e2tir une proposition sur mesure.")}
{L("Done", "Termin\u00e9")}
) : (
)}
, document.body);
}
function RentACar() {
const [cat, setCat] = useState("all");
const [withDriver, setWithDriver] = useState(false);
const [booked, setBooked] = useState(null);
const [salesOpen, setSalesOpen] = useState(false);
const filtered = FLEET.filter((v) => cat === "all" || v.cat === cat);
return (
{/* HERO + BOOKING */}
} style={{ position: "absolute", inset: 0 }} />
Guero’s Group Rent-a-Car
{L("Rent-a-Car · A Guero\u2019s Group service", "Rent-a-Car · Un service du Guero\u2019s Group")}
{L(<>Wheels for the journey>, <>Des roues pour la route>)}.
{L("From a city sedan to a 14-seater van or a box truck — self-drive or chauffeured, by the day or the year. A clean, well-maintained fleet you can count on across Niger.", "De la berline urbaine au van 14 places ou au camion — en libre conduite ou avec chauffeur, \u00e0 la journ\u00e9e ou \u00e0 l\u2019ann\u00e9e. Une flotte propre et bien entretenue, partout au Niger.")}
{/* booking widget */}
setWithDriver(!withDriver)} style={{ display: "flex", alignItems: "center", gap: 10 }}>
{L("With a driver", "Avec chauffeur")}
{L("Free cancellation up to 24h before pick-up", "Annulation gratuite jusqu\u2019\u00e0 24h avant le d\u00e9part")}
{/* FLEET */}
{L("The fleet", "La flotte")}
{L("Pick your ride.", "Choisissez votre v\u00e9hicule.")}
{FLEET_CATS.map((c) => (
setCat(c.k)} style={{
fontFamily: "var(--fsemi)", fontWeight: 700, fontSize: 13.5, letterSpacing: "0.04em", textTransform: "uppercase",
padding: "9px 16px", borderRadius: 2, transition: "all .2s",
background: cat === c.k ? "var(--rent)" : "transparent", color: cat === c.k ? "var(--paper)" : "var(--ink)",
border: "1px solid " + (cat === c.k ? "var(--rent)" : "var(--line)"),
}}>{L(c.label.en, c.label.fr)}
))}
{filtered.map((v, i) => setBooked(v)} />)}
{/* HOW IT WORKS */}
How it works
On the road in four steps.
{[
["Choose", "Pick a vehicle and your dates — self-drive or with a driver.", "search"],
["Confirm", "Send your details and a valid licence. We confirm in minutes.", "check"],
["Collect", "Pick up at a branch or have it delivered to your door or the airport.", "key"],
["Drive", "Hit the road with 24/7 roadside support behind you.", "car"],
].map(([h, d, ic], i) => (
))}
{[["clock", "24/7", "roadside support"], ["shield", "Fully", "insured fleet"], ["gauge", "Unlimited", "mileage included"], ["users", "35%", "of Niger's market"]].map(([ic, n, l]) => (
))}
{/* CORPORATE CTA */}
} style={{ aspectRatio: "5/4", borderRadius: 5 }} />
For business
Corporate & long-term leasing.
Put your team and goods on the road without the capital outlay. Monthly and annual plans, dedicated account management, and replacement vehicles included.
{["Fixed monthly billing — no surprise costs", "Servicing & maintenance handled by us", "Replacement vehicle within 24h", "Optional dedicated drivers"].map((t) => (
{t}
))}
setSalesOpen(true)}>Talk to fleet sales
{/* BOOKING MODAL */}
{booked &&
setBooked(null)} />}
{salesOpen && setSalesOpen(false)} />}
);
}
/* ---------- Fleet card ---------- */
function FleetCard({ v, withDriver, delay, onBook }) {
const [hover, setHover] = useState(false);
const price = v.price + (withDriver ? 25000 : 0);
return (
setHover(true)} onMouseLeave={() => setHover(false)}
style={{ background: "var(--paper-0)", border: "1px solid var(--line)", borderRadius: 5, overflow: "hidden", transition: "transform .2s, box-shadow .2s", transform: hover ? "translateY(-5px)" : "none", boxShadow: hover ? "0 26px 50px -28px rgba(0,0,0,.32)" : "none", display: "flex", flexDirection: "column", height: "100%" }}>
} style={{ aspectRatio: "16/10" }}>
{v.tag &&
{v.tag === "Popular" ? L("Popular", "Populaire") : v.tag} }
{v.name}
{[["users", v.seats + L(" seats", " places")], ["gear", v.trans === "Auto" ? L("Auto", "Auto") : L("Manual", "Manuelle")], ["bolt", v.fuel === "Petrol" ? L("Petrol", "Essence") : v.fuel === "Diesel" ? "Diesel" : L("Hybrid", "Hybride")]].map(([ic, tx]) => (
{tx}
))}
{price.toLocaleString()} {L("FCFA/day", "FCFA/jour")}
{withDriver &&
{L("incl. driver", "chauffeur inclus")}
}
{L("Reserve", "R\u00e9server")}
);
}
/* ---------- Booking modal ---------- */
function BookingModal({ v, withDriver, onClose }) {
const [done, setDone] = useState(false);
useEffect(() => {
document.body.classList.add("no-scroll");
return () => document.body.classList.remove("no-scroll");
}, []);
const price = v.price + (withDriver ? 25000 : 0);
return ReactDOM.createPortal(
e.stopPropagation()} style={{ background: "var(--paper)", borderRadius: 6, width: "min(560px,100%)", maxHeight: "90vh", overflow: "auto", boxShadow: "0 40px 80px -30px rgba(0,0,0,.6)" }}>
} style={{ aspectRatio: "16/8" }} />
, document.body);
}
function RField({ icon, label, children }) {
return (
);
}
Object.assign(window, { RentACar });