/* eslint-disable */ /* components.jsx — EZ-C redesigned primitives, sidebar, page header. */ const T = window.React; const { useState } = T; /* ============================================================ Icon — inline lucide-style SVG paths at 1.5 stroke Only the icons we actually use across the redesign. ============================================================ */ const ICON_PATHS = { // navigation layoutGrid: 'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z', target: 'M12 2v2 M22 12h-2 M12 22v-2 M2 12h2', targetCircle: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z', checkSquare: 'M9 11l3 3 8-8 M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11', listChecks: 'M3 6h2 M9 6h12 M3 12h2 M9 12h12 M3 18h2 M9 18h12', users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2 M22 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75 M13 7a4 4 0 1 1-8 0 4 4 0 0 1 8 0z', 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', fileText: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8', folder: 'M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z', clock: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M12 6v6l4 2', 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', plane: 'M17.8 19.2L16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z', database: 'M12 8a9 3 0 1 0 0-6 9 3 0 0 0 0 6z M21 12c0 1.66-4 3-9 3s-9-1.34-9-3 M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5', messageSquare: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', trendingUp: 'M22 7l-8.5 8.5-5-5L2 17 M16 7h6v6', trendingDown: 'M22 17l-8.5-8.5-5 5L2 7 M16 17h6v-6', pieChart: 'M21.21 15.89A10 10 0 1 1 8 2.83 M22 12A10 10 0 0 0 12 2v10z', briefcase: 'M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16 M21 21V8a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v13z', // ui search: 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z M21 21l-4.35-4.35', plus: 'M12 5v14 M5 12h14', bell: 'M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9 M10.3 21a1.94 1.94 0 0 0 3.4 0', chevronDown: 'M6 9l6 6 6-6', chevronRight: 'M9 18l6-6-6-6', chevronLeft: 'M15 18l-6-6 6-6', chevronsUpDown: 'M7 15l5 5 5-5 M7 9l5-5 5 5', filter: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z', moreHorizontal: 'M12 12h.01 M19 12h.01 M5 12h.01', star: 'M12 2l3.09 6.26 6.91 1-5 4.87 1.18 6.88L12 17.77 5.82 21l1.18-6.88-5-4.87 6.91-1z', sparkles: 'M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z M5 3v4 M3 5h4 M19 17v4 M17 19h4', alertCircle: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M12 8v4 M12 16h.01', arrowDownLeft: 'M17 7L7 17 M17 17H7V7', arrowUpRight: 'M7 17L17 7 M7 7h10v10', arrowRight: 'M5 12h14 M12 5l7 7-7 7', arrowLeft: 'M19 12H5 M12 19l-7-7 7-7', check: 'M20 6L9 17l-5-5', x: 'M18 6L6 18 M6 6l12 12', flag: 'M4 22V4a2 2 0 0 1 2-2h12l-2 5 2 5H4', paperclip: 'M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48', command: 'M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3z', logout: 'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4 M16 17l5-5-5-5 M21 12H9', grip: 'M9 5h.01 M9 12h.01 M9 19h.01 M15 5h.01 M15 12h.01 M15 19h.01', 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.12 2.12 0 0 1 3 3L12 15l-4 1 1-4z', eye: 'M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z', 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 M10 11v6 M14 11v6', bot: 'M12 8V4H8 M2 14h2 M20 14h2 M15 13v2 M9 13v2 M6 8h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2z', pieChartSmall: 'M21.21 15.89A10 10 0 1 1 8 2.83v9.17h10.83z', mail: 'M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z M22 6l-10 7L2 6', menu: 'M3 6h18 M3 12h18 M3 18h18', undo: 'M9 14L4 9l5-5 M4 9h11a4 4 0 1 1 0 8h-3', redo: 'M15 14l5-5-5-5 M20 9H9a4 4 0 1 0 0 8h3', copy: 'M9 9h10a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2z M5 15a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2', save: 'M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z M17 21v-8H7v8 M7 3v4h8', }; function Icon({ name, size = 16, stroke = 1.5, color = 'currentColor', style }) { const d = ICON_PATHS[name]; if (!d) return null; // some paths use multi-subpath M ... separated by spaces — treat as one path const subpaths = d.split(' M').map((p, i) => (i === 0 ? p : 'M' + p)); return ( {subpaths.map((p, i) => )} ); } /* ============================================================ Button ============================================================ */ function Button({ variant = 'primary', size = 'md', leftIcon, rightIcon, children, style, ...rest }) { const base = { display: 'inline-flex', alignItems: 'center', gap: 6, padding: size === 'sm' ? '6px 10px' : '8px 14px', fontSize: size === 'sm' ? 12 : 13, fontWeight: 500, borderRadius: 'var(--radius-card)', border: '1px solid transparent', transition: 'background .15s', whiteSpace: 'nowrap', lineHeight: 1.2, }; const variants = { primary: { background: 'var(--color-accent-500)', color: '#fff', }, secondary: { background: 'var(--color-surface)', color: 'var(--color-ink-700)', borderColor: 'var(--color-line-subtle)', }, ghost: { background: 'transparent', color: 'var(--color-ink-700)', }, danger: { background: 'var(--color-danger-500)', color: '#fff', }, ai: { background: 'var(--color-ai-50)', color: 'var(--color-ai-700)', border: '1px solid rgba(168,114,54,0.18)', }, }; return ( ); } /* ============================================================ Badge / Chip / StatusDot ============================================================ */ function Badge({ tone = 'muted', children, icon, style }) { const tones = { accent: ['#EEF1F6', 'var(--color-accent-700)'], success: ['var(--color-success-50)', 'var(--color-success-700)'], warning: ['var(--color-warning-50)', 'var(--color-warning-700)'], danger: ['var(--color-danger-50)', 'var(--color-danger-700)'], info: ['var(--color-info-50)', 'var(--color-info-700)'], muted: ['var(--color-muted-50)', 'var(--color-ink-700)'], ai: ['var(--color-ai-50)', 'var(--color-ai-700)'], }; const [bg, fg] = tones[tone]; return ( {icon ? : null} {children} ); } function StatusDot({ tone, size = 8 }) { const tones = { planned: 'var(--color-info-500)', paid: 'var(--color-success-500)', overdue: 'var(--color-danger-500)', draft: 'var(--color-ink-400)', active: 'var(--color-accent-500)', success: 'var(--color-success-500)', warning: 'var(--color-warning-500)', danger: 'var(--color-danger-500)', info: 'var(--color-info-500)', muted: 'var(--color-ink-400)', }; return ( ); } /* Filter chip group used in headers */ function ChipGroup({ options, value, onChange }) { return (
{options.map(opt => { const active = opt.value === value; return ( ); })}
); } /* ============================================================ Card ============================================================ */ function Card({ children, padding = 16, style, hoverable }) { return (
{children}
); } /* ============================================================ Sidebar ============================================================ */ const NAV_GROUPS = [ { label: null, items: [ { id: 'dashboard', icon: 'pieChartSmall', label: 'Дашборд' }, ], }, { label: 'РАБОТА', items: [ { id: 'projects', icon: 'briefcase', label: 'Проекты', count: 17 }, { id: 'tenders', icon: 'targetCircle', label: 'Тендеры', count: 14 }, { id: 'leads', icon: 'trendingUp', label: 'Лиды', count: 38, badge: 'AI' }, { id: 'mytasks', icon: 'checkSquare', label: 'Мои задачи', count: 2 }, { id: 'teamtasks', icon: 'listChecks', label: 'Задачи команды' }, ], }, { label: 'ФИНАНСЫ', items: [ { id: 'payments', icon: 'wallet', label: 'Оплаты' }, { id: 'docs', icon: 'fileText', label: 'АВР / СФ' }, ], }, { label: 'СПРАВОЧНИКИ', items: [ { id: 'clients', icon: 'users', label: 'Клиенты' }, { id: 'suppliers', icon: 'users', label: 'Подрядчики' }, { id: 'prices', icon: 'database', label: 'База цен' }, { id: 'files', icon: 'folder', label: 'Файлы' }, ], }, { label: 'ВРЕМЯ', items: [ { id: 'timetrack', icon: 'clock', label: 'Тайм-трекер' }, { id: 'timesheet', icon: 'calendar', label: 'Табель' }, { id: 'vacations', icon: 'plane', label: 'Отпуска' }, ], }, { label: null, items: [ { id: 'feedback', icon: 'messageSquare', label: 'Обратная связь' }, ], }, ]; function Sidebar({ active = 'projects', dense = false }) { return ( ); } /* ============================================================ App page header with breadcrumbs ============================================================ */ function PageHeader({ crumbs = [], title, subtitle, actions, viewSwitch }) { return (
{crumbs.length > 0 && (
{crumbs.map((c, i) => ( {c} {i < crumbs.length - 1 && ( )} ))}
)}

{title}

{subtitle && (
{subtitle}
)}
{viewSwitch} {actions}
); } /* View switcher segmented control */ function ViewSwitch({ options, value, onChange }) { return (
{options.map(opt => { const active = opt.value === value; return ( ); })}
); } /* ============================================================ Search input / Select pill ============================================================ */ function FilterBar({ children, style }) { return (
{children}
); } function SearchInput({ placeholder = 'Поиск…', value, onChange, width = 240 }) { return (
onChange && onChange(e.target.value)} style={{ width: '100%', padding: '7px 10px 7px 30px', fontSize: 13, background: 'var(--color-surface)', border: '1px solid var(--color-line-subtle)', borderRadius: 'var(--radius-input)', color: 'var(--color-ink-700)', outline: 'none', }} />
); } function SelectPill({ label, value, width }) { return ( ); } /* ============================================================ StatTile — used in dashboards and detail headers ============================================================ */ function StatTile({ label, value, sub, tone = 'neutral', icon, delta, deltaTone, style }) { const toneBg = { neutral: 'var(--color-surface)', accent: 'var(--color-accent-50)', success: 'var(--color-success-50)', warning: 'var(--color-warning-50)', danger: 'var(--color-danger-50)', info: 'var(--color-info-50)', }[tone]; const toneIcon = { neutral: 'var(--color-ink-500)', accent: 'var(--color-accent-500)', success: 'var(--color-success-500)', warning: 'var(--color-warning-500)', danger: 'var(--color-danger-500)', info: 'var(--color-info-500)', }[tone]; return (
{icon && } {label} {delta && ( {delta} )}
{value}
{sub && (
{sub}
)}
); } /* ============================================================ Avatar + AvatarStack ============================================================ */ const AVATAR_COLORS = ['#E8E1D2','#DDE5DD','#E0DCEA','#EADBD6','#DAE4EC','#EAE2D1']; function Avatar({ name, size = 22 }) { const initials = (name || '?').split(' ').map(s => s[0]).slice(0,2).join(''); const colorIdx = (name || '').length % AVATAR_COLORS.length; return (
{initials}
); } function AvatarStack({ names, size = 22, max = 4 }) { const shown = names.slice(0, max); const overflow = names.length - shown.length; return (
{shown.map((n, i) => (
))} {overflow > 0 && (
+{overflow}
)}
); } /* ============================================================ AppShell — wraps Sidebar + content with PageHeader ============================================================ */ function AppShell({ active, crumbs, title, subtitle, actions, viewSwitch, children }) { return (
{children}
); } /* Export all */ Object.assign(window, { Icon, Button, Badge, StatusDot, ChipGroup, Card, Sidebar, PageHeader, ViewSwitch, FilterBar, SearchInput, SelectPill, StatTile, Avatar, AvatarStack, AppShell, });