/* global React */
// ACN-Cars · Shared icons & small UI atoms
const Icon = {
Heart: ({ filled }) => (
),
Compare: () => (
),
Info2: () => (
),
Shield: () => (
),
Clock: () => (
),
Handshake: () => (
),
User: () => (
),
Mail: () => (
),
Phone: () => (
),
Card: () => (
),
MessageSquare: () => (
),
ArrowLeft: ({ size = 16 }) => (
),
ArrowRight: ({ size = 16 }) => (
),
Check: () => (
),
Plus: () => (
),
WhatsApp: () => (
),
Search: () => (
),
Map: () => (
),
Cal: () => (
),
Sun: () => (
),
Moon: () => (
),
Sparkle: () => (
),
Bolt: () => (
),
Info: () => (
),
};
// Format CHF
function chf(n) {
return new Intl.NumberFormat("de-CH", { minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(Math.round(n));
}
// Spec pill row used on cards
function SpecPills({ vehicle }) {
return (
{vehicle.transmission}
{vehicle.fuel}
{vehicle.seats} Plätze
{vehicle.power} PS
);
}
// Live countdown
function Countdown({ to }) {
const [, setTick] = React.useState(0);
React.useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
const ms = Math.max(0, to - Date.now());
const s = Math.floor(ms / 1000);
const d = Math.floor(s / 86400);
const h = Math.floor((s % 86400) / 3600);
const m = Math.floor((s % 3600) / 60);
const sec = s % 60;
const cell = (n, l) => (
{String(n).padStart(2, "0")}
{l}
);
return (
{d > 0 && cell(d, "TAG")}
{cell(h, "STD")}
{cell(m, "MIN")}
{cell(sec, "SEK")}
);
}
// Helper: Inkl. km für gewählte Stunden — spiegelt die Server-Logik aus class-acn-rest.php
// v2.2.44 — Per-Tier-Override (kmIncluded-Map mit explizit eingestellten km_3h/km_6h/...)
// hat IMMER Vorrang. kmPerDay nur als Fallback wenn keine Tier-Map vorhanden ist.
// Vorher (v2.2.38) hat kmPerDay die im Backend gepflegten Tier-Werte ignoriert →
// Symptom: Backend km_6h = 180 km, Frontend zeigt 55 km (= 220 * 6/24).
function ACN_kmIncludedFor(vehicle, hours) {
if (!vehicle) return 0;
const h = Math.max(1, parseInt(hours, 10) || 1);
// 1) PRIORITÄT: kmIncluded-Map (Per-Tier-Override aus Backend)
// Wenn der Admin pro Tier explizit km_3h/km_6h/km_24h/... eingetragen hat,
// haben diese Werte Vorrang vor jeder Berechnung.
const m = vehicle && vehicle.kmIncluded;
if (m && typeof m === "object") {
// Exakter Match (z.B. 6 Std → m["6"])
if (m[String(h)] != null && parseInt(m[String(h)], 10) > 0) {
return parseInt(m[String(h)], 10);
}
// ≥ 1 Woche: nächste Woche aufrunden
if (h >= 168 && parseInt(m["168"], 10) > 0) {
const weeks = Math.ceil(h / 168);
return parseInt(m["168"], 10) * weeks;
}
// Bucket-Match: nächstgrösserer eingetragener Tier (z.B. 4 Std → m["6"])
const buckets = [1, 3, 6, 24, 48, 72, 168];
for (const b of buckets) {
if (h <= b && m[String(b)] != null && parseInt(m[String(b)], 10) > 0) {
return parseInt(m[String(b)], 10);
}
}
}
// 2) Fallback: kmPerDay-Modell (nur wenn keine Tier-Map vorhanden)
const perDay = parseInt(vehicle.kmPerDay, 10);
if (perDay > 0) {
if (h <= 24) {
const val = Math.round(perDay * (h / 24));
return Math.max(30, val);
}
const days = Math.ceil(h / 24);
return perDay * days;
}
return 0;
}
Object.assign(window, { Icon, chf, SpecPills, Countdown, ACN_kmIncludedFor });