- Extraído CSS inline de 15 templates POS + 13 templates Dashboard - CSS movido a archivos .css externos en pos/static/css/ y dashboard/ - Generados .min.css vía minify-assets.sh - Nginx auto-serve transparente para .min.css - Tests: 73/73 pasando - Script: scripts/extract-inline-css.py
616 lines
27 KiB
HTML
616 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Diagramas - Nexus Autoparts</title>
|
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔧</text></svg>">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Orbitron:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
|
<link rel="stylesheet" href="/shared.css">
|
|
<link rel="stylesheet" href="diagrams.css">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Shared Navigation -->
|
|
<div id="shared-nav"></div>
|
|
<script src="/nav.js"></script>
|
|
|
|
<!-- Main Content -->
|
|
<div class="main-container">
|
|
|
|
<!-- Vehicle Selection -->
|
|
<div style="margin-bottom: 1.5rem;">
|
|
<h2 style="font-family: 'Orbitron', monospace; font-size: 1.3rem; margin-bottom: 1rem; color: var(--accent);">
|
|
<i class="fas fa-drafting-compass"></i> Diagramas de Suspensión y Dirección
|
|
</h2>
|
|
<p style="color: var(--text-secondary); margin-bottom: 1.25rem; font-size: 0.9rem;">
|
|
Selecciona tu vehículo para ver los diagramas MOOG disponibles, o usa "Ver Todos" para navegar la galería completa.
|
|
</p>
|
|
|
|
<div class="search-section">
|
|
<select class="filter-select" id="brandSelect" onchange="onBrandChange()">
|
|
<option value="">Marca</option>
|
|
</select>
|
|
<select class="filter-select" id="modelSelect" onchange="onModelChange()" disabled>
|
|
<option value="">Modelo</option>
|
|
</select>
|
|
<select class="filter-select" id="yearSelect" onchange="onYearChange()" disabled>
|
|
<option value="">Año</option>
|
|
</select>
|
|
<select class="filter-select" id="engineSelect" onchange="onEngineChange()" disabled>
|
|
<option value="">Motor</option>
|
|
</select>
|
|
<button class="type-btn" id="browseAllBtn" onclick="toggleBrowseAll()">
|
|
<i class="fas fa-th"></i> Ver Todos
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vehicle info bar -->
|
|
<div id="vehicleInfoBar" style="display: none;"></div>
|
|
|
|
<!-- Stats (shown in browse all mode) -->
|
|
<div class="stats-bar" id="statsBar" style="display: none;">
|
|
<div>Diagramas: <span id="diagramCount">0</span></div>
|
|
<div>Frontal: <span id="frontCount">0</span></div>
|
|
<div>Direccion: <span id="steeringCount">0</span></div>
|
|
<div>Trasera: <span id="rearCount">0</span></div>
|
|
</div>
|
|
|
|
<!-- Browse All search/filter (shown in browse mode) -->
|
|
<div id="browseAllControls" style="display: none;">
|
|
<div class="search-section">
|
|
<div class="search-wrapper">
|
|
<i class="fas fa-search"></i>
|
|
<input type="text" class="search-input" id="searchInput"
|
|
placeholder="Buscar por código (F200, S341, R004...)">
|
|
</div>
|
|
<div class="type-filters">
|
|
<button class="type-btn active" data-type="all">Todos</button>
|
|
<button class="type-btn" data-type="F"><i class="fas fa-car"></i> Delantera</button>
|
|
<button class="type-btn" data-type="S"><i class="fas fa-cog"></i> Dirección</button>
|
|
<button class="type-btn" data-type="R"><i class="fas fa-car-rear"></i> Trasera</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Diagram Grid -->
|
|
<div class="diagram-grid" id="diagramGrid">
|
|
<div class="empty-state" style="grid-column: 1/-1;">
|
|
<i class="fas fa-car" style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.3;"></i>
|
|
<p>Selecciona un vehículo arriba para ver sus diagramas</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div class="pagination" id="pagination"></div>
|
|
</div>
|
|
|
|
<!-- Viewer Overlay -->
|
|
<div class="viewer-overlay" id="viewerOverlay">
|
|
<button class="close-btn" id="closeViewer"><i class="fas fa-times"></i></button>
|
|
<button class="viewer-nav-btn prev" id="prevDiagram"><i class="fas fa-chevron-left"></i></button>
|
|
<button class="viewer-nav-btn next" id="nextDiagram"><i class="fas fa-chevron-right"></i></button>
|
|
|
|
<div class="viewer-layout">
|
|
<!-- Diagram Image -->
|
|
<div class="viewer-diagram" style="position: relative;">
|
|
<div class="viewer-toolbar">
|
|
<div>
|
|
<div class="title" id="viewerTitle">F200</div>
|
|
<div class="subtitle" id="viewerSubtitle">Suspension Delantera</div>
|
|
</div>
|
|
</div>
|
|
<div class="viewer-img-container" id="imgContainer">
|
|
<img id="viewerImg" src="" alt="Diagram">
|
|
</div>
|
|
<div class="zoom-controls">
|
|
<button class="zoom-btn" id="zoomOut"><i class="fas fa-minus"></i></button>
|
|
<span class="zoom-level" id="zoomLevel">100%</span>
|
|
<button class="zoom-btn" id="zoomIn"><i class="fas fa-plus"></i></button>
|
|
<button class="zoom-btn" id="zoomFit"><i class="fas fa-expand"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Parts Panel -->
|
|
<div class="viewer-parts">
|
|
<div class="parts-header">
|
|
<i class="fas fa-list-ul" style="color: var(--accent)"></i>
|
|
<h3>Partes del Diagrama</h3>
|
|
<span class="count" id="partsCount">0</span>
|
|
</div>
|
|
<div class="parts-search">
|
|
<input type="text" id="partsFilter" placeholder="Filtrar partes...">
|
|
</div>
|
|
<div class="parts-list" id="partsList">
|
|
<div class="loading-state">
|
|
<i class="fas fa-spinner spinner"></i>
|
|
<p>Selecciona un diagrama</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const API = '';
|
|
let allDiagrams = [];
|
|
let filteredDiagrams = [];
|
|
let currentPage = 1;
|
|
const perPage = 48;
|
|
let currentViewerIdx = -1;
|
|
let currentZoom = 1;
|
|
let isDragging = false;
|
|
let dragStart = { x: 0, y: 0 };
|
|
let scrollStart = { x: 0, y: 0 };
|
|
let browseAllMode = false;
|
|
let selectedMYEId = null;
|
|
|
|
// --- Vehicle-first navigation ---
|
|
async function loadBrands() {
|
|
try {
|
|
const res = await fetch(`${API}/api/brands`);
|
|
const brands = await res.json();
|
|
const sel = document.getElementById('brandSelect');
|
|
brands.forEach(b => {
|
|
const opt = document.createElement('option');
|
|
opt.value = b;
|
|
opt.textContent = b;
|
|
sel.appendChild(opt);
|
|
});
|
|
} catch(e) {}
|
|
}
|
|
|
|
async function onBrandChange() {
|
|
const brand = document.getElementById('brandSelect').value;
|
|
const modelSel = document.getElementById('modelSelect');
|
|
const yearSel = document.getElementById('yearSelect');
|
|
const engineSel = document.getElementById('engineSelect');
|
|
|
|
modelSel.innerHTML = '<option value="">Modelo</option>';
|
|
yearSel.innerHTML = '<option value="">Año</option>';
|
|
engineSel.innerHTML = '<option value="">Motor</option>';
|
|
modelSel.disabled = true;
|
|
yearSel.disabled = true;
|
|
engineSel.disabled = true;
|
|
selectedMYEId = null;
|
|
|
|
if (!brand) return;
|
|
|
|
try {
|
|
const res = await fetch(`${API}/api/models?brand=${encodeURIComponent(brand)}`);
|
|
const models = await res.json();
|
|
models.forEach(m => {
|
|
const opt = document.createElement('option');
|
|
opt.value = m;
|
|
opt.textContent = m;
|
|
modelSel.appendChild(opt);
|
|
});
|
|
modelSel.disabled = false;
|
|
} catch(e) {}
|
|
}
|
|
|
|
async function onModelChange() {
|
|
const brand = document.getElementById('brandSelect').value;
|
|
const model = document.getElementById('modelSelect').value;
|
|
const yearSel = document.getElementById('yearSelect');
|
|
const engineSel = document.getElementById('engineSelect');
|
|
|
|
yearSel.innerHTML = '<option value="">Año</option>';
|
|
engineSel.innerHTML = '<option value="">Motor</option>';
|
|
yearSel.disabled = true;
|
|
engineSel.disabled = true;
|
|
selectedMYEId = null;
|
|
|
|
if (!model) return;
|
|
|
|
try {
|
|
const res = await fetch(`${API}/api/years?brand=${encodeURIComponent(brand)}&model=${encodeURIComponent(model)}`);
|
|
const years = await res.json();
|
|
years.forEach(y => {
|
|
const opt = document.createElement('option');
|
|
opt.value = y;
|
|
opt.textContent = y;
|
|
yearSel.appendChild(opt);
|
|
});
|
|
yearSel.disabled = false;
|
|
} catch(e) {}
|
|
}
|
|
|
|
async function onYearChange() {
|
|
const brand = document.getElementById('brandSelect').value;
|
|
const model = document.getElementById('modelSelect').value;
|
|
const year = document.getElementById('yearSelect').value;
|
|
const engineSel = document.getElementById('engineSelect');
|
|
|
|
engineSel.innerHTML = '<option value="">Motor</option>';
|
|
engineSel.disabled = true;
|
|
selectedMYEId = null;
|
|
|
|
if (!year) return;
|
|
|
|
try {
|
|
const res = await fetch(`${API}/api/engines?brand=${encodeURIComponent(brand)}&model=${encodeURIComponent(model)}&year=${year}`);
|
|
const engines = await res.json();
|
|
engines.forEach(e => {
|
|
const opt = document.createElement('option');
|
|
opt.value = e;
|
|
opt.textContent = e;
|
|
engineSel.appendChild(opt);
|
|
});
|
|
engineSel.disabled = false;
|
|
|
|
// If only one engine, auto-select
|
|
if (engines.length === 1) {
|
|
engineSel.value = engines[0];
|
|
onEngineChange();
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
|
|
async function onEngineChange() {
|
|
const brand = document.getElementById('brandSelect').value;
|
|
const model = document.getElementById('modelSelect').value;
|
|
const year = document.getElementById('yearSelect').value;
|
|
const engine = document.getElementById('engineSelect').value;
|
|
|
|
if (!engine) { selectedMYEId = null; return; }
|
|
|
|
// Get mye_id
|
|
try {
|
|
const res = await fetch(`${API}/api/model-year-engine?brand=${encodeURIComponent(brand)}&model=${encodeURIComponent(model)}&year=${year}&with_parts=false&per_page=100`);
|
|
const result = await res.json();
|
|
const records = result.data || result;
|
|
const match = records.find(r => r.engine === engine);
|
|
|
|
if (match) {
|
|
selectedMYEId = match.id;
|
|
loadVehicleDiagrams(match.id, `${brand} ${model} ${year} - ${engine}`);
|
|
}
|
|
} catch(e) {
|
|
console.error('Error finding MYE:', e);
|
|
}
|
|
}
|
|
|
|
async function loadVehicleDiagrams(myeId, vehicleLabel) {
|
|
const grid = document.getElementById('diagramGrid');
|
|
grid.innerHTML = '<div class="loading-state" style="grid-column:1/-1"><i class="fas fa-spinner spinner" style="font-size:2rem"></i><p>Cargando diagramas...</p></div>';
|
|
|
|
// Hide browse-all controls
|
|
document.getElementById('browseAllControls').style.display = 'none';
|
|
document.getElementById('statsBar').style.display = 'none';
|
|
document.getElementById('pagination').innerHTML = '';
|
|
|
|
// Show vehicle info
|
|
const infoBar = document.getElementById('vehicleInfoBar');
|
|
infoBar.style.display = 'flex';
|
|
infoBar.className = 'stats-bar';
|
|
infoBar.innerHTML = `<div><i class="fas fa-car" style="color:var(--accent)"></i> <span>${vehicleLabel}</span></div>`;
|
|
|
|
try {
|
|
const res = await fetch(`${API}/api/vehicles/${myeId}/diagrams`);
|
|
const diagrams = await res.json();
|
|
|
|
filteredDiagrams = diagrams.map(d => ({
|
|
...d,
|
|
image_path: d.image_url ? d.image_url.replace(/^\//, '') : `static/diagrams/moog/${d.name}.jpg`
|
|
}));
|
|
|
|
if (filteredDiagrams.length === 0) {
|
|
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><i class="fas fa-drafting-compass" style="font-size:2.5rem;margin-bottom:0.75rem;opacity:0.3"></i><p>No hay diagramas disponibles para este vehículo</p></div>';
|
|
return;
|
|
}
|
|
|
|
infoBar.innerHTML += `<div>Diagramas: <span>${filteredDiagrams.length}</span></div>`;
|
|
|
|
renderGrid();
|
|
} catch (e) {
|
|
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><i class="fas fa-exclamation-triangle"></i><p>Error cargando diagramas</p></div>';
|
|
}
|
|
}
|
|
|
|
function toggleBrowseAll() {
|
|
browseAllMode = !browseAllMode;
|
|
const btn = document.getElementById('browseAllBtn');
|
|
|
|
if (browseAllMode) {
|
|
btn.classList.add('active');
|
|
btn.innerHTML = '<i class="fas fa-car"></i> Por Vehículo';
|
|
document.getElementById('browseAllControls').style.display = 'block';
|
|
document.getElementById('statsBar').style.display = 'flex';
|
|
document.getElementById('vehicleInfoBar').style.display = 'none';
|
|
loadAllDiagrams();
|
|
} else {
|
|
btn.classList.remove('active');
|
|
btn.innerHTML = '<i class="fas fa-th"></i> Ver Todos';
|
|
document.getElementById('browseAllControls').style.display = 'none';
|
|
document.getElementById('statsBar').style.display = 'none';
|
|
document.getElementById('pagination').innerHTML = '';
|
|
|
|
const grid = document.getElementById('diagramGrid');
|
|
if (selectedMYEId) {
|
|
const brand = document.getElementById('brandSelect').value;
|
|
const model = document.getElementById('modelSelect').value;
|
|
const year = document.getElementById('yearSelect').value;
|
|
const engine = document.getElementById('engineSelect').value;
|
|
loadVehicleDiagrams(selectedMYEId, `${brand} ${model} ${year} - ${engine}`);
|
|
} else {
|
|
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><i class="fas fa-car" style="font-size:3rem;margin-bottom:1rem;opacity:0.3"></i><p>Selecciona un vehículo arriba para ver sus diagramas</p></div>';
|
|
}
|
|
}
|
|
}
|
|
|
|
async function loadAllDiagrams() {
|
|
const grid = document.getElementById('diagramGrid');
|
|
grid.innerHTML = '<div class="loading-state" style="grid-column:1/-1"><i class="fas fa-spinner spinner" style="font-size:2rem"></i><p>Cargando todos los diagramas...</p></div>';
|
|
|
|
try {
|
|
const res = await fetch(`${API}/api/diagrams`);
|
|
const data = await res.json();
|
|
allDiagrams = data.filter(d => d.name && /^[FSR]\d{3}$/.test(d.name)).map(d => ({
|
|
...d,
|
|
image_path: `static/diagrams/moog/${d.name}.jpg`,
|
|
}));
|
|
applyBrowseFilters();
|
|
} catch(e) {
|
|
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><p>Error cargando diagramas</p></div>';
|
|
}
|
|
}
|
|
|
|
function applyBrowseFilters() {
|
|
const searchEl = document.getElementById('searchInput');
|
|
const query = searchEl ? searchEl.value.toUpperCase().trim() : '';
|
|
const typeBtn = document.querySelector('#browseAllControls .type-btn.active');
|
|
const typeFilter = typeBtn ? typeBtn.dataset.type : 'all';
|
|
|
|
filteredDiagrams = allDiagrams.filter(d => {
|
|
const name = (d.name || '').toUpperCase();
|
|
if (typeFilter !== 'all' && !name.startsWith(typeFilter)) return false;
|
|
if (query && !name.includes(query) && !(d.name_es || '').toUpperCase().includes(query)) return false;
|
|
return true;
|
|
});
|
|
|
|
filteredDiagrams.sort((a, b) => {
|
|
const order = { F: 0, S: 1, R: 2 };
|
|
return (order[a.name[0]] ?? 3) - (order[b.name[0]] ?? 3) || a.name.localeCompare(b.name);
|
|
});
|
|
|
|
const front = filteredDiagrams.filter(d => d.name.startsWith('F')).length;
|
|
const steer = filteredDiagrams.filter(d => d.name.startsWith('S')).length;
|
|
const rear = filteredDiagrams.filter(d => d.name.startsWith('R')).length;
|
|
document.getElementById('diagramCount').textContent = filteredDiagrams.length;
|
|
document.getElementById('frontCount').textContent = front;
|
|
document.getElementById('steeringCount').textContent = steer;
|
|
document.getElementById('rearCount').textContent = rear;
|
|
|
|
currentPage = 1;
|
|
renderGrid();
|
|
}
|
|
|
|
// --- Render grid ---
|
|
function renderGrid() {
|
|
const grid = document.getElementById('diagramGrid');
|
|
const start = (currentPage - 1) * perPage;
|
|
const pageItems = filteredDiagrams.slice(start, start + perPage);
|
|
|
|
if (pageItems.length === 0) {
|
|
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><i class="fas fa-search" style="font-size:2rem;margin-bottom:0.5rem;opacity:0.3"></i><p>No se encontraron diagramas</p></div>';
|
|
document.getElementById('pagination').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
grid.innerHTML = pageItems.map((d, i) => {
|
|
const globalIdx = start + i;
|
|
const type = (d.name || '')[0];
|
|
const badgeClass = type === 'F' ? 'badge-front' : type === 'S' ? 'badge-steering' : 'badge-rear';
|
|
const badgeText = type === 'F' ? 'Delantera' : type === 'S' ? 'Dirección' : 'Trasera';
|
|
const imgSrc = d.image_url ? d.image_url : (d.image_path || `static/diagrams/moog/${d.name}.jpg`);
|
|
|
|
return `
|
|
<div class="diagram-card" onclick="openViewer(${globalIdx})">
|
|
<img class="diagram-card-img" src="${imgSrc}" alt="${d.name}" loading="lazy"
|
|
onerror="this.style.background='#333';this.style.minHeight='150px'">
|
|
<div class="diagram-card-body">
|
|
<div class="diagram-card-title">${d.name}</div>
|
|
<div class="diagram-card-sub">${d.name_es || d.group_name || ''}</div>
|
|
<span class="diagram-card-badge ${badgeClass}">${badgeText}</span>
|
|
</div>
|
|
</div>`;
|
|
}).join('');
|
|
|
|
renderPagination();
|
|
}
|
|
|
|
function renderPagination() {
|
|
const totalPages = Math.ceil(filteredDiagrams.length / perPage);
|
|
const pag = document.getElementById('pagination');
|
|
if (totalPages <= 1) { pag.innerHTML = ''; return; }
|
|
|
|
let html = `<button class="page-btn" onclick="goPage(${currentPage-1})" ${currentPage<=1?'disabled':''}><i class="fas fa-chevron-left"></i></button>`;
|
|
const start = Math.max(1, currentPage - 3);
|
|
const end = Math.min(totalPages, currentPage + 3);
|
|
if (start > 1) {
|
|
html += `<button class="page-btn" onclick="goPage(1)">1</button>`;
|
|
if (start > 2) html += `<span style="color:var(--text-secondary);padding:0 0.5rem">...</span>`;
|
|
}
|
|
for (let p = start; p <= end; p++) {
|
|
html += `<button class="page-btn ${p===currentPage?'active':''}" onclick="goPage(${p})">${p}</button>`;
|
|
}
|
|
if (end < totalPages) {
|
|
if (end < totalPages-1) html += `<span style="color:var(--text-secondary);padding:0 0.5rem">...</span>`;
|
|
html += `<button class="page-btn" onclick="goPage(${totalPages})">${totalPages}</button>`;
|
|
}
|
|
html += `<button class="page-btn" onclick="goPage(${currentPage+1})" ${currentPage>=totalPages?'disabled':''}><i class="fas fa-chevron-right"></i></button>`;
|
|
pag.innerHTML = html;
|
|
}
|
|
|
|
function goPage(p) {
|
|
const totalPages = Math.ceil(filteredDiagrams.length / perPage);
|
|
if (p < 1 || p > totalPages) return;
|
|
currentPage = p;
|
|
renderGrid();
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
}
|
|
|
|
// --- Viewer ---
|
|
function openViewer(idx) {
|
|
currentViewerIdx = idx;
|
|
const d = filteredDiagrams[idx];
|
|
if (!d) return;
|
|
|
|
const overlay = document.getElementById('viewerOverlay');
|
|
overlay.classList.add('active');
|
|
document.body.style.overflow = 'hidden';
|
|
loadDiagramInViewer(d);
|
|
}
|
|
|
|
function loadDiagramInViewer(d) {
|
|
const type = (d.name || '')[0];
|
|
const typeLabel = type === 'F' ? 'Suspensión Delantera' : type === 'S' ? 'Dirección' : 'Suspensión Trasera';
|
|
|
|
document.getElementById('viewerTitle').textContent = d.name;
|
|
document.getElementById('viewerSubtitle').textContent = d.name_es || typeLabel;
|
|
|
|
const img = document.getElementById('viewerImg');
|
|
img.src = d.image_url ? d.image_url : (d.image_path || `static/diagrams/moog/${d.name}.jpg`);
|
|
img.classList.remove('zoomed');
|
|
currentZoom = 1;
|
|
img.style.transform = '';
|
|
document.getElementById('zoomLevel').textContent = '100%';
|
|
|
|
const myeParam = selectedMYEId ? `?mye_id=${selectedMYEId}` : '';
|
|
loadDiagramParts(d.id, myeParam);
|
|
}
|
|
|
|
async function loadDiagramParts(diagramId, queryParams) {
|
|
const list = document.getElementById('partsList');
|
|
list.innerHTML = '<div class="loading-state"><i class="fas fa-spinner spinner"></i><p>Cargando partes...</p></div>';
|
|
document.getElementById('partsCount').textContent = '...';
|
|
|
|
try {
|
|
const res = await fetch(`${API}/api/diagrams/${diagramId}/parts${queryParams || ''}`);
|
|
const parts = await res.json();
|
|
|
|
document.getElementById('partsCount').textContent = parts.length;
|
|
|
|
if (parts.length === 0) {
|
|
list.innerHTML = '<div class="empty-state"><i class="fas fa-box-open"></i><p>No hay partes vinculadas</p></div>';
|
|
return;
|
|
}
|
|
|
|
const grouped = {};
|
|
parts.forEach(p => {
|
|
const g = p.group_name_es || p.group_name || 'Otros';
|
|
if (!grouped[g]) grouped[g] = [];
|
|
grouped[g].push(p);
|
|
});
|
|
|
|
let html = '';
|
|
for (const [group, groupParts] of Object.entries(grouped)) {
|
|
html += `<div style="font-size:0.75rem;color:var(--accent);padding:0.5rem 0.25rem 0.25rem;font-weight:600;">${group}</div>`;
|
|
for (const p of groupParts) {
|
|
let xrefHtml = '';
|
|
if (p.cross_references && p.cross_references.length > 0) {
|
|
xrefHtml = `<div class="xref-list"><div class="xref-label">Refs:</div>${p.cross_references.map(x => `<span class="xref-item">${x.number}</span>`).join('')}</div>`;
|
|
}
|
|
html += `<div class="part-item" data-part-id="${p.id}"><div class="part-number">${p.part_number}</div><div class="part-name">${p.name_es || p.name || ''}</div>${xrefHtml}</div>`;
|
|
}
|
|
}
|
|
list.innerHTML = html;
|
|
} catch (e) {
|
|
list.innerHTML = '<div class="empty-state"><i class="fas fa-exclamation-triangle"></i><p>Error cargando partes</p></div>';
|
|
}
|
|
}
|
|
|
|
function closeViewer() {
|
|
document.getElementById('viewerOverlay').classList.remove('active');
|
|
document.body.style.overflow = '';
|
|
currentViewerIdx = -1;
|
|
}
|
|
|
|
function navigateViewer(delta) {
|
|
const newIdx = currentViewerIdx + delta;
|
|
if (newIdx < 0 || newIdx >= filteredDiagrams.length) return;
|
|
currentViewerIdx = newIdx;
|
|
loadDiagramInViewer(filteredDiagrams[newIdx]);
|
|
}
|
|
|
|
function setZoom(level) {
|
|
currentZoom = Math.max(0.25, Math.min(4, level));
|
|
const img = document.getElementById('viewerImg');
|
|
if (currentZoom !== 1) {
|
|
img.classList.add('zoomed');
|
|
img.style.transform = `scale(${currentZoom})`;
|
|
} else {
|
|
img.classList.remove('zoomed');
|
|
img.style.transform = '';
|
|
}
|
|
document.getElementById('zoomLevel').textContent = `${Math.round(currentZoom * 100)}%`;
|
|
}
|
|
|
|
// --- Pan (drag) ---
|
|
const imgContainer = document.getElementById('imgContainer');
|
|
imgContainer.addEventListener('mousedown', (e) => {
|
|
if (currentZoom <= 1) return;
|
|
isDragging = true;
|
|
dragStart = { x: e.clientX, y: e.clientY };
|
|
scrollStart = { x: imgContainer.scrollLeft, y: imgContainer.scrollTop };
|
|
imgContainer.style.cursor = 'grabbing';
|
|
});
|
|
window.addEventListener('mousemove', (e) => {
|
|
if (!isDragging) return;
|
|
imgContainer.scrollLeft = scrollStart.x - (e.clientX - dragStart.x);
|
|
imgContainer.scrollTop = scrollStart.y - (e.clientY - dragStart.y);
|
|
});
|
|
window.addEventListener('mouseup', () => { isDragging = false; imgContainer.style.cursor = ''; });
|
|
|
|
// Parts filter
|
|
document.getElementById('partsFilter').addEventListener('input', (e) => {
|
|
const q = e.target.value.toLowerCase();
|
|
document.querySelectorAll('.part-item').forEach(el => {
|
|
el.style.display = el.textContent.toLowerCase().includes(q) ? '' : 'none';
|
|
});
|
|
});
|
|
|
|
// Browse-all search & type filter listeners
|
|
document.getElementById('searchInput')?.addEventListener('input', () => { if (browseAllMode) applyBrowseFilters(); });
|
|
document.querySelectorAll('#browseAllControls .type-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
document.querySelectorAll('#browseAllControls .type-btn').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
if (browseAllMode) applyBrowseFilters();
|
|
});
|
|
});
|
|
|
|
// Viewer controls
|
|
document.getElementById('closeViewer').addEventListener('click', closeViewer);
|
|
document.getElementById('prevDiagram').addEventListener('click', () => navigateViewer(-1));
|
|
document.getElementById('nextDiagram').addEventListener('click', () => navigateViewer(1));
|
|
document.getElementById('zoomIn').addEventListener('click', () => setZoom(currentZoom + 0.25));
|
|
document.getElementById('zoomOut').addEventListener('click', () => setZoom(currentZoom - 0.25));
|
|
document.getElementById('zoomFit').addEventListener('click', () => setZoom(1));
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
const overlay = document.getElementById('viewerOverlay');
|
|
if (overlay.classList.contains('active')) {
|
|
if (e.key === 'Escape') closeViewer();
|
|
if (e.key === 'ArrowLeft') navigateViewer(-1);
|
|
if (e.key === 'ArrowRight') navigateViewer(1);
|
|
if (e.key === '+' || e.key === '=') setZoom(currentZoom + 0.25);
|
|
if (e.key === '-') setZoom(currentZoom - 0.25);
|
|
}
|
|
});
|
|
|
|
imgContainer.addEventListener('wheel', (e) => {
|
|
if (!document.getElementById('viewerOverlay').classList.contains('active')) return;
|
|
e.preventDefault();
|
|
setZoom(currentZoom + (e.deltaY > 0 ? -0.15 : 0.15));
|
|
});
|
|
|
|
// Initialize
|
|
loadBrands();
|
|
</script>
|
|
</body>
|
|
</html>
|