Cargando catálogo

EDITOR

Acceso restringido. Ingresá la clave para administrar el catálogo.

← volver a la tienda
Herramientas profesionales

SRVIntegral

el aliado del profesional

Stock propio
Envíos a todo el país

Productos

🔍
Marca
Tipo
Stock
Orden
Tu carrito
🛒
Tu carrito está vacío
Finalizar compra
Datos personales
Envío
CABA / GBA
$6.000
Interior del país
$12.000
Retiro en persona
Sin costo
Forma de pago
🏦
Transferencia
5% OFF · Alias MP
💳
Mercado Pago
Tarjeta · Cuotas
Alias MP srvintegral.mp
✓ 5% de descuento aplicado
Resumen del pedido
Completá todos los campos obligatorios.
SRVINT Editor Sincronizado
0
En tienda
$0
Valor
Sincronización automática activa · Última: cargando...

Mis publicaciones en ML

Solo publicaciones propias (tradicionales). Tocá para seleccionar y agregar a la tienda. Estrella verde ★ = ya en tu tienda.

Estado
Stock
En tienda
Orden

Cargando productos

Conectando con Mercado Libre

Markup global
%
🔍
Orden
Mostrar
🛒

Tu tienda está vacía

Andá a "Mis productos en ML" y agregá productos

Ventas
Pedidos recibidos. Cambiá el estado para llevar seguimiento.
📦

Sin ventas aún

Los pedidos aparecerán acá cuando los clientes completen una compra

SRV Integral · ${order.id}

Destinatario
${escapeHtml(order.customer.name)}
DNI ${escapeHtml(order.customer.dni)} · Tel ${escapeHtml(order.customer.phone)}
${escapeHtml(order.customer.email)}
Dirección de envío
${escapeHtml(order.shipping.address)}
${escapeHtml(order.shipping.city)}, ${escapeHtml(order.shipping.province)}
Envío: ${order.shipping.zone === 'local' ? 'CABA/GBA' : 'Interior'} — ${formatPrice(order.shipping.cost)}
Pago
${order.payment === 'mp' ? 'Mercado Pago' : 'Transferencia'}
Productos
${order.items.map(i => `
${escapeHtml(i.title.substring(0,45))} ×${i.qty}${formatPrice(i.price*i.qty)}
`).join('')}
Total: ${formatPrice(order.total)}
${new Date(order.date).toLocaleDateString('es-AR')}

`); w.document.close(); } function checkLogin() { const input = document.getElementById('login-input').value; if (input === CONFIG.editorPassword) { isEditorAuthenticated = true; sessionStorage.setItem('srv_editor_auth', 'true'); document.getElementById('login-error').textContent = ''; document.getElementById('login-input').value = ''; window.location.hash = 'editor'; showEditor(); loadMLProducts(); loadSyncStatus(); } else { document.getElementById('login-error').textContent = 'Clave incorrecta'; document.getElementById('login-input').value = ''; } } function logout() { isEditorAuthenticated = false; sessionStorage.removeItem('srv_editor_auth'); showPublic(); } document.addEventListener('DOMContentLoaded', () => { const li = document.getElementById('login-input'); if (li) li.addEventListener('keypress', e => { if (e.key === 'Enter') checkLogin(); }); const ms = document.getElementById('ml-search'); if (ms) ms.addEventListener('input', () => { currentPage = 1; renderMLGrid(); }); // Buscador en mi tienda const ss = document.getElementById('shop-search'); if (ss) { ss.addEventListener('input', e => { shopSearchTerm = e.target.value; const clearBtn = document.getElementById('shop-search-clear'); clearBtn.classList.toggle('visible', e.target.value.length > 0); renderShopEditor(); updateShopSearchStats(); }); } document.querySelectorAll('.filter-chip').forEach(chip => { chip.addEventListener('click', () => setFilter(chip.dataset.filter, chip.dataset.value)); }); // Buscador público const ps = document.getElementById('public-search'); if (ps) { ps.addEventListener('input', e => { publicFilters.search = e.target.value.toLowerCase(); const clearBtn = document.getElementById('public-search-clear'); clearBtn.classList.toggle('visible', e.target.value.length > 0); renderShopPublic(); }); } // Refrescar estado de sync cada minuto setInterval(() => { if (document.getElementById('editor-view').classList.contains('active')) { loadSyncStatus(); } }, 60000); }); function clearPublicSearch() { const input = document.getElementById('public-search'); if (input) { input.value = ''; publicFilters.search = ''; document.getElementById('public-search-clear').classList.remove('visible'); renderShopPublic(); input.focus(); } } function setPublicFilter(type, value) { if (publicFilters[type] === value) publicFilters[type] = null; else publicFilters[type] = value; renderShopPublic(); } function clearAllPublicFilters() { publicFilters.brand = null; publicFilters.type = null; publicFilters.search = ''; publicStockFilter = 'all'; ['all','in','out'].forEach(v => { const btn = document.getElementById(`public-stock-${v}`); if (btn) btn.classList.toggle('active', v === 'all'); }); const input = document.getElementById('public-search'); if (input) input.value = ''; document.getElementById('public-search-clear').classList.remove('visible'); renderShopPublic(); } function switchTab(tab) { currentTab = tab; document.querySelectorAll('.tab-btn').forEach(b => b.classList.toggle('active', b.dataset.tab === tab)); document.querySelectorAll('.tab-content').forEach(c => c.classList.toggle('active', c.id === 'tab-' + tab)); if (tab === 'orders') loadOrders(); } function toast(msg, type = 'info') { const t = document.getElementById('toast'); t.textContent = msg; t.className = 'toast show ' + type; setTimeout(() => t.classList.remove('show'), 3500); } function formatPrice(n) { if (!n && n !== 0) return '$0'; return '$' + Math.round(n).toLocaleString('es-AR'); } function calcFinalPrice(p) { const markup = p.markup ?? 0; return p.originalPrice * (1 + markup / 100); } function copyToClipboard(text, el) { navigator.clipboard.writeText(text).then(() => { const original = el.textContent; el.textContent = '✓ Copiado'; setTimeout(() => { el.textContent = original; }, 1500); }).catch(() => toast('No se pudo copiar', 'error')); } function applyGlobalMarkup() { const val = parseFloat(document.getElementById('global-markup').value); if (isNaN(val)) { toast('Ingresá un porcentaje válido', 'error'); return; } products.forEach(p => p.markup = val); save(); toast(`Markup ${val > 0 ? '+' : ''}${val}% aplicado`, 'success'); } function resetMarkups() { products.forEach(p => p.markup = 0); save(); toast('Markups reseteados', 'info'); } function updateProductMarkup(id, value) { const p = products.find(x => x.id === id); if (!p) return; p.markup = parseFloat(value) || 0; save(); } function removeProduct(id) { products = products.filter(p => p.id !== id); shopSelection.delete(id); save(); renderMLGrid(); updateBulkBar(); toast('Producto eliminado', 'info'); } function isVisibleInShop(p) { return true; } function render() { document.getElementById('stat-count').textContent = products.length; const total = products.reduce((sum, p) => sum + calcFinalPrice(p), 0); document.getElementById('stat-value').textContent = formatPrice(total); renderShopEditor(); renderShopPublic(); renderPublicFilters(); } function renderShopEditor() { const container = document.getElementById('shop-products-container'); const toolbar = document.getElementById('my-shop-toolbar'); const searchWrapper = document.querySelector('.shop-search-wrapper'); if (products.length === 0) { if (toolbar) toolbar.style.display = 'none'; if (searchWrapper) searchWrapper.style.display = 'none'; container.innerHTML = `
🛒

Tu tienda está vacía

Andá a "Mis productos en ML" y agregá productos

`; return; } if (toolbar) toolbar.style.display = 'flex'; if (searchWrapper) searchWrapper.style.display = 'block'; updateShopSearchStats(); const filtered = getFilteredShopProducts(); if (filtered.length === 0) { container.innerHTML = `
🔍

No hay coincidencias

Probá con otra búsqueda

`; return; } const allBrands = getAllBrands(); const allTypes = getAllTypes(); // Detectar duplicados por título const titleCount = {}; products.forEach(p => { const t = (p.title || '').trim().toLowerCase(); titleCount[t] = (titleCount[t] || 0) + 1; }); let datalistsHtml = ` ${allBrands.map(b => ` ${allTypes.map(t => ` `; const html = datalistsHtml + '
' + filtered.map(p => { const finalPrice = calcFinalPrice(p); const markup = p.markup ?? 0; const showOriginal = markup !== 0; const isSelected = shopSelection.has(p.id); const badge = markup !== 0 ? `${markup > 0 ? '+' : ''}${markup}%` : ''; let stockBadge = ''; if (p.stock === 0) stockBadge = `SIN STOCK · oculto`; else if (p.stock !== undefined && p.stock !== null) { const stockClass = p.stock <= 3 ? 'low' : ''; stockBadge = `${p.stock} en stock`; } else stockBadge = `no sincronizado`; let tagsHtml = ''; if (p.brand && p.brand.trim()) tagsHtml += `${escapeHtml(p.brand)}`; else tagsHtml += `sin marca`; if (p.type && p.type.trim()) tagsHtml += `${escapeHtml(p.type)}`; else tagsHtml += `sin tipo`; const isDuplicate = titleCount[(p.title || '').trim().toLowerCase()] > 1; if (isDuplicate) tagsHtml += `⚠ Duplicado`; return `
${isSelected ? '✓' : ''}
${escapeAttr(p.title)}
${tagsHtml}

${escapeHtml(p.title)}

${showOriginal ? `${formatPrice(p.originalPrice)}` : ''} ${formatPrice(finalPrice)} ${badge}
${stockBadge}
${p.itemId} ver en ML ↗
`; }).join('') + '
'; container.innerHTML = html; } function renderPublicFilters() { const visible = products; const brandsSet = new Set(); const typesSet = new Set(); visible.forEach(p => { if (p.brand && p.brand.trim()) brandsSet.add(p.brand.trim()); if (p.type && p.type.trim()) typesSet.add(p.type.trim()); }); const brands = Array.from(brandsSet).sort((a, b) => a.localeCompare(b, 'es')); const types = Array.from(typesSet).sort((a, b) => a.localeCompare(b, 'es')); const brandsContainer = document.getElementById('public-brands'); const filtersWrapper = document.getElementById('public-filters'); if (!brandsContainer) return; // No tocar display - lo maneja togglePublicFilters con la clase .visible if (brands.length > 0) { brandsContainer.innerHTML = brands.map(b => ` `).join(''); } else { brandsContainer.innerHTML = `— sin marcas asignadas —`; } const typesSelect = document.getElementById('public-types-select'); if (typesSelect) { const currentType = publicFilters.type || ''; typesSelect.innerHTML = '' + types.map(t => ``).join(''); } const hasFilters = publicFilters.brand || publicFilters.type || publicFilters.search; const clearBtn = document.getElementById('clear-filters-btn'); if (clearBtn) clearBtn.style.display = hasFilters ? 'inline-flex' : 'none'; } function getFilteredPublicProducts() { let visible = products.slice(); if (publicFilters.search) { const s = publicFilters.search.toLowerCase(); visible = visible.filter(p => { const title = (p.title || '').toLowerCase(); const brand = (p.brand || '').toLowerCase(); const type = (p.type || '').toLowerCase(); return title.includes(s) || brand.includes(s) || type.includes(s); }); } if (publicFilters.brand) visible = visible.filter(p => (p.brand || '').trim() === publicFilters.brand); if (publicFilters.type) visible = visible.filter(p => (p.type || '').trim() === publicFilters.type); if (publicStockFilter === 'in') visible = visible.filter(p => p.stock !== 0 && p.mlStatus !== 'paused'); if (publicStockFilter === 'out') visible = visible.filter(p => p.stock === 0 || p.mlStatus === 'paused'); if (publicSort === 'az') visible.sort((a, b) => (a.title || '').localeCompare(b.title || '', 'es')); if (publicSort === 'za') visible.sort((a, b) => (b.title || '').localeCompare(a.title || '', 'es')); return visible; } function renderShopPublic() { const grid = document.getElementById('shop-grid'); const totalAll = products.length; const inStockCount = products.filter(p => p.stock !== 0 && p.stock !== null && p.stock !== undefined && Number(p.stock) > 0 && p.mlStatus !== 'paused' && p.mlStatus !== 'closed').length; const visible = getFilteredPublicProducts(); const counter = document.getElementById('public-count'); if (counter) { const outOfStock = visible.filter(p => p.stock === 0).length; const inStock = visible.length - outOfStock; if (visible.length === totalAll) { let txt = inStock === 1 ? '1 producto' : `${inStock} productos`; if (outOfStock > 0) txt += ` · ${outOfStock} agotado${outOfStock > 1 ? 's' : ''}`; counter.textContent = txt; } else { let txt = `${visible.length} de ${totalAll}`; if (outOfStock > 0) txt += ` (${outOfStock} agotado${outOfStock > 1 ? 's' : ''})`; counter.textContent = txt; } } renderPublicFilters(); if (totalAll === 0) { grid.innerHTML = `
🔧

Catálogo en preparación

Pronto vas a ver nuestros productos acá

`; return; } if (visible.length === 0) { grid.innerHTML = `
🔍

No encontramos productos

Probá con otros filtros o palabras de búsqueda

`; return; } grid.innerHTML = visible.map(p => { const finalPrice = calcFinalPrice(p); const outOfStock = p.stock === 0 || p.stock === null || p.stock === undefined || p.stock === '' || Number(p.stock) === 0; const isPaused = p.mlStatus === 'paused' || p.mlStatus === 'closed'; const isUnavailable = outOfStock || isPaused; const lowStock = !isUnavailable && p.stock !== undefined && p.stock !== null && p.stock <= 3 && p.stock > 0; const metaParts = []; if (p.brand && p.brand.trim()) metaParts.push(`${escapeHtml(p.brand)}`); if (p.type && p.type.trim()) metaParts.push(`${escapeHtml(p.type)}`); const metaHtml = metaParts.length > 0 ? `
${metaParts.join('')}
` : ''; const badgeText = isPaused ? 'Pausado' : 'Sin stock'; const ctaText = isPaused ? 'Publicación pausada' : 'Sin stock disponible'; const priceText = isPaused ? 'Pausado' : 'Sin stock'; return `
${isUnavailable ? `${badgeText}` : ''} ${lowStock ? 'Últimas unidades' : ''} ${escapeAttr(p.title)}
${metaHtml}

${escapeHtml(p.title)}

${isUnavailable ? priceText : formatPrice(finalPrice)}
${isUnavailable ? `
${ctaText}
` : `
+ Agregar al carrito
Ver detalle →
`}
`; }).join(''); } async function openModal(id) { const p = products.find(x => x.id === id); if (!p) return; currentOrderProduct = p; const finalPrice = calcFinalPrice(p); const formattedPrice = formatPrice(finalPrice); document.getElementById('modal-img').src = p.mainPicture; document.getElementById('modal-title').textContent = p.title; document.getElementById('modal-price').textContent = formattedPrice; document.getElementById('modal-amount-copy').textContent = formattedPrice; const tagsRow = document.getElementById('modal-tags-row'); let tagsHtml = ''; if (p.brand && p.brand.trim()) tagsHtml += `${escapeHtml(p.brand)}`; if (p.type && p.type.trim()) tagsHtml += `${escapeHtml(p.type)}`; tagsRow.innerHTML = tagsHtml; tagsRow.style.display = tagsHtml ? 'flex' : 'none'; const waMessage = encodeURIComponent( `Hola SRV Integral! 👋\n\nMe interesa este producto:\n\n*${p.title}*\nPrecio: ${formattedPrice}\nRef: ${p.itemId}\n\n¿Tenés stock disponible?` ); document.getElementById('modal-wa-btn').href = `https://wa.me/${CONFIG.whatsapp}?text=${waMessage}`; document.getElementById('modal').classList.add('active'); // Descripción: mostrar inmediatamente si ya está cacheada const descEl = document.getElementById('modal-desc'); if (p.description) { descEl.textContent = p.description; descEl.classList.remove('loading'); } else { descEl.textContent = 'Cargando descripción...'; descEl.classList.add('loading'); try { const res = await fetch(`${CONFIG.workerUrl}/ml/description/${p.itemId}`); if (res.ok) { const data = await res.json(); const text = data.plain_text || data.text || ''; p.description = text || 'Sin descripción disponible.'; } else { p.description = 'Sin descripción disponible.'; } } catch (e) { p.description = 'Sin descripción disponible.'; } // Solo actualizar si el modal sigue abierto con el mismo producto if (document.getElementById('modal').classList.contains('active')) { descEl.textContent = p.description; descEl.classList.remove('loading'); } } } function closeModal() { document.getElementById('modal').classList.remove('active'); } function escapeHtml(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[c])); } function escapeAttr(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[c])); } init();