Files
Autoparts-DB/dashboard/admin.html
consultoria-as e66b18f6ae Add admin panel, enhanced search, Gonher import and expand API
- Add admin interface (admin.html, admin.js) for managing catalog data
- Add enhanced search module with advanced filtering capabilities
- Expand server.py with new API endpoints and admin functionality
- Add Gonher catalog import scripts (import_gonher_catalog.py, import_gonher_complete.py)
- Add demo data population script and sample CSV data
- Update customer landing page and dashboard with UI improvements
- Update database with enriched vehicle and parts data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 00:35:05 +00:00

1600 lines
60 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel - Autopartes DB</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Orbitron:wght@700&display=swap" rel="stylesheet">
<style>
:root {
--bg-primary: #0a0a0f;
--bg-secondary: #12121a;
--bg-tertiary: #1a1a25;
--text-primary: #ffffff;
--text-secondary: #8888aa;
--accent: #ff6b35;
--accent-hover: #ff8555;
--success: #00d68f;
--warning: #ffaa00;
--danger: #ff4444;
--border: #2a2a3a;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
}
/* Header */
.header {
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
}
.logo {
font-family: 'Orbitron', sans-serif;
font-size: 1.5rem;
color: var(--accent);
text-decoration: none;
}
.header-nav {
display: flex;
gap: 1rem;
}
.header-nav a {
color: var(--text-secondary);
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 6px;
transition: all 0.2s;
}
.header-nav a:hover {
color: var(--text-primary);
background: var(--bg-tertiary);
}
/* Layout */
.container {
display: flex;
min-height: calc(100vh - 60px);
}
/* Sidebar */
.sidebar {
width: 250px;
background: var(--bg-secondary);
border-right: 1px solid var(--border);
padding: 1rem 0;
flex-shrink: 0;
}
.sidebar-section {
padding: 0.5rem 1rem;
margin-bottom: 0.5rem;
}
.sidebar-section h3 {
font-size: 0.75rem;
text-transform: uppercase;
color: var(--text-secondary);
margin-bottom: 0.5rem;
letter-spacing: 0.05em;
}
.sidebar-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
color: var(--text-secondary);
text-decoration: none;
cursor: pointer;
transition: all 0.2s;
border-left: 3px solid transparent;
}
.sidebar-item:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.sidebar-item.active {
background: var(--bg-tertiary);
color: var(--accent);
border-left-color: var(--accent);
}
.sidebar-item .icon {
width: 20px;
text-align: center;
}
/* Main Content */
.main-content {
flex: 1;
padding: 2rem;
overflow-x: auto;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.page-title {
font-size: 1.75rem;
font-weight: 600;
}
.page-actions {
display: flex;
gap: 1rem;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.btn-primary {
background: var(--accent);
color: white;
}
.btn-primary:hover {
background: var(--accent-hover);
}
.btn-secondary {
background: var(--bg-tertiary);
color: var(--text-primary);
border: 1px solid var(--border);
}
.btn-secondary:hover {
background: var(--border);
}
.btn-success {
background: var(--success);
color: white;
}
.btn-danger {
background: var(--danger);
color: white;
}
.btn-sm {
padding: 0.5rem 0.75rem;
font-size: 0.8rem;
}
/* Cards */
.card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border);
}
.card-title {
font-size: 1.1rem;
font-weight: 600;
}
/* Tables */
.table-wrapper {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
text-align: left;
padding: 1rem;
border-bottom: 1px solid var(--border);
}
th {
font-weight: 600;
color: var(--text-secondary);
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.05em;
background: var(--bg-tertiary);
}
tr:hover td {
background: rgba(255, 107, 53, 0.05);
}
.actions-cell {
display: flex;
gap: 0.5rem;
}
/* Forms */
.form-group {
margin-bottom: 1.25rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
font-size: 0.9rem;
color: var(--text-secondary);
}
.form-input {
width: 100%;
padding: 0.75rem 1rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text-primary);
font-size: 0.95rem;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: var(--accent);
}
.form-input::placeholder {
color: var(--text-secondary);
}
select.form-input {
cursor: pointer;
}
textarea.form-input {
min-height: 100px;
resize: vertical;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
/* Modal */
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 1000;
justify-content: center;
align-items: flex-start;
padding: 2rem;
overflow-y: auto;
}
.modal-overlay.active {
display: flex;
}
.modal {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 16px;
width: 100%;
max-width: 600px;
margin-top: 2rem;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid var(--border);
}
.modal-title {
font-size: 1.25rem;
font-weight: 600;
}
.modal-close {
background: none;
border: none;
color: var(--text-secondary);
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
line-height: 1;
}
.modal-close:hover {
color: var(--text-primary);
}
.modal-body {
padding: 1.5rem;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 1rem;
padding: 1.5rem;
border-top: 1px solid var(--border);
}
/* File Upload */
.file-upload {
border: 2px dashed var(--border);
border-radius: 12px;
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
}
.file-upload:hover {
border-color: var(--accent);
background: rgba(255, 107, 53, 0.05);
}
.file-upload.dragover {
border-color: var(--accent);
background: rgba(255, 107, 53, 0.1);
}
.file-upload input {
display: none;
}
.file-upload-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.file-upload-text {
color: var(--text-secondary);
}
.file-upload-text strong {
color: var(--accent);
}
/* Stats Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: var(--accent);
}
.stat-label {
color: var(--text-secondary);
font-size: 0.9rem;
margin-top: 0.25rem;
}
/* Badge */
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
}
.badge-economy { background: #444; color: #ccc; }
.badge-standard { background: #2a5a2a; color: #7fff7f; }
.badge-premium { background: #5a5a2a; color: #ffff7f; }
.badge-oem { background: #2a2a5a; color: #7f7fff; }
/* Alert */
.alert {
padding: 1rem 1.5rem;
border-radius: 8px;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.75rem;
}
.alert-success {
background: rgba(0, 214, 143, 0.1);
border: 1px solid var(--success);
color: var(--success);
}
.alert-error {
background: rgba(255, 68, 68, 0.1);
border: 1px solid var(--danger);
color: var(--danger);
}
/* Pagination */
.pagination {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 1.5rem;
}
.pagination button {
padding: 0.5rem 1rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-primary);
cursor: pointer;
}
.pagination button:hover:not(:disabled) {
background: var(--accent);
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination button.active {
background: var(--accent);
}
/* Search */
.search-box {
position: relative;
max-width: 300px;
}
.search-box input {
width: 100%;
padding: 0.75rem 1rem 0.75rem 2.5rem;
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text-primary);
}
.search-box::before {
content: "🔍";
position: absolute;
left: 0.75rem;
top: 50%;
transform: translateY(-50%);
font-size: 0.9rem;
}
/* Tab navigation */
.tab-nav {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.5rem;
}
.tab-btn {
padding: 0.75rem 1.5rem;
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
border-radius: 8px 8px 0 0;
transition: all 0.2s;
}
.tab-btn:hover {
color: var(--text-primary);
background: var(--bg-tertiary);
}
.tab-btn.active {
color: var(--accent);
background: var(--bg-tertiary);
border-bottom: 2px solid var(--accent);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* Hidden sections */
.admin-section {
display: none;
}
.admin-section.active {
display: block;
}
/* Loading */
.loading {
text-align: center;
padding: 2rem;
color: var(--text-secondary);
}
.spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 3px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* Image Upload */
.image-upload-container {
display: flex;
gap: 1rem;
align-items: flex-start;
}
.image-preview {
width: 120px;
height: 120px;
background: var(--bg-tertiary);
border: 2px dashed var(--border);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
}
.image-preview img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.image-placeholder {
color: var(--text-secondary);
font-size: 0.85rem;
text-align: center;
}
.image-upload-actions {
flex: 1;
}
.image-upload-actions input[type="file"] {
display: none;
}
.part-thumbnail {
width: 50px;
height: 50px;
object-fit: contain;
border-radius: 4px;
background: var(--bg-tertiary);
}
/* Bulk Parts Selector */
.bulk-parts-container {
max-height: 400px;
overflow-y: auto;
border: 1px solid var(--border);
border-radius: 8px;
padding: 1rem;
background: var(--bg-tertiary);
}
.bulk-part-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem;
border-bottom: 1px solid var(--border);
cursor: pointer;
transition: background 0.2s;
}
.bulk-part-item:last-child {
border-bottom: none;
}
.bulk-part-item:hover {
background: var(--bg-hover, rgba(255, 107, 53, 0.1));
}
.bulk-part-item.selected {
background: rgba(0, 214, 143, 0.15);
border-color: var(--success);
}
.bulk-part-item input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--accent);
}
.bulk-part-info {
flex: 1;
}
.bulk-part-number {
font-family: monospace;
color: var(--accent);
font-size: 0.85rem;
}
.bulk-part-name {
font-weight: 500;
}
.bulk-part-group {
font-size: 0.8rem;
color: var(--text-secondary);
}
.bulk-part-qty {
width: 60px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<!-- Header -->
<header class="header">
<a href="/" class="logo">AUTOPARTES DB</a>
<nav class="header-nav">
<a href="/">Catálogo</a>
<a href="/customer-landing.html">Landing</a>
<a href="/admin.html" style="color: var(--accent);">Admin</a>
</nav>
</header>
<div class="container">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-section">
<h3>Dashboard</h3>
<div class="sidebar-item active" data-section="dashboard">
<span class="icon">📊</span>
<span>Resumen</span>
</div>
</div>
<div class="sidebar-section">
<h3>Catálogo</h3>
<div class="sidebar-item" data-section="categories">
<span class="icon">📁</span>
<span>Categorías</span>
</div>
<div class="sidebar-item" data-section="groups">
<span class="icon">📂</span>
<span>Grupos</span>
</div>
<div class="sidebar-item" data-section="parts">
<span class="icon">🔧</span>
<span>Partes OEM</span>
</div>
</div>
<div class="sidebar-section">
<h3>Aftermarket</h3>
<div class="sidebar-item" data-section="manufacturers">
<span class="icon">🏭</span>
<span>Fabricantes</span>
</div>
<div class="sidebar-item" data-section="aftermarket">
<span class="icon">🔩</span>
<span>Partes Aftermarket</span>
</div>
<div class="sidebar-item" data-section="crossref">
<span class="icon">🔗</span>
<span>Cross-References</span>
</div>
</div>
<div class="sidebar-section">
<h3>Fitment</h3>
<div class="sidebar-item" data-section="fitment">
<span class="icon">🚗</span>
<span>Vehículo-Partes</span>
</div>
</div>
<div class="sidebar-section">
<h3>Importar/Exportar</h3>
<div class="sidebar-item" data-section="import">
<span class="icon">📥</span>
<span>Importar CSV</span>
</div>
<div class="sidebar-item" data-section="export">
<span class="icon">📤</span>
<span>Exportar CSV</span>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="main-content">
<!-- Alert container -->
<div id="alertContainer"></div>
<!-- Dashboard Section -->
<section id="section-dashboard" class="admin-section active">
<div class="page-header">
<h1 class="page-title">Panel de Administración</h1>
</div>
<div class="stats-grid" id="dashboardStats">
<div class="stat-card">
<div class="stat-value" id="statCategories">-</div>
<div class="stat-label">Categorías</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statGroups">-</div>
<div class="stat-label">Grupos</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statParts">-</div>
<div class="stat-label">Partes OEM</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statAftermarket">-</div>
<div class="stat-label">Partes Aftermarket</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statManufacturers">-</div>
<div class="stat-label">Fabricantes</div>
</div>
<div class="stat-card">
<div class="stat-value" id="statFitment">-</div>
<div class="stat-label">Fitments</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">Acciones Rápidas</h2>
</div>
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
<button class="btn btn-primary" onclick="showSection('import')">📥 Importar CSV</button>
<button class="btn btn-secondary" onclick="showSection('parts')">🔧 Agregar Parte</button>
<button class="btn btn-secondary" onclick="showSection('manufacturers')">🏭 Agregar Fabricante</button>
<button class="btn btn-secondary" onclick="showSection('export')">📤 Exportar Datos</button>
</div>
</div>
</section>
<!-- Categories Section -->
<section id="section-categories" class="admin-section">
<div class="page-header">
<h1 class="page-title">Categorías</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openCategoryModal()">+ Nueva Categoría</button>
</div>
</div>
<div class="card">
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Nombre (ES)</th>
<th>Slug</th>
<th>Icono</th>
<th>Orden</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="categoriesTable">
<tr><td colspan="7" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Groups Section -->
<section id="section-groups" class="admin-section">
<div class="page-header">
<h1 class="page-title">Grupos</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openGroupModal()">+ Nuevo Grupo</button>
</div>
</div>
<div class="card">
<div class="form-group" style="max-width: 300px; margin-bottom: 1rem;">
<label class="form-label">Filtrar por Categoría</label>
<select class="form-input" id="groupCategoryFilter" onchange="loadGroups()">
<option value="">Todas las categorías</option>
</select>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Nombre (ES)</th>
<th>Categoría</th>
<th>Orden</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="groupsTable">
<tr><td colspan="6" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Parts Section -->
<section id="section-parts" class="admin-section">
<div class="page-header">
<h1 class="page-title">Partes OEM</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openPartModal()">+ Nueva Parte</button>
</div>
</div>
<div class="card">
<div class="form-row" style="margin-bottom: 1rem;">
<div class="search-box">
<input type="text" id="partSearch" placeholder="Buscar por nombre o número..." onkeyup="debounceSearch(loadParts)">
</div>
<div class="form-group" style="margin-bottom: 0;">
<select class="form-input" id="partGroupFilter" onchange="loadParts()">
<option value="">Todos los grupos</option>
</select>
</div>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Img</th>
<th>Número OEM</th>
<th>Nombre</th>
<th>Grupo</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="partsTable">
<tr><td colspan="5" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
<div class="pagination" id="partsPagination"></div>
</div>
</section>
<!-- Manufacturers Section -->
<section id="section-manufacturers" class="admin-section">
<div class="page-header">
<h1 class="page-title">Fabricantes</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openManufacturerModal()">+ Nuevo Fabricante</button>
</div>
</div>
<div class="card">
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Tipo</th>
<th>Calidad</th>
<th>País</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="manufacturersTable">
<tr><td colspan="6" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Aftermarket Section -->
<section id="section-aftermarket" class="admin-section">
<div class="page-header">
<h1 class="page-title">Partes Aftermarket</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openAftermarketModal()">+ Nueva Parte</button>
</div>
</div>
<div class="card">
<div class="form-row" style="margin-bottom: 1rem;">
<div class="search-box">
<input type="text" id="aftermarketSearch" placeholder="Buscar..." onkeyup="debounceSearch(loadAftermarket)">
</div>
<div class="form-group" style="margin-bottom: 0;">
<select class="form-input" id="aftermarketManufacturerFilter" onchange="loadAftermarket()">
<option value="">Todos los fabricantes</option>
</select>
</div>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Número</th>
<th>Nombre</th>
<th>OEM Ref</th>
<th>Fabricante</th>
<th>Calidad</th>
<th>Precio</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="aftermarketTable">
<tr><td colspan="8" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
<div class="pagination" id="aftermarketPagination"></div>
</div>
</section>
<!-- Cross-References Section -->
<section id="section-crossref" class="admin-section">
<div class="page-header">
<h1 class="page-title">Cross-References</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openCrossRefModal()">+ Nueva Referencia</button>
</div>
</div>
<div class="card">
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Parte OEM</th>
<th>Número Referencia</th>
<th>Tipo</th>
<th>Fuente</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="crossrefTable">
<tr><td colspan="6" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
<div class="pagination" id="crossrefPagination"></div>
</div>
</section>
<!-- Fitment Section -->
<section id="section-fitment" class="admin-section">
<div class="page-header">
<h1 class="page-title">Fitment (Vehículo-Partes)</h1>
<div class="page-actions">
<button class="btn btn-primary" onclick="openFitmentModal()">+ Nuevo Fitment</button>
</div>
</div>
<div class="card">
<div class="form-row" style="margin-bottom: 1rem;">
<div class="form-group" style="margin-bottom: 0;">
<select class="form-input" id="fitmentBrandFilter" onchange="loadFitmentModels()">
<option value="">Selecciona marca...</option>
</select>
</div>
<div class="form-group" style="margin-bottom: 0;">
<select class="form-input" id="fitmentModelFilter" onchange="loadFitment()">
<option value="">Todos los modelos</option>
</select>
</div>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Vehículo</th>
<th>Parte</th>
<th>Cantidad</th>
<th>Posición</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="fitmentTable">
<tr><td colspan="6" class="loading"><div class="spinner"></div></td></tr>
</tbody>
</table>
</div>
<div class="pagination" id="fitmentPagination"></div>
</div>
<!-- Bulk Fitment Editor -->
<div class="card">
<div class="card-header">
<h2 class="card-title">⚡ Editor Masivo de Fitment</h2>
</div>
<p style="color: var(--text-secondary); margin-bottom: 1.5rem;">
Selecciona un vehículo y agrega múltiples partes de una vez.
</p>
<div class="form-row" style="margin-bottom: 1.5rem;">
<div class="form-group">
<label class="form-label">1. Selecciona Marca</label>
<select class="form-input" id="bulkBrand" onchange="loadBulkModels()">
<option value="">Selecciona marca...</option>
</select>
</div>
<div class="form-group">
<label class="form-label">2. Selecciona Modelo</label>
<select class="form-input" id="bulkModel" onchange="loadBulkYears()">
<option value="">Selecciona modelo...</option>
</select>
</div>
<div class="form-group">
<label class="form-label">3. Selecciona Año</label>
<select class="form-input" id="bulkYear" onchange="loadBulkEngines()">
<option value="">Selecciona año...</option>
</select>
</div>
<div class="form-group">
<label class="form-label">4. Selecciona Motor</label>
<select class="form-input" id="bulkEngine" onchange="selectBulkVehicle()">
<option value="">Selecciona motor...</option>
</select>
</div>
</div>
<div id="bulkVehicleSelected" style="display: none;">
<div class="alert alert-success" style="margin-bottom: 1rem;">
<strong>Vehículo seleccionado:</strong> <span id="bulkVehicleName"></span>
(MYE ID: <span id="bulkMYEId"></span>)
</div>
<div class="form-group">
<label class="form-label">5. Selecciona Categoría de Partes</label>
<select class="form-input" id="bulkCategory" onchange="loadBulkParts()" style="max-width: 300px;">
<option value="">Todas las categorías</option>
</select>
</div>
<div class="form-group">
<label class="form-label">6. Selecciona Partes a Vincular</label>
<div class="bulk-parts-container" id="bulkPartsContainer">
<p style="color: var(--text-secondary);">Cargando partes disponibles...</p>
</div>
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-success" onclick="saveBulkFitments()">
✓ Guardar Fitments Seleccionados
</button>
<span id="bulkSelectedCount" style="margin-left: 1rem; color: var(--text-secondary);">
0 partes seleccionadas
</span>
</div>
</div>
</div>
</section>
<!-- Import Section -->
<section id="section-import" class="admin-section">
<div class="page-header">
<h1 class="page-title">Importar Datos (CSV)</h1>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">Selecciona tipo de datos</h2>
</div>
<div class="form-group">
<label class="form-label">Tipo de importación</label>
<select class="form-input" id="importType" style="max-width: 300px;">
<option value="parts">Partes OEM</option>
<option value="aftermarket">Partes Aftermarket</option>
<option value="manufacturers">Fabricantes</option>
<option value="categories">Categorías</option>
<option value="groups">Grupos</option>
<option value="crossref">Cross-References</option>
<option value="fitment">Fitment (Vehículo-Partes)</option>
</select>
</div>
<div class="file-upload" id="dropZone">
<input type="file" id="csvFile" accept=".csv">
<div class="file-upload-icon">📄</div>
<div class="file-upload-text">
Arrastra un archivo CSV aquí o <strong>haz clic para seleccionar</strong>
</div>
</div>
<div id="importPreview" style="margin-top: 1.5rem; display: none;">
<h3 style="margin-bottom: 1rem;">Vista previa (<span id="previewCount">0</span> registros)</h3>
<div class="table-wrapper" style="max-height: 300px; overflow-y: auto;">
<table id="previewTable">
<thead id="previewHead"></thead>
<tbody id="previewBody"></tbody>
</table>
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-success" onclick="executeImport()">✓ Importar Datos</button>
<button class="btn btn-secondary" onclick="cancelImport()">✕ Cancelar</button>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2 class="card-title">Formato de CSV</h2>
</div>
<div id="csvFormatHelp">
<p style="color: var(--text-secondary); margin-bottom: 1rem;">Selecciona un tipo de importación para ver el formato requerido.</p>
</div>
</div>
</section>
<!-- Export Section -->
<section id="section-export" class="admin-section">
<div class="page-header">
<h1 class="page-title">Exportar Datos (CSV)</h1>
</div>
<div class="card">
<p style="color: var(--text-secondary); margin-bottom: 1.5rem;">
Descarga los datos en formato CSV para respaldo o importación en otros sistemas.
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<button class="btn btn-secondary" onclick="exportData('categories')">📁 Categorías</button>
<button class="btn btn-secondary" onclick="exportData('groups')">📂 Grupos</button>
<button class="btn btn-secondary" onclick="exportData('parts')">🔧 Partes OEM</button>
<button class="btn btn-secondary" onclick="exportData('manufacturers')">🏭 Fabricantes</button>
<button class="btn btn-secondary" onclick="exportData('aftermarket')">🔩 Aftermarket</button>
<button class="btn btn-secondary" onclick="exportData('crossref')">🔗 Cross-References</button>
<button class="btn btn-secondary" onclick="exportData('fitment')">🚗 Fitment</button>
</div>
</div>
</section>
</main>
</div>
<!-- Category Modal -->
<div class="modal-overlay" id="categoryModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="categoryModalTitle">Nueva Categoría</h2>
<button class="modal-close" onclick="closeModal('categoryModal')">&times;</button>
</div>
<div class="modal-body">
<form id="categoryForm">
<input type="hidden" id="categoryId">
<div class="form-group">
<label class="form-label">Nombre (EN)</label>
<input type="text" class="form-input" id="categoryName" required>
</div>
<div class="form-group">
<label class="form-label">Nombre (ES)</label>
<input type="text" class="form-input" id="categoryNameEs">
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Slug</label>
<input type="text" class="form-input" id="categorySlug" placeholder="auto-generado">
</div>
<div class="form-group">
<label class="form-label">Icono</label>
<input type="text" class="form-input" id="categoryIcon" placeholder="ej: engine">
</div>
</div>
<div class="form-group">
<label class="form-label">Orden de visualización</label>
<input type="number" class="form-input" id="categoryOrder" value="0">
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('categoryModal')">Cancelar</button>
<button class="btn btn-primary" onclick="saveCategory()">Guardar</button>
</div>
</div>
</div>
<!-- Group Modal -->
<div class="modal-overlay" id="groupModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="groupModalTitle">Nuevo Grupo</h2>
<button class="modal-close" onclick="closeModal('groupModal')">&times;</button>
</div>
<div class="modal-body">
<form id="groupForm">
<input type="hidden" id="groupId">
<div class="form-group">
<label class="form-label">Categoría</label>
<select class="form-input" id="groupCategory" required></select>
</div>
<div class="form-group">
<label class="form-label">Nombre (EN)</label>
<input type="text" class="form-input" id="groupName" required>
</div>
<div class="form-group">
<label class="form-label">Nombre (ES)</label>
<input type="text" class="form-input" id="groupNameEs">
</div>
<div class="form-group">
<label class="form-label">Orden de visualización</label>
<input type="number" class="form-input" id="groupOrder" value="0">
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('groupModal')">Cancelar</button>
<button class="btn btn-primary" onclick="saveGroup()">Guardar</button>
</div>
</div>
</div>
<!-- Part Modal -->
<div class="modal-overlay" id="partModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="partModalTitle">Nueva Parte OEM</h2>
<button class="modal-close" onclick="closeModal('partModal')">&times;</button>
</div>
<div class="modal-body">
<form id="partForm">
<input type="hidden" id="partId">
<div class="form-group">
<label class="form-label">Número de Parte OEM</label>
<input type="text" class="form-input" id="partOemNumber" required>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Nombre (EN)</label>
<input type="text" class="form-input" id="partName" required>
</div>
<div class="form-group">
<label class="form-label">Nombre (ES)</label>
<input type="text" class="form-input" id="partNameEs">
</div>
</div>
<div class="form-group">
<label class="form-label">Grupo</label>
<select class="form-input" id="partGroup" required></select>
</div>
<div class="form-group">
<label class="form-label">Descripción (EN)</label>
<textarea class="form-input" id="partDescription"></textarea>
</div>
<div class="form-group">
<label class="form-label">Descripción (ES)</label>
<textarea class="form-input" id="partDescriptionEs"></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Peso (kg)</label>
<input type="number" step="0.01" class="form-input" id="partWeight">
</div>
<div class="form-group">
<label class="form-label">Material</label>
<input type="text" class="form-input" id="partMaterial">
</div>
</div>
<div class="form-group">
<label class="form-label">Imagen de la parte</label>
<div class="image-upload-container">
<div class="image-preview" id="partImagePreview">
<span class="image-placeholder">📷 Sin imagen</span>
</div>
<div class="image-upload-actions">
<input type="file" id="partImageFile" accept="image/*" onchange="previewPartImage(this)">
<button type="button" class="btn btn-secondary btn-sm" onclick="document.getElementById('partImageFile').click()">
📤 Subir imagen
</button>
<input type="text" class="form-input" id="partImageUrl" placeholder="O pegar URL de imagen..." style="margin-top: 0.5rem;">
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('partModal')">Cancelar</button>
<button class="btn btn-primary" onclick="savePart()">Guardar</button>
</div>
</div>
</div>
<!-- Manufacturer Modal -->
<div class="modal-overlay" id="manufacturerModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="manufacturerModalTitle">Nuevo Fabricante</h2>
<button class="modal-close" onclick="closeModal('manufacturerModal')">&times;</button>
</div>
<div class="modal-body">
<form id="manufacturerForm">
<input type="hidden" id="manufacturerId">
<div class="form-group">
<label class="form-label">Nombre</label>
<input type="text" class="form-input" id="manufacturerName" required>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Tipo</label>
<select class="form-input" id="manufacturerType" required>
<option value="aftermarket">Aftermarket</option>
<option value="oem">OEM</option>
<option value="remanufactured">Remanufacturado</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Tier de Calidad</label>
<select class="form-input" id="manufacturerQuality">
<option value="economy">Economy</option>
<option value="standard">Standard</option>
<option value="premium">Premium</option>
<option value="oem">OEM</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">País</label>
<input type="text" class="form-input" id="manufacturerCountry">
</div>
<div class="form-group">
<label class="form-label">Website</label>
<input type="url" class="form-input" id="manufacturerWebsite">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('manufacturerModal')">Cancelar</button>
<button class="btn btn-primary" onclick="saveManufacturer()">Guardar</button>
</div>
</div>
</div>
<!-- Aftermarket Part Modal -->
<div class="modal-overlay" id="aftermarketModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="aftermarketModalTitle">Nueva Parte Aftermarket</h2>
<button class="modal-close" onclick="closeModal('aftermarketModal')">&times;</button>
</div>
<div class="modal-body">
<form id="aftermarketForm">
<input type="hidden" id="aftermarketId">
<div class="form-group">
<label class="form-label">Parte OEM de referencia</label>
<select class="form-input" id="aftermarketOemPart" required></select>
</div>
<div class="form-group">
<label class="form-label">Fabricante</label>
<select class="form-input" id="aftermarketManufacturer" required></select>
</div>
<div class="form-group">
<label class="form-label">Número de Parte</label>
<input type="text" class="form-input" id="aftermarketPartNumber" required>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Nombre (EN)</label>
<input type="text" class="form-input" id="aftermarketName">
</div>
<div class="form-group">
<label class="form-label">Nombre (ES)</label>
<input type="text" class="form-input" id="aftermarketNameEs">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Calidad</label>
<select class="form-input" id="aftermarketQuality">
<option value="economy">Economy</option>
<option value="standard">Standard</option>
<option value="premium">Premium</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Precio (USD)</label>
<input type="number" step="0.01" class="form-input" id="aftermarketPrice">
</div>
</div>
<div class="form-group">
<label class="form-label">Garantía (meses)</label>
<input type="number" class="form-input" id="aftermarketWarranty">
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('aftermarketModal')">Cancelar</button>
<button class="btn btn-primary" onclick="saveAftermarket()">Guardar</button>
</div>
</div>
</div>
<!-- Cross-Reference Modal -->
<div class="modal-overlay" id="crossrefModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="crossrefModalTitle">Nueva Cross-Reference</h2>
<button class="modal-close" onclick="closeModal('crossrefModal')">&times;</button>
</div>
<div class="modal-body">
<form id="crossrefForm">
<input type="hidden" id="crossrefId">
<div class="form-group">
<label class="form-label">Parte OEM</label>
<select class="form-input" id="crossrefPart" required></select>
</div>
<div class="form-group">
<label class="form-label">Número de Referencia</label>
<input type="text" class="form-input" id="crossrefNumber" required>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Tipo</label>
<select class="form-input" id="crossrefType" required>
<option value="oem_alternate">OEM Alternativo</option>
<option value="supersession">Supersesión</option>
<option value="interchange">Intercambio</option>
<option value="competitor">Competidor</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Fuente</label>
<input type="text" class="form-input" id="crossrefSource">
</div>
</div>
<div class="form-group">
<label class="form-label">Notas</label>
<textarea class="form-input" id="crossrefNotes"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('crossrefModal')">Cancelar</button>
<button class="btn btn-primary" onclick="saveCrossRef()">Guardar</button>
</div>
</div>
</div>
<!-- Fitment Modal -->
<div class="modal-overlay" id="fitmentModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="fitmentModalTitle">Nuevo Fitment</h2>
<button class="modal-close" onclick="closeModal('fitmentModal')">&times;</button>
</div>
<div class="modal-body">
<form id="fitmentForm">
<input type="hidden" id="fitmentId">
<div class="form-group">
<label class="form-label">Vehículo (Marca/Modelo/Año)</label>
<select class="form-input" id="fitmentVehicle" required></select>
</div>
<div class="form-group">
<label class="form-label">Parte OEM</label>
<select class="form-input" id="fitmentPart" required></select>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Cantidad requerida</label>
<input type="number" class="form-input" id="fitmentQuantity" value="1" min="1">
</div>
<div class="form-group">
<label class="form-label">Posición</label>
<select class="form-input" id="fitmentPosition">
<option value="">N/A</option>
<option value="front">Frontal</option>
<option value="rear">Trasero</option>
<option value="left">Izquierdo</option>
<option value="right">Derecho</option>
<option value="front-left">Frontal Izq.</option>
<option value="front-right">Frontal Der.</option>
<option value="rear-left">Trasero Izq.</option>
<option value="rear-right">Trasero Der.</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">Notas de fitment</label>
<textarea class="form-input" id="fitmentNotes"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('fitmentModal')">Cancelar</button>
<button class="btn btn-primary" onclick="saveFitment()">Guardar</button>
</div>
</div>
</div>
<script src="admin.js"></script>
</body>
</html>