feat: complete session — catalog, marketplace, WhatsApp, peer-to-peer, install scripts
Major features: - Pixel-Perfect glassmorphism design (landing + POS + public catalog) - OEM/Local catalog toggle with Nexpart taxonomy (14 groups, 108 subgroups, 558 part types) - Marketplace B2B Phase 1 (bodegas, POs, status machine, WA+email notifications) - Peer-to-peer inventory (multi-instance, LAN discovery) - WhatsApp: photo→Vision AI, voice→Whisper, conversational quotations - Smart unified search (VIN/plate/part_number/keyword auto-detect) - Shop Supplies tab (vehicle-independent parts) - Chatbot AI fallback chain (5 models) + response cache - CSV inventory import tool + setup_instance.sh installer - Tablet-responsive CSS + sidebar toggle - Filters, export CSV, employee edit, business data save - Quotation system (WA→POS) with auto-print on confirmation - Live stats on landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,8 @@
|
||||
]},
|
||||
{ label: _t('nav_management'), items: [
|
||||
{ name: _t('customers'), href: '/pos/customers', icon: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>' },
|
||||
{ name: 'Cotizaciones', href: '/pos/quotations', icon: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="15" x2="15" y2="15"/><line x1="12" y1="12" x2="12" y2="18"/>' },
|
||||
{ name: 'Marketplace', href: '/pos/marketplace', icon: '<circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>' },
|
||||
{ name: _t('invoicing'), href: '/pos/invoicing', icon: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/>' },
|
||||
{ name: _t('accounting'), href: '/pos/accounting', icon: '<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>' },
|
||||
{ name: _t('reports'), href: '/pos/reports', icon: '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>' },
|
||||
@@ -163,4 +165,61 @@
|
||||
var main = document.querySelector('main, .main-content, #mainContent, .main, .page-content');
|
||||
if (main) main.classList.add('pos-main-offset');
|
||||
|
||||
// ── Tablet/mobile: sidebar toggle + overlay ─────────────────────
|
||||
// Creates a hamburger button + overlay for screens < 1024px.
|
||||
// The CSS in pos-glass.css hides the sidebar by default on tablets
|
||||
// and shows it as a slide-in drawer when .open is added.
|
||||
|
||||
var sidebar = document.querySelector('.pos-sidebar, .sidebar, #sidebar');
|
||||
var overlay = document.getElementById('sidebar-overlay');
|
||||
|
||||
// Create overlay if it doesn't exist
|
||||
if (!overlay && sidebar) {
|
||||
overlay = document.createElement('div');
|
||||
overlay.id = 'sidebar-overlay';
|
||||
overlay.className = 'sidebar-overlay';
|
||||
overlay.addEventListener('click', function () { closeSidebar(); });
|
||||
sidebar.parentNode.insertBefore(overlay, sidebar);
|
||||
}
|
||||
|
||||
// Create hamburger button if it doesn't exist
|
||||
var hamburger = document.getElementById('hamburger-btn');
|
||||
if (!hamburger) {
|
||||
hamburger = document.createElement('button');
|
||||
hamburger.id = 'hamburger-btn';
|
||||
hamburger.className = 'hamburger-btn';
|
||||
hamburger.setAttribute('aria-label', 'Menú');
|
||||
hamburger.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>';
|
||||
hamburger.style.cssText = 'display:none;position:fixed;top:10px;left:10px;z-index:' +
|
||||
(parseInt(getComputedStyle(document.documentElement).getPropertyValue('--z-modal') || 1050) + 2) +
|
||||
';background:var(--glass-bg-strong);backdrop-filter:blur(12px);border:1px solid var(--glass-border);' +
|
||||
'border-radius:var(--radius-md);padding:8px;cursor:pointer;color:var(--color-text-primary);' +
|
||||
'box-shadow:0 2px 8px rgba(0,0,0,0.2);';
|
||||
hamburger.addEventListener('click', function () { toggleSidebar(); });
|
||||
document.body.appendChild(hamburger);
|
||||
}
|
||||
|
||||
function toggleSidebar() {
|
||||
if (!sidebar) return;
|
||||
var isOpen = sidebar.classList.contains('open');
|
||||
sidebar.classList.toggle('open', !isOpen);
|
||||
if (overlay) overlay.classList.toggle('open', !isOpen);
|
||||
document.body.style.overflow = isOpen ? '' : 'hidden';
|
||||
}
|
||||
|
||||
function closeSidebar() {
|
||||
if (sidebar) sidebar.classList.remove('open');
|
||||
if (overlay) overlay.classList.remove('open');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Auto-close sidebar on window resize to desktop
|
||||
window.addEventListener('resize', function () {
|
||||
if (window.innerWidth >= 1024) closeSidebar();
|
||||
});
|
||||
|
||||
// Expose globally so inline onclick handlers and page-specific JS can call them
|
||||
window.toggleSidebar = toggleSidebar;
|
||||
window.closeSidebar = closeSidebar;
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user