feat: selector de region en catalogo publico (MX/USA/CA, Europa, Asia, Todos)

Filtra marcas por mercado regional. 4 opciones:
- Mexico/USA/Canada (36 marcas)
- Europa (27 marcas)
- Asia (15 marcas)
- Todos (546 marcas)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 23:33:43 +00:00
parent 989a178143
commit a389228048
3 changed files with 94 additions and 17 deletions

View File

@@ -87,6 +87,34 @@
} }
.search-wrapper button:hover { background: var(--btn-primary-bg-hover); } .search-wrapper button:hover { background: var(--btn-primary-bg-hover); }
/* ── Region bar ── */
.region-bar {
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
padding: var(--space-2) 0;
}
.region-inner {
max-width: var(--content-xl); margin: 0 auto;
padding: 0 var(--space-6);
display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
}
.region-label {
font-size: var(--text-caption); font-weight: var(--font-weight-semibold);
color: var(--color-text-muted); text-transform: uppercase;
letter-spacing: var(--tracking-wider); margin-right: var(--space-2);
}
.region-btn {
background: var(--btn-ghost-bg); border: 1px solid var(--btn-ghost-border);
color: var(--btn-ghost-text); padding: var(--space-1) var(--space-3);
border-radius: var(--radius-md); cursor: pointer; font-size: var(--text-caption);
font-family: var(--font-body); transition: var(--transition-fast);
}
.region-btn:hover { background: var(--color-surface-2); color: var(--color-text-primary); }
.region-btn.is-active {
background: var(--color-primary-muted); color: var(--color-primary);
border-color: var(--color-primary); font-weight: var(--font-weight-semibold);
}
/* ── Breadcrumb ── */ /* ── Breadcrumb ── */
.breadcrumb { .breadcrumb {
max-width: var(--content-xl); margin: 0 auto; max-width: var(--content-xl); margin: 0 auto;
@@ -309,6 +337,17 @@
</div> </div>
</header> </header>
<!-- Country / Region selector -->
<div class="region-bar">
<div class="region-inner">
<span class="region-label">Región:</span>
<button class="region-btn is-active" data-region="north-america" onclick="setRegion('north-america')">🇲🇽 México, 🇺🇸 USA, 🇨🇦 Canadá</button>
<button class="region-btn" data-region="europe" onclick="setRegion('europe')">🇪🇺 Europa</button>
<button class="region-btn" data-region="asia" onclick="setRegion('asia')">🇯🇵 Asia</button>
<button class="region-btn" data-region="all" onclick="setRegion('all')">🌐 Todos</button>
</div>
</div>
<div class="search-bar"> <div class="search-bar">
<div class="search-wrapper"> <div class="search-wrapper">
<input type="text" id="searchInput" placeholder="Buscar por numero de parte o nombre..." autocomplete="off"> <input type="text" id="searchInput" placeholder="Buscar por numero de parte o nombre..." autocomplete="off">

View File

@@ -16,10 +16,22 @@
engine: null, // {id_mye, name, trim} engine: null, // {id_mye, name, trim}
category: null, // {id, name} category: null, // {id, name}
group: null, // {id, name} group: null, // {id, name}
region: 'north-america',
page: 1, page: 1,
totalPages: 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 API = '/api/catalog';
var content = document.getElementById('content'); var content = document.getElementById('content');
var breadcrumbEl = document.getElementById('breadcrumb'); var breadcrumbEl = document.getElementById('breadcrumb');
@@ -170,7 +182,7 @@
state.level = 'brands'; state.level = 'brands';
renderBreadcrumb(); renderBreadcrumb();
content.innerHTML = '<div class="loading">Cargando marcas...</div>'; content.innerHTML = '<div class="loading">Cargando marcas...</div>';
fetch(API + '/brands') fetch(API + '/brands?region=' + (state.region || 'north-america'))
.then(function (r) { return r.json(); }) .then(function (r) { return r.json(); })
.then(function (brands) { .then(function (brands) {
var html = '<h2>Selecciona una Marca</h2><div class="nav-grid">'; var html = '<h2>Selecciona una Marca</h2><div class="nav-grid">';

View File

@@ -220,28 +220,54 @@ def enhanced_search_js():
# Public Catalog API — No auth required # Public Catalog API — No auth required
# ============================================================================ # ============================================================================
NORTH_AMERICA_BRANDS = ( REGION_BRANDS = {
'ACURA', 'AUDI', 'BMW', 'BUICK', 'CADILLAC', 'CHEVROLET', 'CHRYSLER', 'north-america': (
'DODGE', 'FIAT', 'FORD', 'GMC', 'HONDA', 'HYUNDAI', 'INFINITI', 'ACURA', 'AUDI', 'BMW', 'BUICK', 'CADILLAC', 'CHEVROLET', 'CHRYSLER',
'JAGUAR', 'JEEP', 'KIA', 'LAND ROVER', 'LEXUS', 'LINCOLN', 'MAZDA', 'DODGE', 'FIAT', 'FORD', 'GMC', 'HONDA', 'HYUNDAI', 'INFINITI',
'MERCEDES-BENZ', 'MINI', 'MITSUBISHI', 'NISSAN', 'PEUGEOT', 'PORSCHE', 'JAGUAR', 'JEEP', 'KIA', 'LAND ROVER', 'LEXUS', 'LINCOLN', 'MAZDA',
'RAM', 'RENAULT', 'SEAT', 'SUBARU', 'SUZUKI', 'TESLA', 'TOYOTA', 'MERCEDES-BENZ', 'MINI', 'MITSUBISHI', 'NISSAN', 'PEUGEOT', 'PORSCHE',
'VOLVO', 'VW', 'RAM', 'RENAULT', 'SEAT', 'SUBARU', 'SUZUKI', 'TESLA', 'TOYOTA',
) 'VOLVO', 'VW',
),
'europe': (
'ALFA ROMEO', 'ASTON MARTIN', 'AUDI', 'BENTLEY', 'BMW', 'CITROEN',
'DACIA', 'DS', 'FERRARI', 'FIAT', 'JAGUAR', 'LAMBORGHINI', 'LAND ROVER',
'MASERATI', 'MERCEDES-BENZ', 'MINI', 'OPEL', 'PEUGEOT', 'PORSCHE',
'RENAULT', 'ROLLS-ROYCE', 'SAAB', 'SEAT', 'SKODA', 'SMART',
'VAUXHALL', 'VOLVO', 'VW',
),
'asia': (
'ACURA', 'DAIHATSU', 'HONDA', 'HYUNDAI', 'INFINITI', 'ISUZU', 'KIA',
'LEXUS', 'MAZDA', 'MITSUBISHI', 'NISSAN', 'SSANGYONG', 'SUBARU',
'SUZUKI', 'TOYOTA',
),
}
NORTH_AMERICA_BRANDS = REGION_BRANDS['north-america']
@app.route('/api/catalog/brands') @app.route('/api/catalog/brands')
def api_catalog_brands(): def api_catalog_brands():
region = request.args.get('region', 'north-america')
session = Session() session = Session()
try: try:
rows = session.execute(text(""" if region == 'all':
SELECT DISTINCT b.id_brand, b.name_brand rows = session.execute(text("""
FROM brands b SELECT DISTINCT b.id_brand, b.name_brand
JOIN models m ON m.brand_id = b.id_brand FROM brands b
JOIN model_year_engine mye ON mye.model_id = m.id_model JOIN models m ON m.brand_id = b.id_brand
WHERE b.name_brand = ANY(:brands) JOIN model_year_engine mye ON mye.model_id = m.id_model
ORDER BY b.name_brand ORDER BY b.name_brand
"""), {'brands': list(NORTH_AMERICA_BRANDS)}).mappings().all() """)).mappings().all()
else:
brand_list = list(REGION_BRANDS.get(region, NORTH_AMERICA_BRANDS))
rows = session.execute(text("""
SELECT DISTINCT b.id_brand, b.name_brand
FROM brands b
JOIN models m ON m.brand_id = b.id_brand
JOIN model_year_engine mye ON mye.model_id = m.id_model
WHERE b.name_brand = ANY(:brands)
ORDER BY b.name_brand
"""), {'brands': brand_list}).mappings().all()
return jsonify([{'id_brand': r['id_brand'], 'name_brand': r['name_brand']} for r in rows]) return jsonify([{'id_brand': r['id_brand'], 'name_brand': r['name_brand']} for r in rows])
finally: finally:
session.close() session.close()