// Shared product tile — real photo if available, else monochrome gradient with initials function ProductTile({ tono = 'beige', catalogo, imagen, size = 56, radius = 8 }) { const T = window.T; const bg = window.TONE_GRADIENT[tono] || window.TONE_GRADIENT.beige; const initials = (catalogo || '').replace(/[^A-Za-z]/g, '').slice(0, 2).toUpperCase(); const [imgFailed, setImgFailed] = React.useState(false); const useImage = imagen && !imgFailed; return (
{useImage ? ( setImgFailed(true)} style={{ width: '100%', height: '100%', objectFit: 'contain', padding: Math.max(2, size * 0.06), }} /> ) : ( {initials} )}
); } function StatusPill({ estado, enviado, size = 'md' }) { const T = window.T; const map = { pendiente: { label: enviado ? 'En curso' : 'Borrador', bg: enviado ? T.warnSoft : T.paperDeep, fg: enviado ? 'oklch(0.45 0.12 70)' : T.inkSoft, dot: enviado ? T.warn : T.inkMute }, completado: { label: 'Completado', bg: T.okSoft, fg: 'oklch(0.38 0.1 155)', dot: T.ok }, cancelado: { label: 'Cancelado', bg: T.dangerSoft, fg: T.accentInk, dot: T.danger }, }; const s = map[estado] || map.pendiente; const px = size === 'sm' ? '3px 8px' : '5px 10px'; const fs = size === 'sm' ? 10 : 11; return ( {s.label} ); } // Section header for inside screens (small serif eyebrow + title) function SectionHead({ eyebrow, title, action }) { const T = window.T; return (
{eyebrow && (
{eyebrow}
)}
{title}
{action}
); } // Primary button function PrimaryBtn({ children, onClick, disabled, icon, variant = 'accent', style = {} }) { const T = window.T; const bg = variant === 'accent' ? T.ink : T.accent; return ( ); } function GhostBtn({ children, onClick, icon, style = {} }) { const T = window.T; return ( ); } Object.assign(window, { ProductTile, StatusPill, SectionHead, PrimaryBtn, GhostBtn });