/* =========================================================================
Nexus Autoparts — Public Catalog (catalog-public.js)
Vehicle hierarchy navigation: Brand > Model > Year > Engine > Category > Group > Parts
No auth, no cart, no prices — public browsing only.
========================================================================= */
(function () {
'use strict';
// ── State ──
var state = {
level: 'brands', // brands | models | years | engines | categories | groups | parts | search
brand: null, // {id, name}
model: null, // {id, name}
year: null, // {id, value}
engine: null, // {id_mye, name, trim}
category: null, // {id, name}
group: null, // {id, name}
region: 'north-america',
page: 1,
totalPages: 1,
};
// ── Region selector (global) ──
window.setRegion = function (region) {
state.region = region;
document.querySelectorAll('.region-btn').forEach(function (b) {
b.classList.toggle('is-active', b.dataset.region === region);
});
// Reload brands with new region
state.brand = state.model = state.year = state.engine = state.category = state.group = null;
loadBrands();
};
var API = '/api/catalog';
var content = document.getElementById('content');
var breadcrumbEl = document.getElementById('breadcrumb');
var searchInput = document.getElementById('searchInput');
// Check URL for brand param
var urlParams = new URLSearchParams(window.location.search);
var initBrandId = urlParams.get('brand');
// ── Init ──
if (initBrandId) {
// Load brands, find the one matching, then navigate
fetch(API + '/brands')
.then(function (r) { return r.json(); })
.then(function (brands) {
var found = brands.find(function (b) { return b.id_brand == initBrandId; });
if (found) {
state.brand = { id: found.id_brand, name: found.name_brand };
state.level = 'models';
loadModels();
} else {
loadBrands();
}
})
.catch(function () { loadBrands(); });
} else {
loadBrands();
}
// Enter on search
searchInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') doSearch();
});
// ── Theme toggle (global) ──
window.toggleTheme = function () {
var html = document.documentElement;
var cur = html.getAttribute('data-theme');
var next = cur === 'industrial' ? 'modern' : 'industrial';
html.setAttribute('data-theme', next);
localStorage.setItem('nexus-theme', next);
};
// ── Search (global) ──
window.doSearch = function () {
var q = searchInput.value.trim();
if (!q || q.length < 2) return;
state.level = 'search';
renderBreadcrumb();
content.innerHTML = '
Buscando...
';
fetch(API + '/search?q=' + encodeURIComponent(q))
.then(function (r) { return r.json(); })
.then(function (data) { renderSearchResults(data); })
.catch(function () { content.innerHTML = 'Error en la busqueda.
'; });
};
// ── Detail modal (global) ──
window.openDetail = function (partId) {
var modal = document.getElementById('detailModal');
var body = document.getElementById('detailBody');
body.innerHTML = 'Cargando detalle...
';
modal.classList.add('open');
fetch(API + '/part/' + partId)
.then(function (r) { return r.json(); })
.then(function (d) { renderDetail(d, body); })
.catch(function () { body.innerHTML = 'Error cargando detalle.
'; });
};
window.closeDetail = function () {
document.getElementById('detailModal').classList.remove('open');
};
// Close modal on backdrop click
document.getElementById('detailModal').addEventListener('click', function (e) {
if (e.target === this) closeDetail();
});
// ── Breadcrumb ──
function renderBreadcrumb() {
var parts = [];
parts.push('Catalogo');
if (state.brand) {
parts.push('/');
parts.push('' + esc(state.brand.name) + '');
}
if (state.model) {
parts.push('/');
parts.push('' + esc(state.model.name) + '');
}
if (state.year) {
parts.push('/');
parts.push('' + esc(String(state.year.value)) + '');
}
if (state.engine) {
parts.push('/');
var engineLabel = state.engine.name + (state.engine.trim ? ' (' + state.engine.trim + ')' : '');
parts.push('' + esc(engineLabel) + '');
}
if (state.category) {
parts.push('/');
parts.push('' + esc(state.category.name) + '');
}
if (state.group) {
parts.push('/');
parts.push('' + esc(state.group.name) + '');
}
if (state.level === 'search') {
parts.push('/');
parts.push('Busqueda');
}
breadcrumbEl.innerHTML = parts.join('');
}
// Global nav
window.catalogNav = function (level) {
if (level === 'brands') {
state.brand = state.model = state.year = state.engine = state.category = state.group = null;
state.level = 'brands';
loadBrands();
} else if (level === 'models') {
state.model = state.year = state.engine = state.category = state.group = null;
state.level = 'models';
loadModels();
} else if (level === 'years') {
state.year = state.engine = state.category = state.group = null;
state.level = 'years';
loadYears();
} else if (level === 'engines') {
state.engine = state.category = state.group = null;
state.level = 'engines';
loadEngines();
} else if (level === 'categories') {
state.category = state.group = null;
state.level = 'categories';
loadCategories();
} else if (level === 'groups') {
state.group = null;
state.level = 'groups';
loadGroups();
}
};
// ── Data loaders ──
function loadBrands() {
state.level = 'brands';
renderBreadcrumb();
content.innerHTML = 'Cargando marcas...
';
fetch(API + '/brands?region=' + (state.region || 'north-america'))
.then(function (r) { return r.json(); })
.then(function (brands) {
var html = 'Selecciona una Marca
';
brands.forEach(function (b) {
html += '
';
html += '' + esc(b.name_brand) + '';
html += '
';
});
html += '
';
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando marcas.
'; });
}
window.selectBrand = function (id, name) {
state.brand = { id: id, name: name };
state.level = 'models';
loadModels();
};
function loadModels() {
renderBreadcrumb();
content.innerHTML = 'Cargando modelos...
';
fetch(API + '/models?brand_id=' + state.brand.id)
.then(function (r) { return r.json(); })
.then(function (models) {
var html = '' + esc(state.brand.name) + ' — Modelos
';
models.forEach(function (m) {
html += '
';
html += '' + esc(m.name_model) + '';
html += '
';
});
html += '
';
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando modelos.
'; });
}
window.selectModel = function (id, name) {
state.model = { id: id, name: name };
state.level = 'years';
loadYears();
};
function loadYears() {
renderBreadcrumb();
content.innerHTML = 'Cargando anos...
';
fetch(API + '/years?model_id=' + state.model.id)
.then(function (r) { return r.json(); })
.then(function (years) {
var html = '' + esc(state.brand.name) + ' ' + esc(state.model.name) + ' — Anos
';
years.forEach(function (y) {
html += '
';
html += '' + y.year_car + '';
html += '
';
});
html += '
';
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando anos.
'; });
}
window.selectYear = function (id, value) {
state.year = { id: id, value: value };
state.level = 'engines';
loadEngines();
};
function loadEngines() {
renderBreadcrumb();
content.innerHTML = 'Cargando motores...
';
fetch(API + '/engines?model_id=' + state.model.id + '&year_id=' + state.year.id)
.then(function (r) { return r.json(); })
.then(function (engines) {
var html = '' + esc(state.brand.name) + ' ' + esc(state.model.name) + ' ' + state.year.value + ' — Motor
';
html += '';
engines.forEach(function (e) {
var label = e.name_engine + (e.trim_level ? ' (' + e.trim_level + ')' : '');
html += '
';
html += '' + esc(label) + '';
html += '
';
});
html += '
';
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando motores.
'; });
}
window.selectEngine = function (id_mye, name, trim) {
state.engine = { id_mye: id_mye, name: name, trim: trim };
state.level = 'categories';
loadCategories();
};
function loadCategories() {
renderBreadcrumb();
content.innerHTML = 'Cargando categorias...
';
fetch(API + '/categories?mye_id=' + state.engine.id_mye)
.then(function (r) { return r.json(); })
.then(function (cats) {
if (!cats.length) {
content.innerHTML = 'Categorias
No se encontraron categorias con partes para este vehiculo.
';
return;
}
var html = 'Categorias
';
cats.forEach(function (c) {
html += '
';
html += '' + esc(c.name) + '';
html += '' + c.part_count + '';
html += '
';
});
html += '
';
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando categorias.
'; });
}
window.selectCategory = function (id, name) {
state.category = { id: id, name: name };
state.level = 'groups';
loadGroups();
};
function loadGroups() {
renderBreadcrumb();
content.innerHTML = 'Cargando grupos...
';
fetch(API + '/groups?mye_id=' + state.engine.id_mye + '&category_id=' + state.category.id)
.then(function (r) { return r.json(); })
.then(function (groups) {
if (!groups.length) {
content.innerHTML = '' + esc(state.category.name) + '
No se encontraron sub-grupos.
';
return;
}
var html = '' + esc(state.category.name) + '
';
groups.forEach(function (g) {
html += '
';
html += '' + esc(g.name) + '';
html += '' + g.part_count + '';
html += '
';
});
html += '
';
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando grupos.
'; });
}
window.selectGroup = function (id, name) {
state.group = { id: id, name: name };
state.level = 'parts';
state.page = 1;
loadParts();
};
function loadParts() {
renderBreadcrumb();
content.innerHTML = 'Cargando partes...
';
var url = API + '/parts?mye_id=' + state.engine.id_mye + '&group_id=' + state.group.id + '&page=' + state.page;
fetch(url)
.then(function (r) { return r.json(); })
.then(function (resp) {
var parts = resp.data;
var pag = resp.pagination;
state.totalPages = pag.total_pages;
if (!parts.length) {
content.innerHTML = '' + esc(state.group.name) + '
No se encontraron partes.
';
return;
}
var html = '' + esc(state.group.name) + ' (' + pag.total + ' partes)
';
html += '';
parts.forEach(function (p) {
html += '
';
html += '
';
html += '
' + esc(p.oem_part_number) + '
';
html += '
' + esc(p.name || '') + '
';
if (p.description) html += '
' + esc(p.description) + '
';
html += '
';
html += '
';
if (p.image_url) {
html += '
 + ')
';
}
html += '
';
});
html += '
';
// Pagination
if (pag.total_pages > 1) {
html += '';
}
content.innerHTML = html;
})
.catch(function () { content.innerHTML = 'Error cargando partes.
'; });
}
window.partsPage = function (p) {
state.page = p;
loadParts();
window.scrollTo({ top: 0, behavior: 'smooth' });
};
// ── Search results ──
function renderSearchResults(results) {
renderBreadcrumb();
if (!results.length) {
content.innerHTML = 'Busqueda
No se encontraron resultados.
';
return;
}
var html = 'Resultados (' + results.length + ')
';
results.forEach(function (p) {
html += '
';
html += '
';
html += '
' + esc(p.oem_part_number) + '
';
html += '
' + esc(p.name || '') + '
';
if (p.vehicle_info) html += '
' + esc(p.vehicle_info) + '
';
html += '
';
html += '
';
if (p.image_url) {
html += '
 + ')
';
}
html += '
';
});
html += '
';
content.innerHTML = html;
}
// ── Part detail ──
function renderDetail(d, body) {
if (!d || !d.part) {
body.innerHTML = 'Parte no encontrada.
';
return;
}
var p = d.part;
var html = '';
html += '' + esc(p.oem_part_number) + '
';
html += '' + esc(p.name || '') + '
';
if (p.category_name) html += '' + esc(p.category_name) + (p.group_name ? ' / ' + esc(p.group_name) : '') + '
';
if (p.description) html += '' + esc(p.description) + '
';
if (p.image_url) {
html += '';
html += '
 + ')
';
html += '
';
}
// Alternatives
if (d.alternatives && d.alternatives.length) {
html += '';
html += '
Alternativas y Cross-References (' + d.alternatives.length + ')
';
html += '
| Numero | Fabricante | Nombre | Tipo |
';
d.alternatives.forEach(function (a) {
html += '';
html += '| ' + esc(a.part_number || '') + ' | ';
html += '' + esc(a.manufacturer || '') + ' | ';
html += '' + esc(a.name || '-') + ' | ';
html += '' + esc(a.type === 'aftermarket' ? 'Aftermarket' : 'Cross-Ref') + ' | ';
html += '
';
});
html += '
';
}
// Bodegas
if (d.bodegas && d.bodegas.length) {
html += '';
html += '
Disponibilidad en Bodegas (' + d.bodegas.length + ')
';
html += '
| Bodega | Stock | Ubicacion |
';
d.bodegas.forEach(function (b) {
html += '';
html += '| ' + esc(b.business_name || '') + ' | ';
html += '' + b.stock + ' | ';
html += '' + esc(b.location || '-') + ' | ';
html += '
';
});
html += '
';
}
body.innerHTML = html;
}
// ── Helpers ──
function esc(s) {
if (!s) return '';
var d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
function escAttr(s) {
return esc(s).replace(/'/g, "\\'").replace(/"/g, '"');
}
})();