// DETALLE de vehículo — galería de fotos (o 360° fallback), ficha técnica, calculadora de cuotas
const DetailScreen = ({ vehicleId, onBack, onNav, onSelectVehicle }) => {
const v = window.OMEGA_HELPERS.byId(vehicleId);
const { fmtPrice, fmtPrecio, fmtKm } = window.OMEGA_HELPERS;
const { vehicles } = window.OMEGA_DATA;
const hasPhotos = v && v.photos && v.photos.length > 0;
// Galería de fotos
const [activePhoto, setActivePhoto] = useState(0);
const [copiado, setCopiado] = useState(false);
// Link directo a ESTE vehículo (deep-link) para mandar al cliente.
const compartirLink = () => {
const url = window.location.origin + window.location.pathname + '#detalle/' + encodeURIComponent(vehicleId);
const ok = () => { setCopiado(true); setTimeout(() => setCopiado(false), 2200); };
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(ok).catch(() => window.prompt('Copiá el link del vehículo:', url));
} else { window.prompt('Copiá el link del vehículo:', url); }
} catch (e) { window.prompt('Copiá el link del vehículo:', url); }
};
const [lightbox, setLightbox] = useState(false);
// 360 viewer (fallback)
const [rotation, setRotation] = useState(0);
const [auto360, setAuto360] = useState(true);
const dragRef = useRef({ active: false, startX: 0, startRot: 0 });
useEffect(() => {
if (hasPhotos || !auto360) return;
let raf;
let last = performance.now();
const tick = (t) => {
const dt = t - last; last = t;
setRotation(r => (r + dt * 0.04) % 360);
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [auto360, hasPhotos]);
const onDown = (e) => {
setAuto360(false);
const x = (e.touches ? e.touches[0].clientX : e.clientX);
dragRef.current = { active: true, startX: x, startRot: rotation };
};
const onMove = (e) => {
if (!dragRef.current.active) return;
const x = (e.touches ? e.touches[0].clientX : e.clientX);
const dx = x - dragRef.current.startX;
setRotation(((dragRef.current.startRot + dx * 0.6) % 360 + 360) % 360);
};
const onUp = () => { dragRef.current.active = false; };
useEffect(() => {
window.addEventListener('mousemove', onMove);
window.addEventListener('mouseup', onUp);
window.addEventListener('touchmove', onMove);
window.addEventListener('touchend', onUp);
return () => {
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onUp);
window.removeEventListener('touchmove', onMove);
window.removeEventListener('touchend', onUp);
};
}, []);
// Lightbox keyboard
useEffect(() => {
if (!lightbox) return;
const onKey = (e) => {
if (e.key === 'Escape') setLightbox(false);
if (e.key === 'ArrowRight') setActivePhoto(p => (p+1) % v.photos.length);
if (e.key === 'ArrowLeft') setActivePhoto(p => (p-1+v.photos.length) % v.photos.length);
};
document.body.style.overflow = 'hidden';
window.addEventListener('keydown', onKey);
return () => { document.body.style.overflow = ''; window.removeEventListener('keydown', onKey); };
}, [lightbox, v]);
// Calculadora de cuotas
const [calc, setCalc] = useState({ anticipoPct: 50, plazo: 36 });
const tnaAnual = 0.42; // tasa interna — NO se muestra al cliente en la web
const result = useMemo(() => {
if (!v) return null;
const total = v.precio;
const anticipo = Math.round(total * (calc.anticipoPct / 100));
const capital = total - anticipo;
const i = tnaAnual / 12;
const n = calc.plazo;
const cuota = capital * (i * Math.pow(1+i, n)) / (Math.pow(1+i, n) - 1);
return { anticipo, capital, cuota: Math.round(cuota), total: Math.round(cuota * n) + anticipo };
}, [v, calc]);
if (!v) return
Vehículo no encontrado. Volver
;
const related = vehicles.filter(x => x.carroceria === v.carroceria && x.id !== v.id).slice(0,3);
return (
{/* Breadcrumb */}
onNav('home')}>Inicio
/
onNav('catalogo')}>Catálogo
/
{v.marca} · {v.modelo}
{/* IZQUIERDA: galería de fotos o 360 fallback */}
{hasPhotos ? (
setLightbox(true)}>
{activePhoto + 1} / {v.photos.length}
Click para ampliar
{v.photos.length > 1 && (
{e.stopPropagation(); setActivePhoto(p=>(p-1+v.photos.length)%v.photos.length);}} aria-label="Anterior">‹
{e.stopPropagation(); setActivePhoto(p=>(p+1)%v.photos.length);}} aria-label="Siguiente">›
)}
{v.photos.map((p, i) => (
setActivePhoto(i)}>
))}
) : (
360° · arrastrá para girar
{Math.round(rotation)}°
{['EXTERIOR','INTERIOR','MOTOR','BAÚL','DETALLES','TABLERO'].map((t,i) => (
{t}
))}
)}
{/* DERECHA: precio, ficha, CTA */}
{v.tag && {v.tag}
}
{v.anio} · {v.carroceria} · {v.color}
{v.marca}
{v.modelo}
Precio público
{fmtPrecio(v)}
Acepta permuta y financiamiento
COMBUSTIBLE
{v.combustible}
TRANSMISIÓN
{v.transmision}
Consultar por WhatsApp
Reservar visita en showroom
{copiado ? '✓ ¡Link copiado!' : 'Compartir link de este vehículo'}
Inspección 142 pts
Historial verificado
{/* CALCULADORA DE CUOTAS — financiación SOLO para modelos 2012 en adelante.
La financiera (DECRÉDITO) no toma modelos 2011 o anteriores (ni el 50%). */}
{/* ponytail: financiación en vivo OCULTA a pedido (2026-08). Volver: cambiar false por true. */}
{false && (
{(!v.anio || v.anio >= 2012) ? (
Financiación en vivo
Calculá tu cuota.
Movimientos los slider y vé el resultado al instante. Cuota estimada — sujeta a aprobación.
Cuota mensual estimada
{fmtPrice(result.cuota)}
Anticipo {fmtPrice(result.anticipo)}
Capital a financiar {fmtPrice(result.capital)}
Plazo {calc.plazo} meses
Pre-aprobar mi crédito
) : (
Financiación
Este modelo requiere simular el financiamiento en nuestra agencia de forma personal.
La financiación está disponible solo para modelos 2012 en adelante . Para este vehículo (año {v.anio}) podés coordinar contado o permuta con un asesor.
Hablar con un asesor
)}
)}
{/* FICHA TÉCNICA */}
Ficha técnica completa
Especificaciones.
{/* RELACIONADOS */}
{related.length > 0 && (
Te puede interesar
Otras unidades {v.carroceria}.
onNav('catalogo')}>Ver todas
{related.map(rv => )}
)}
{/* LIGHTBOX */}
{lightbox && hasPhotos && (
setLightbox(false)}>
setLightbox(false)} aria-label="Cerrar">
{e.stopPropagation(); setActivePhoto(p=>(p-1+v.photos.length)%v.photos.length);}}>‹
e.stopPropagation()}/>
{e.stopPropagation(); setActivePhoto(p=>(p+1)%v.photos.length);}}>›
{activePhoto+1} / {v.photos.length}
)}
);
};
const SpecBlock = ({ title, rows }) => (
{title}
{rows.map(([k,vv],i) => (
{k}
{vv}
))}
);
Object.assign(window, { DetailScreen });