// MendiArt — Página principal (mobile)
// Marca: azul #1A2FA0, naranja #F05A0A
const C = {
azul: '#1A2FA0',
azulDark: '#0E1F7F',
naranja: '#F05A0A',
naranjaDark: '#D04A00',
verde: '#25D366',
bg: '#F7F7F7',
text: '#1A1A1A',
muted: '#6B7280',
line: '#E7E8EE',
chipGray: '#F1F2F6',
};
// ─── Iconos (line, 1.6 stroke) ───────────────────────────────
const Icon = {
Search: ({s=22, c='#111'}) => (
),
Cart: ({s=22, c='#111'}) => (
),
Home: ({s=22, c='#111'}) => (
),
Grid: ({s=22, c='#111'}) => (
),
Check: ({s=22, c='#111'}) => (
),
Truck: ({s=24, c='#111'}) => (
),
Store: ({s=24, c='#111'}) => (
),
Compat: ({s=24, c='#111'}) => (
),
Wrench: ({s=24, c='#111'}) => (
),
Tag: ({s=24, c='#111'}) => (
),
Instagram: ({s=24, c='#fff'}) => (
),
Facebook: ({s=24, c='#fff'}) => (
),
YouTube: ({s=24, c='#fff'}) => (
),
Plus: ({s=18, c='#fff'}) => (
),
Minus: ({s=18, c='#fff'}) => (
),
WhatsApp: ({s=28, c='#fff'}) => (
),
};
// ─── Logo ─────────────────────────────────────────────────────
function Logo({ size = 'md', dark = false }) {
const h = size === 'lg' ? 60 : size === 'sm' ? 30 : 44;
return (
);
}
// ─── Cinta scrolleable (marquee continuo) ─────────────────────
const TICKER_DEFAULT = ['Envíos a todo Mendoza', 'Consultá por WhatsApp', 'Buscador de compatibilidad', 'Retiro en el día', 'Precios especiales para distribuidores', 'Servicio técnico de impresoras'];
function Ticker({ cintaMensajes }) {
let items = TICKER_DEFAULT;
if (cintaMensajes) {
try {
const parsed = typeof cintaMensajes === 'string' ? JSON.parse(cintaMensajes) : cintaMensajes;
if (Array.isArray(parsed) && parsed.length > 0) items = parsed;
} catch {}
}
const set = (key) => (
{items.map((t, i) => (
{t}
))}
);
return (
{set('a')}{set('b')}{set('c')}
);
}
// ─── Navbar ──────────────────────────────────────────────────
function Navbar({ cartCount, onSearchClick, onCartClick }) {
return (
);
}
const iconBtn = {
width: 40, height: 40, borderRadius: 12, border: 'none',
background: 'transparent', cursor: 'pointer',
display: 'flex', alignItems: 'center', justifyContent: 'center',
};
// ─── Hero ────────────────────────────────────────────────────
function Hero({ onCatalog, slogan, subtitulo }) {
const textoSlogan = slogan || 'Todo para imprimir mejor.';
const textoSub = subtitulo || 'Papel fotográfico, tintas y cartuchos para tu impresora.';
return (
Mendoza, Argentina
{textoSlogan}
{textoSub}
{/* logo flotante — esquina inferior derecha del hero */}
);
}
// ─── Beneficios ──────────────────────────────────────────────
function Benefits() {
const items = [
{ ic: Icon.Truck, t: 'Envío a domicilio' },
{ ic: Icon.Store, t: 'Retiro en local' },
{ ic: Icon.Compat, t: 'Compatibilidad' },
{ ic: Icon.Wrench, t: 'Serv. técnico' },
{ ic: Icon.Tag, t: 'Precio distribuidor' },
];
return (
{items.map(({ ic: I, t }) => (
))}
);
}
// ─── Buscador ────────────────────────────────────────────────
function SearchBar({ value, onChange }) {
return (
onChange(e.target.value)}
placeholder="Buscar producto o modelo…"
style={{
flex: 1, border: 'none', outline: 'none',
fontFamily: 'inherit', fontSize: 14.5,
color: C.text, background: 'transparent',
}}
/>
{value && (
)}
);
}
// ─── Categorías chips ────────────────────────────────────────
function Categories({ active, onPick, cats }) {
return (
{cats.map(c => {
const isActive = c === active;
return (
);
})}
);
}
// ─── Producto: placeholder con stripes ───────────────────────
function ProductImage({ kind, tone }) {
const palettes = {
cyan: ['#E6F4FA', '#1A2FA0', 'cyan ink'],
magenta: ['#FBEAF2', '#1A2FA0', 'magenta ink'],
yellow: ['#FFF5DC', '#1A2FA0', 'yellow ink'],
black: ['#E9EAF0', '#1A2FA0', 'black ink'],
paper: ['#FAF7F0', '#1A2FA0', 'photo paper'],
service: ['#EEF0FA', '#1A2FA0', 'service'],
multi: ['#F2EAFB', '#1A2FA0', 'multipack'],
};
const [bg, fg, label] = palettes[tone] || palettes.cyan;
return (
{kind === 'bottle' && (
)}
{kind === 'cartridge' && (
)}
{kind === 'paper' && (
)}
{kind === 'service' && (
)}
{label}
);
}
// ─── Imagen real o placeholder ───────────────────────────────
function ProductImg({ p, radius = 12 }) {
const [error, setError] = React.useState(false);
if (p.fotos && p.fotos.length > 0 && !error) {
return (

setError(true)}
/>
);
}
return ;
}
// ─── Card producto ───────────────────────────────────────────
function ProductCard({ p, qty, onAdd, onRemove, onConsult, onOpen }) {
const outOfStock = !p.stock;
return (
{!outOfStock ? (
${p.price.toLocaleString('es-AR')}
IVA inc. · Sin IVA: ${p.price_sin_iva.toLocaleString('es-AR')}
{qty > 0 ? (
{qty}
) : (
)}
) : (
)}
);
}
const qtyBtn = {
width: 28, height: 28, borderRadius: 999, border: 'none',
background: 'transparent', cursor: 'pointer',
display: 'flex', alignItems: 'center', justifyContent: 'center',
};
// ─── Bottom nav ──────────────────────────────────────────────
function BottomNav({ active, onPick, cartCount }) {
const items = [
{ id: 'home', t: 'Inicio', I: Icon.Home },
{ id: 'cat', t: 'Catálogo', I: Icon.Grid },
{ id: 'compat', t: 'Compatibilidad', I: Icon.Compat },
{ id: 'cart', t: 'Carrito', I: Icon.Cart, badge: cartCount },
];
return (
{items.map(({ id, t, I, badge }) => {
const isActive = id === active;
const c = isActive ? C.azul : C.muted;
return (
);
})}
);
}
// ─── Vista Carrito ───────────────────────────────────────────
function CartView({ cart, products, waNum, onRemove, onAdd, onClear }) {
const items = Object.entries(cart)
.map(([id, qty]) => ({ p: products.find(x => x.id === id), qty }))
.filter(({ p }) => p);
const total = items.reduce((s, { p, qty }) => s + p.price * qty, 0);
const pedirPorWA = () => {
if (items.length === 0) return;
const lineas = items.map(({ p, qty }) =>
`• ${p.name} x${qty} — $${(p.price * qty).toLocaleString('es-AR')}`
).join('\n');
const msg = encodeURIComponent(
`Hola! Quiero hacer el siguiente pedido:\n\n${lineas}\n\nTotal: $${total.toLocaleString('es-AR')}`
);
if (waNum) window.open(`https://wa.me/${waNum}?text=${msg}`, '_blank');
};
return (
Carrito
{items.length > 0 && (
)}
{items.length === 0 ? (
Tu carrito está vacío
) : (
{items.map(({ p, qty }) => (
{p.name}
${p.price.toLocaleString('es-AR')} c/u
{qty}
))}
)}
{items.length > 0 && (
Total
${total.toLocaleString('es-AR')}
)}
);
}
// ─── WhatsApp FAB ────────────────────────────────────────────
function WhatsAppFAB({ onClick }) {
return (
);
}
// ─── Toast ───────────────────────────────────────────────────
function Toast({ msg, visible }) {
return (
{msg}
);
}
// ─── API ─────────────────────────────────────────────────────
const API = 'https://sg.mendiart.com.ar';
const MEDIA = 'https://sg.mendiart.com.ar/media/productos';
function mapProduct(p) {
const cat = (p.categoria || '').toLowerCase();
const color = (p.color_tinta || '').toLowerCase();
const nombre = (p.nombre_producto || '').toLowerCase();
let kind = 'bottle';
if (cat.includes('papel')) kind = 'paper';
else if (cat.includes('cartucho')) kind = 'cartridge';
else if (cat.includes('serv')) kind = 'service';
let tone = 'cyan';
if (kind === 'paper') tone = 'paper';
else if (kind === 'service') tone = 'service';
else if (color.includes('magenta')) tone = 'magenta';
else if (color.includes('amarill') || color.includes('yellow')) tone = 'yellow';
else if (color.includes('negro') || color.includes('black')) tone = 'black';
else if (nombre.includes('pack') || nombre.includes('kit')) tone = 'multi';
const iva = p.porcentaje_iva || 21;
const price = Math.round(p.precio_venta || 0);
const price_sin_iva = Math.round(price / (1 + iva / 100));
return {
id: p.id_producto,
cat: p.categoria || 'General',
name: p.nombre_producto,
descripcion: p.descripcion_web || '',
compat: p.compatible_con || '',
marca: p.marca || '',
calidad: p.calidad || '',
cantidad_por_unidad: p.cantidad_por_unidad || '',
color_tinta: p.color_tinta || '',
price_sin_iva,
fotos: Array.isArray(p.fotos) ? p.fotos : [],
video_youtube_id: p.video_youtube_id || '',
price,
kind,
tone,
stock: (p.stock || 0) > 0,
};
}
// ─── App ─────────────────────────────────────────────────────
function App() {
const [active, setActive] = React.useState('');
const [tab, setTab] = React.useState('home');
const [search, setSearch] = React.useState('');
const [cart, setCart] = React.useState({});
const [toast, setToast] = React.useState({ msg: '', show: false });
const [view, setView] = React.useState({ kind: 'home', productId: null });
const [products, setProducts] = React.useState([]);
const [categories, setCategories] = React.useState([]);
const [config, setConfig] = React.useState({});
const [loading, setLoading] = React.useState(true);
const scrollRef = React.useRef(null);
const catalogRef = React.useRef(null);
React.useEffect(() => {
Promise.all([
fetch(`${API}/web/categorias`).then(r => r.json()),
fetch(`${API}/web/productos`).then(r => r.json()),
fetch(`${API}/web/config`).then(r => r.json()),
]).then(([cats, prods, cfg]) => {
setCategories(cats);
setProducts(prods.map(mapProduct));
setConfig(cfg);
if (cats.length > 0) setActive(cats[0]);
setLoading(false);
}).catch(() => setLoading(false));
}, []);
React.useEffect(() => {
if (config.meta_descripcion) {
const meta = document.querySelector('meta[name="description"]');
if (meta) meta.setAttribute('content', config.meta_descripcion);
}
}, [config.meta_descripcion]);
const cartCount = Object.values(cart).reduce((a, b) => a + b, 0);
const showToast = (msg) => {
setToast({ msg, show: true });
clearTimeout(window.__toastT);
window.__toastT = setTimeout(() => setToast(t => ({ ...t, show: false })), 1800);
};
const waNum = (config.whatsapp || '').replace(/\D/g, '');
const add = (id) => {
setCart(c => ({ ...c, [id]: (c[id] || 0) + 1 }));
const p = products.find(x => x.id === id);
if (p && !cart[id]) showToast(`Agregado: ${p.name.split('·')[0].trim()}`);
};
const remove = (id) => {
setCart(c => {
const n = (c[id] || 0) - 1;
const copy = { ...c };
if (n <= 0) delete copy[id]; else copy[id] = n;
return copy;
});
};
const consult = (p) => {
const msg = encodeURIComponent(`Hola, consulto por ${p.name}. ¿Tienen stock disponible?`);
if (waNum) window.open(`https://wa.me/${waNum}?text=${msg}`, '_blank');
else showToast(`Consulta sobre ${p.name.split('·')[0].trim()}…`);
};
const openProduct = (id) => setView({ kind: 'product', productId: id });
const goHome = () => setView({ kind: 'home', productId: null });
if (loading) {
return (
{}} onCartClick={() => {}} />
);
}
// Cart view
if (tab === 'cart' && view.kind !== 'product') {
return (
);
}
// Product view
if (view.kind === 'product') {
const p = products.find(x => x.id === view.productId);
if (!p) { goHome(); return null; }
return (
{ setTab('cart'); goHome(); }} />
{ setTab(t); goHome(); }} cartCount={cartCount} />
);
}
const filtered = products.filter(p => {
const matchesCat = !active || p.cat === active;
const matchesSearch = search === '' ||
p.name.toLowerCase().includes(search.toLowerCase()) ||
(p.compat || '').toLowerCase().includes(search.toLowerCase());
return matchesCat && matchesSearch;
});
const direccion = config.direccion || 'Sobremonte 448, Dorrego, Guaymallén, Mendoza';
const horarios = config.horarios || 'Lun a Vie 9 a 19hs · Sáb 9 a 13hs';
return (
{
if (catalogRef.current && scrollRef.current) {
const top = catalogRef.current.getBoundingClientRect().top
- scrollRef.current.getBoundingClientRect().top
+ scrollRef.current.scrollTop;
scrollRef.current.scrollTo({ top, behavior: 'smooth' });
}
}}
onCartClick={() => setTab('cart')} />
{
if (catalogRef.current && scrollRef.current) {
const top = catalogRef.current.getBoundingClientRect().top
- scrollRef.current.getBoundingClientRect().top
+ scrollRef.current.scrollTop;
scrollRef.current.scrollTo({ top, behavior: 'smooth' });
}
setTab('cat');
}} />
{active}
{filtered.length} productos
{filtered.map(p => (
))}
{filtered.length === 0 && (
No encontramos productos{active && <> en {active}>}
{search && <> para «{search}»>}.
)}
{direccion} · {horarios}
{(config.instagram || config.facebook || config.youtube || waNum) && (
{config.instagram && (
)}
{config.facebook && (
)}
{config.youtube && (
)}
{waNum && (
)}
)}
{
if (waNum) window.open(`https://wa.me/${waNum}`, '_blank');
else showToast('Abriendo WhatsApp…');
}} />
);
}
Object.assign(window, { App, C, Icon, ProductImage, ProductImg, MEDIA, qtyBtn, iconBtn });