/* global React, ReactDOM */ // ACN-Cars · WordPress App-Shell // Verwendet window.ACN_DATA, das von WordPress per wp_localize_script bereitgestellt wird. const { useState, useEffect } = React; function ACNApp() { const data = window.ACN_DATA || {}; const { vehicles = [], addons = [], deals = [], locations = [], contact = {}, boot = {} } = data; const [page, setPage] = useState(boot.view || "home"); // v2.2.6 — Default-Vehicle aus boot.defaultVehicleId (= oberstes nach menu_order) const [vehicleId, setVehicleId] = useState(boot.vehicleSeed || boot.defaultVehicleId || (vehicles[0] && vehicles[0].id) || null); const [favs, setFavs] = useState([]); // v2.2.12 — Favoriten-FAB (wird nur eingeblendet, wenn Plugin-Header aus ist) const [showFavs, setShowFavs] = useState(false); const [comparing, setComparing] = useState([]); const [showCompare, setShowCompare] = useState(false); const [bookingDone, setBookingDone] = useState(null); const [submitting, setSubmitting] = useState(false); const [submitError, setSubmitError] = useState(null); const [theme, setTheme] = useState(boot.theme || "dark"); // v2.2.4 — Aktive Aktion (z. B. Wochenend-Pauschale), wenn der User über eine Aktions-Card kommt. // Steuert Pre-Fill der Daten, Bonus-Tag-Toggle und den Aktions-Hinweis im Buchungsformular. const [dealSeed, setDealSeed] = useState(null); // v2.2.16 — Aktion-Kontext für die Detail-Seite. Wenn der User aus einer // Aktion auf ein Fahrzeug klickt, fliesst die Aktion durch, damit DetailPage // den Aktions-Banner, den Aktions-Preis und „Mit Aktion buchen" anzeigt. const [dealContext, setDealContext] = useState(null); useEffect(() => { const root = document.querySelector(".acn-app-root"); if (root) { root.setAttribute("data-theme", theme); // Custom Theme-Farben aus Backend-Settings als CSS-Variablen if (boot.bgDark) root.style.setProperty("--bg-dark", boot.bgDark); if (boot.surfaceDark) root.style.setProperty("--surface-dark", boot.surfaceDark); if (boot.bgLight) root.style.setProperty("--bg-light", boot.bgLight); if (boot.surfaceLight)root.style.setProperty("--surface-light",boot.surfaceLight); // v2.2.5 — Buchen-Button-Farben (separat konfigurierbar) root.style.setProperty("--btn-bg", boot.btnBg || boot.accent || "#0A0A0A"); root.style.setProperty("--btn-fg", boot.btnFg || "#FFFFFF"); } document.documentElement.style.setProperty("--accent", boot.accent || "#00D4FF"); document.documentElement.style.setProperty("--btn-bg", boot.btnBg || boot.accent || "#0A0A0A"); document.documentElement.style.setProperty("--btn-fg", boot.btnFg || "#FFFFFF"); }, [theme, boot.accent, boot.bgDark, boot.surfaceDark, boot.bgLight, boot.surfaceLight, boot.btnBg, boot.btnFg]); useEffect(() => { window.scrollTo({ top: 0, behavior: "instant" }); }, [page]); const currentVehicle = vehicles.find(v => v.id === vehicleId); const onFav = id => setFavs(f => f.includes(id) ? f.filter(x => x !== id) : [...f, id]); const onCompare = id => setComparing(c => { if (c.includes(id)) return c.filter(x => x !== id); if (c.length >= 3) return c; return [...c, id]; }); // v2.2.6 — Tier-Hint: Wenn Karte einen Tier vorausgewählt hat, BookingPage diesen Tier voreinstellen const [bookingTierHours, setBookingTierHours] = useState(null); const onBook = (v, tierHours) => { setVehicleId(v.id); setDealSeed(null); setBookingTierHours(tierHours || null); setPage("booking"); }; // v2.2.16 — onOpen akzeptiert einen optionalen Deal-Parameter. Wenn der // User aus einer Aktion auf ein Fahrzeug klickt, fliesst die Aktion mit, // damit DetailPage den Aktions-Banner zeigt und „Mit Aktion buchen" CTA. const onOpen = (v, deal) => { setVehicleId(v.id); setDealContext(deal && typeof deal === "object" ? deal : null); setPage("detail"); }; // v2.2.4 — Über Aktions-Card buchen: Aktion mitgeben, damit BookingPage Daten vorbelegen // und Bonus-Tag-Toggle anzeigen kann. Erstes Fahrzeug der Aktion wird vorausgewählt. // v2.2.19 — Optionales `vehicle`: wenn ein konkretes Fahrzeug mitgegeben wird (z. B. // wenn der User in der Aktion-Magazin-Karte auf ein Auto klickt), wird dieses // Fahrzeug vorausgewählt — sonst Fallback auf deal.vehicles[0]. const onDealBook = (deal, vehicle) => { if (vehicle && vehicle.id) { setVehicleId(vehicle.id); } else if (deal && Array.isArray(deal.vehicles) && deal.vehicles.length > 0) { setVehicleId(deal.vehicles[0]); } setDealSeed(deal || null); setPage("booking"); }; // ============ REST Submit ============ const submitInquiry = async (formData) => { setSubmitting(true); setSubmitError(null); try { const res = await fetch(boot.apiBase + "/inquiry", { method: "POST", headers: { "Content-Type": "application/json", "X-WP-Nonce": boot.nonce }, body: JSON.stringify({ pickup_loc: formData.pickupLoc, return_loc: formData.returnLoc, pickup_at: formData.pickupAt, return_at: formData.returnAt, vehicle_id: formData.vehicleId, addons: formData.addons, extra_km: parseInt(formData.extraKm || 0, 10), name: formData.name, email: formData.email, phone: formData.phone, license: formData.license, message: formData.message || "", consent: !!formData.consent, website: formData.website || "", // v2.2.37 — Aktion-Kontext (Weekend-Pauschale) auch bei Inquiry-Submit mitgeben, // damit der Server-Total mit dem im Frontend angezeigten Pauschalpreis übereinstimmt. deal_id: (dealSeed && dealSeed.id) ? dealSeed.id : "", }), }); const json = await res.json(); if (!res.ok || !json.success) { const msg = json.message || (json.code ? json.code : "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut."); setSubmitError(msg); setSubmitting(false); return; } setBookingDone({ ...formData, ref: json.ref, total: json.total }); setPage("done"); } catch (err) { setSubmitError("Netzwerk-Fehler. Bitte versuchen Sie es erneut oder kontaktieren Sie uns direkt: " + (contact.phone || "")); } finally { setSubmitting(false); } }; // Plugin-Header / -Footer können in den Settings ausgeblendet werden (für Themes mit eigener Nav). const showPluginHeader = boot.showPluginHeader !== false; // default: true (legacy) const showPluginFooter = boot.showPluginFooter !== false; // default: true return (
{showPluginHeader && (
)} {page === "home" && ( )} {/* v2.2.41 — Flotte-Seite entfernt. HomePage zeigt alle Fahrzeuge im Grid. */} {page === "fleet" && ( )} {page === "detail" && currentVehicle && ( { setDealContext(null); setPage(dealContext ? "deals" : "home"); }} onBook={onBook} onDealBook={onDealBook} onCompare={onCompare} comparing={comparing.includes(vehicleId)} fav={favs.includes(vehicleId)} onFav={onFav} /> )} {page === "booking" && ( setPage("home")} onFleet={() => setPage("home")} /> )} {page === "deals" && ( setPage("home")} onFleet={() => setPage("home")} /> )} {/* v2.2.6 — "Kalkulator"-Seite wurde durch "Vergleichen" ersetzt. Legacy-Route page === "calc" leitet auf ComparePage. */} {(page === "calc" || page === "compare") && ( setPage("home")} onFleet={() => setPage("home")} onBook={onBook} /> )} {page === "done" && setPage("home")} />} {showPluginFooter &&