// Shared design system — Promoup 360° const DS = { colors: { primary: '#EA6B00', primaryDark: '#C45508', primaryLight: '#FFF4E6', success: '#10B981', successLight: '#ECFDF5', warning: '#F59E0B', warningLight: '#FFFBEB', danger: '#EF4444', dangerLight: '#FEF2F2', sidebar: '#12085C', sidebarHover: '#1C1280', sidebarActive: '#EA6B00', bg: '#F8F5FF', card: '#FFFFFF', border: '#E2E8F0', text: '#0F172A', textSec: '#64748B', textMuted: '#94A3B8', purple: '#7B35C8', }, radius: { sm: '8px', md: '12px', lg: '16px', xl: '20px', full: '9999px' }, shadow: { sm: '0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)', md: '0 4px 12px rgba(0,0,0,0.06), 0 2px 6px rgba(0,0,0,0.04)', lg: '0 10px 30px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.04)', } }; // Icon library — simple SVG paths const iconPaths = { dashboard: 'M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z M9 22V12h6v10', calendar: 'M8 2v4 M16 2v4 M3 10h18 M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z', budget: 'M12 2v20 M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6', users: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z M23 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75', crm: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z', tasks: 'M9 11l3 3L22 4 M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11', portal: 'M3 9a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9z M3 9l9 6 9-6', reports: 'M18 20V10 M12 20V4 M6 20v-6', wallet: 'M21 12V7H5a2 2 0 0 1 0-4h14v4 M3 5v14a2 2 0 0 0 2 2h16v-5 M18 12a2 2 0 0 0 0 4h4v-4z', bell: 'M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9 M13.73 21a2 2 0 0 1-3.46 0', search: 'M21 21l-4.35-4.35 M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z', plus: 'M12 5v14 M5 12h14', x: 'M18 6L6 18 M6 6l12 12', check: 'M20 6L9 17l-5-5', arrow: 'M5 12h14 M12 5l7 7-7 7', arrowDown: 'M12 5v14 M19 12l-7 7-7-7', edit: 'M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7 M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z', trash: 'M3 6h18 M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6 M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2', mail: 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z M22 6l-10 7L2 6', phone: 'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.15 12.91a19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 3.06 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L7.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 21 16.92z', star: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z', link: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71', download: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M7 10l5 5 5-5 M12 15V3', eye: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 12a3 3 0 1 0 0-6 3 3 0 0 0 0 6z', filter: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z', grid: 'M3 3h7v7H3z M14 3h7v7h-7z M14 14h7v7h-7z M3 14h7v7H3z', list: 'M8 6h13 M8 12h13 M8 18h13 M3 6h.01 M3 12h.01 M3 18h.01', settings: 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z', logout: 'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4 M16 17l5-5-5-5 M21 12H9', message: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', trending: 'M23 6l-9.5 9.5-5-5L1 18 M17 6h6v6', clock: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M12 6v6l4 2', alert: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z M12 9v4 M12 17h.01', chevronRight: 'M9 18l6-6-6-6', chevronDown: 'M6 9l6 6 6-6', zap: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z', award: 'M12 15a7 7 0 1 0 0-14 7 7 0 0 0 0 14z M8.21 13.89L7 23l5-3 5 3-1.21-9.12', briefcase: 'M20 7H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2', tag: 'M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z M7 7h.01', photo: 'M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z M12 17a4 4 0 1 0 0-8 4 4 0 0 0 0 8z', map: 'M1 6v16l7-4 8 4 7-4V2l-7 4-8-4-7 4z M8 2v16 M16 6v16', cpu: 'M9 2H5a2 2 0 0 0-2 2v4 M15 2h4a2 2 0 0 0 2 2v4 M9 22H5a2 2 0 0 0-2-2v-4 M15 22h4a2 2 0 0 0 2-2v-4 M9 9h6v6H9z M2 9h2 M2 15h2 M20 9h2 M20 15h2 M9 2v2 M15 2v2 M9 20v2 M15 20v2', }; function Icon({ name, size = 18, color = 'currentColor', strokeWidth = 1.8 }) { const d = iconPaths[name]; if (!d) return null; return ( {d.split(' M').map((segment, i) => ( ))} ); } // Badge component function Badge({ children, variant = 'default', size = 'sm' }) { const variants = { default: { bg: '#F1F5F9', color: '#475569' }, primary: { bg: '#EFF6FF', color: '#2563EB' }, success: { bg: '#ECFDF5', color: '#059669' }, warning: { bg: '#FFFBEB', color: '#D97706' }, danger: { bg: '#FEF2F2', color: '#DC2626' }, purple: { bg: '#F5F3FF', color: '#7C3AED' }, }; const v = variants[variant] || variants.default; const padding = size === 'sm' ? '2px 8px' : '4px 12px'; const fontSize = size === 'sm' ? '11px' : '12px'; return ( {children} ); } // Card component function Card({ children, style = {}, onClick, hover = false }) { const [hovered, setHovered] = React.useState(false); return (
hover && setHovered(true)} onMouseLeave={() => hover && setHovered(false)} style={{ background: DS.colors.card, borderRadius: DS.radius.md, boxShadow: hovered ? DS.shadow.md : DS.shadow.sm, border: `1px solid ${DS.colors.border}`, transition: 'all 0.18s ease', cursor: onClick ? 'pointer' : 'default', transform: hovered ? 'translateY(-1px)' : 'none', ...style, }} >{children}
); } // Button component function Btn({ children, variant = 'primary', size = 'md', onClick, icon, style = {}, disabled = false }) { const [hovered, setHovered] = React.useState(false); const variants = { primary: { bg: DS.colors.primary, hover: DS.colors.primaryDark, color: '#fff', border: 'none' }, secondary: { bg: '#fff', hover: '#F8FAFC', color: DS.colors.text, border: `1px solid ${DS.colors.border}` }, ghost: { bg: 'transparent', hover: '#F1F5F9', color: DS.colors.textSec, border: 'none' }, danger: { bg: '#FEF2F2', hover: '#FEE2E2', color: DS.colors.danger, border: 'none' }, success: { bg: DS.colors.success, hover: '#059669', color: '#fff', border: 'none' }, }; const v = variants[variant] || variants.primary; const sizes = { sm: { padding: '6px 12px', fontSize: '12px' }, md: { padding: '8px 16px', fontSize: '13px' }, lg: { padding: '11px 22px', fontSize: '14px' } }; const s = sizes[size] || sizes.md; return ( ); } // Avatar function Avatar({ name = '', size = 36, src, color }) { const colors = ['#3B82F6','#8B5CF6','#10B981','#F59E0B','#EF4444','#06B6D4','#EC4899']; const c = color || colors[name.charCodeAt(0) % colors.length]; const initials = name.split(' ').map(w => w[0]).slice(0,2).join('').toUpperCase(); return (
{src ? {name}/ : initials}
); } // Progress bar function ProgressBar({ value, max = 100, color = DS.colors.primary, height = 6 }) { const pct = Math.min(100, (value / max) * 100); return (
); } // Stat card function StatCard({ label, value, delta, icon, color = DS.colors.primary, subtitle }) { const positive = delta >= 0; return (
{label}
{value}
{subtitle &&
{subtitle}
}
{delta !== undefined && (
{positive ? '+' : ''}{delta}% vs last month
)}
); } // Empty state function EmptyState({ icon = 'grid', title = 'Nothing here yet', desc = '', action }) { return (
{title}
{desc}
{action &&
{action}
}
); } // Modal function Modal({ open, onClose, title, children, width = 520, extra }) { if (!open) return null; return (
e.stopPropagation()}>
{title}
{extra}
{children}
); } // Input function Input({ label, value, onChange, placeholder, type = 'text', style = {} }) { return (
{label && } onChange(e.target.value)} placeholder={placeholder} style={{ border: `1px solid ${DS.colors.border}`, borderRadius: DS.radius.sm, padding: '9px 12px', fontSize: 13, color: DS.colors.text, outline: 'none', transition: 'border 0.15s', fontFamily: 'inherit', }} onFocus={e => e.target.style.borderColor = DS.colors.primary} onBlur={e => e.target.style.borderColor = DS.colors.border} />
); } // Select function Select({ label, value, onChange, options, style = {} }) { return (
{label && }
); } // Divider function Divider({ style = {} }) { return
; } // Section header function SectionHeader({ title, subtitle, action }) { return (

{title}

{subtitle &&

{subtitle}

}
{action}
); } // Mini chart (bar chart using divs) function MiniBarChart({ data, color = DS.colors.primary, height = 80 }) { const max = Math.max(...data.map(d => d.value)); return (
{data.map((d, i) => (
{d.label}
))}
); } // Donut chart (SVG) function DonutChart({ segments, size = 120 }) { const total = segments.reduce((s, seg) => s + seg.value, 0); let angle = -90; const r = 40, cx = 60, cy = 60; const paths = segments.map(seg => { const pct = seg.value / total; const sweep = pct * 360; const startAngle = angle; angle += sweep; const x1 = cx + r * Math.cos((startAngle * Math.PI) / 180); const y1 = cy + r * Math.sin((startAngle * Math.PI) / 180); const x2 = cx + r * Math.cos(((startAngle + sweep) * Math.PI) / 180); const y2 = cy + r * Math.sin(((startAngle + sweep) * Math.PI) / 180); const large = sweep > 180 ? 1 : 0; return { d: `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`, color: seg.color, label: seg.label, value: seg.value }; }); return ( {paths.map((p, i) => )} ); } // Toast notification function Toast({ message, type = 'success', onClose }) { React.useEffect(() => { const t = setTimeout(onClose, 3000); return () => clearTimeout(t); }, []); const colors = { success: DS.colors.success, danger: DS.colors.danger, warning: DS.colors.warning }; return (
{message}
); } // Loading spinner function Spinner({ size = 32 }) { return (
); } // Export all to window Object.assign(window, { DS, Icon, Badge, Card, Btn, Avatar, ProgressBar, StatCard, EmptyState, Modal, Input, Select, Divider, SectionHeader, MiniBarChart, DonutChart, Toast, Spinner, });