setShowLogout(false)} style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.5)', zIndex:300 }} />
Wylogować się?
Zostaniesz wylogowany z DemoCRM.
>,
document.body
)}
{/* Collapse toggle */}
);
}
// ── 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 => (
))}
,
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 (
);
}
// ── Custom Select (portal-based, avoids native