Chat now fetches tenant inventory summary (brands, counts, low-stock) and injects it into the AI system prompt so responses prioritize local stock. VIN decoder uses free NHTSA vPIC API to decode 17-char VINs and auto-fills the vehicle selector dropdowns when a catalog match is found. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1019,6 +1019,131 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ─── VIN DECODER ───
|
||||
var vinInputWrap = document.getElementById('vinInputWrap');
|
||||
var vinInput = document.getElementById('vinInput');
|
||||
var vinStatus = document.getElementById('vinStatus');
|
||||
var vinToggle = document.getElementById('vinToggle');
|
||||
|
||||
function toggleVin() {
|
||||
var isVisible = vinInputWrap.style.display !== 'none';
|
||||
vinInputWrap.style.display = isVisible ? 'none' : '';
|
||||
vinToggle.textContent = isVisible ? 'Tienes el VIN?' : 'Ocultar VIN';
|
||||
if (!isVisible && vinInput) vinInput.focus();
|
||||
}
|
||||
|
||||
function decodeVin() {
|
||||
var vin = (vinInput.value || '').trim().toUpperCase();
|
||||
if (vin.length !== 17) {
|
||||
showVinStatus('El VIN debe tener exactamente 17 caracteres.', true);
|
||||
return;
|
||||
}
|
||||
showVinStatus('Decodificando VIN...', false);
|
||||
|
||||
apiFetch(API + '/vin/' + encodeURIComponent(vin)).then(function (data) {
|
||||
if (!data) {
|
||||
showVinStatus('Error de conexion al decodificar VIN.', true);
|
||||
return;
|
||||
}
|
||||
if (data.error && !data.make) {
|
||||
showVinStatus(data.error, true);
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
if (data.year) parts.push(data.year);
|
||||
if (data.make) parts.push(data.make);
|
||||
if (data.model) parts.push(data.model);
|
||||
if (data.engine) parts.push(data.engine);
|
||||
var label = parts.join(' ') || 'Vehiculo no reconocido';
|
||||
|
||||
// If we got a catalog match, auto-fill the dropdowns
|
||||
var match = data.catalog_match;
|
||||
if (match && match.brand_id) {
|
||||
showVinStatus(label + ' — Encontrado en catalogo, cargando...', false);
|
||||
_autoFillFromVin(match, data);
|
||||
} else {
|
||||
showVinStatus(label + ' — No encontrado en el catalogo TecDoc.', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _autoFillFromVin(match, vinData) {
|
||||
// Set year dropdown
|
||||
if (match.year_id) {
|
||||
vsYear.value = String(match.year_id);
|
||||
// Trigger brand load
|
||||
apiFetch(API + '/brands?year_id=' + match.year_id).then(function (brandData) {
|
||||
var brands = brandData && (brandData.data || brandData);
|
||||
if (!brands) return;
|
||||
vsBrand.innerHTML = '<option value="">Marca...</option>';
|
||||
brands.forEach(function (b) {
|
||||
vsBrand.innerHTML += '<option value="' + b.id_brand + '">' + esc(b.name_brand) + '</option>';
|
||||
});
|
||||
vsBrand.disabled = false;
|
||||
vsClear.style.display = '';
|
||||
|
||||
if (match.brand_id) {
|
||||
vsBrand.value = String(match.brand_id);
|
||||
// Load models
|
||||
apiFetch(API + '/models?brand_id=' + match.brand_id + '&year_id=' + match.year_id).then(function (modelData) {
|
||||
var models = modelData && (modelData.data || modelData);
|
||||
if (!models) return;
|
||||
vsModel.innerHTML = '<option value="">Modelo...</option>';
|
||||
models.forEach(function (m) {
|
||||
vsModel.innerHTML += '<option value="' + m.id_model + '">' + esc(m.display_name || m.name_model) + '</option>';
|
||||
});
|
||||
vsModel.disabled = false;
|
||||
|
||||
if (match.model_id) {
|
||||
vsModel.value = String(match.model_id);
|
||||
// Load engines
|
||||
apiFetch(API + '/engines?model_id=' + match.model_id + '&year_id=' + match.year_id).then(function (engData) {
|
||||
var engines = engData && (engData.data || engData);
|
||||
if (!engines) return;
|
||||
vsEngine.innerHTML = '<option value="">Motor...</option>';
|
||||
engines.forEach(function (e) {
|
||||
var elabel = e.name_engine + (e.trim_level ? ' (' + e.trim_level + ')' : '');
|
||||
vsEngine.innerHTML += '<option value="' + e.id_mye + '">' + esc(elabel) + '</option>';
|
||||
});
|
||||
vsEngine.disabled = false;
|
||||
|
||||
// Auto-select engine if only one or if match specifies it
|
||||
if (match.id_mye) {
|
||||
vsEngine.value = String(match.id_mye);
|
||||
vsEngineChanged();
|
||||
showVinStatus('Vehiculo cargado desde VIN.', false);
|
||||
} else if (engines.length === 1) {
|
||||
vsEngine.value = engines[0].id_mye;
|
||||
vsEngineChanged();
|
||||
showVinStatus('Vehiculo cargado desde VIN.', false);
|
||||
} else {
|
||||
showVinStatus('Selecciona el motor para continuar.', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showVinStatus(msg, isError) {
|
||||
vinStatus.style.display = msg ? '' : 'none';
|
||||
vinStatus.textContent = msg;
|
||||
vinStatus.style.color = isError ? 'var(--color-error)' : 'var(--color-text-muted)';
|
||||
}
|
||||
|
||||
// Allow Enter key in VIN input to trigger decode
|
||||
if (vinInput) {
|
||||
vinInput.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
decodeVin();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.CatalogApp = {
|
||||
toggleCart: toggleCart,
|
||||
goToCheckout: goToCheckout,
|
||||
@@ -1033,6 +1158,8 @@
|
||||
vsEngineChanged: vsEngineChanged,
|
||||
vsClear: vsClearAll,
|
||||
startBarcodeScan: startBarcodeScan,
|
||||
toggleVin: toggleVin,
|
||||
decodeVin: decodeVin,
|
||||
};
|
||||
|
||||
// ─── INIT ───
|
||||
|
||||
Reference in New Issue
Block a user