// Shell.jsx — Sidebar + Topbar layout (function () { const { useState, useRef } = React; // ── Inline SVG icons ────────────────────────────────────────────── const Ic = ({ d, size = 20, stroke = 'currentColor', fill = 'none', sw = 1.7 }) => ( {Array.isArray(d) ? d.map((p, i) => ) : } ); const icons = { dashboard: , kanban: , calendar: , users: , tasks: , reports: , settings: , bell: , search: , sun: , moon: , chevLeft: , chevRight: , menu: , }; // ── Nav items ────────────────────────────────────────────────────── const NAV = [ { id: 'dashboard', label: 'Dashboard', icon: 'dashboard' }, { id: 'kanban', label: 'Lejek sprzedaży', icon: 'kanban' }, { id: 'clients', label: 'Klienci', icon: 'users' }, { id: 'tasks', label: 'Zadania', icon: 'tasks' }, { id: 'calendar', label: 'Kalendarz', icon: 'calendar' }, { id: 'reports', label: 'Raporty', icon: 'reports' }, { id: 'settings', label: 'Ustawienia', icon: 'settings' }, ]; // ── Sidebar ──────────────────────────────────────────────────────── function Sidebar({ active, setActive, collapsed, setCollapsed }) { const [showLogout, setShowLogout] = useState(false); return ( ); } // ── Notifications ────────────────────────────────────────────────── const NOTIFICATIONS = [ { id:1, text:'Nowy lead: Altima Digital Sp. z o.o. (Meta Ads)', time:'12 min temu', color:'#7C5CFF', unread:true }, { id:2, text:'Spotkanie z InnoPark za 24h — przypomnienie wysłane', time:'1 godz. temu', color:'#4DA3FF', unread:true }, { id:3, text:'Nexus Technology — oferta zaakceptowana', time:'3 godz. temu', color:'#22C55E', unread:true }, { id:4, text:'Zaległe zadanie: umowa Techlab Solutions', time:'wczoraj', color:'#EF4444', unread:false }, ]; function NotificationsBell() { const [open, setOpen] = useState(false); const [rect, setRect] = useState(null); const btnRef = useRef(null); const popRef = useRef(null); React.useEffect(() => { const onDoc = (e) => { if (btnRef.current && btnRef.current.contains(e.target)) return; if (popRef.current && popRef.current.contains(e.target)) return; setOpen(false); }; document.addEventListener('mousedown', onDoc); return () => document.removeEventListener('mousedown', onDoc); }, []); const unreadCount = NOTIFICATIONS.filter(n => n.unread).length; const toggle = () => { if (!open && btnRef.current) setRect(btnRef.current.getBoundingClientRect()); setOpen(v => !v); }; return (
{unreadCount > 0 && (
)} {open && rect && ReactDOM.createPortal(
Powiadomienia
{NOTIFICATIONS.map(n => (
{n.text}
{n.time}
))}
, document.body )}
); } // ── Topbar ───────────────────────────────────────────────────────── function Topbar({ darkMode, setDarkMode, collapsed, setCollapsed, activeView }) { const viewLabels = { dashboard:'Dashboard', kanban:'Lejek sprzedaży', clients:'Klienci', tasks:'Zadania', reports:'Raporty', settings:'Ustawienia' }; return (
{/* Collapse toggle */} {/* Breadcrumb */}
{viewLabels[activeView] || ''}
{/* Bell */} {/* Theme toggle */}
); } // ── Custom Select (portal-based, avoids native