import NotificationBell from './NotificationBell'; import ThemeToggle from './ui/ThemeToggle'; import AuthWidget from './AuthWidget'; import UserDisplay from './ui/UserDisplay'; import { useEffect, useRef, useState } from 'preact/hooks'; import { useSpring, animated } from '@react-spring/web'; import { hydrateAuthFromStorage } from '../stores/authStore'; const ITEMS = [ { id: 'dashboard', label: 'Dashboard', href: '/' }, { id: 'work-orders', label: 'Putni nalozi', href: '/putni-nalozi' }, { id: 'service-records', label: 'Servisni zapisi', href: '/servisni-zapisi' }, { id: 'vehicles', label: 'Vozila', href: '/vehicles' }, { id: 'clients', label: 'Klijenti', href: '/clients' }, ]; function normalizePath(p) { const value = String(p || '/'); if (value.length > 1 && value.endsWith('/')) return value.slice(0, -1); return value; } function getActiveIndex(path) { const idx = ITEMS.findIndex((item) => item.href === path); return idx >= 0 ? idx : 0; } export default function Navbar({ minimal = false }) { // Navbar ostaje montiran (transition:persist) — pratimo promjene pathname preko astro:page-load const [pathname, setPathname] = useState(() => typeof window !== 'undefined' ? normalizePath(window.location.pathname) : '/' ); const itemRefs = useRef([]); const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0 }); // Na prvom renderu preskočimo animaciju (pill se pojavljuje na ispravnoj poziciji bez slide-a od 0) const [skipAnimation, setSkipAnimation] = useState(true); const activeIndex = getActiveIndex(pathname); const measureIndicator = (currentIndex) => { const idx = currentIndex ?? activeIndex; const el = itemRefs.current[idx]; if (!el) return; const parent = el.closest('ul'); if (!parent) return; const parentRect = parent.getBoundingClientRect(); const elRect = el.getBoundingClientRect(); setIndicatorStyle({ left: elRect.left - parentRect.left, width: elRect.width, }); }; // Mount: izmjeri početnu poziciju bez animacije, pa uključi animaciju za buduće navigacije useEffect(() => { hydrateAuthFromStorage(); const timer = setTimeout(() => { measureIndicator(); setSkipAnimation(false); }, 30); // Sluša Astro view-transition navigaciju (Navbar ostaje montiran zahvaljujući transition:persist) const handlePageLoad = () => { setPathname(normalizePath(window.location.pathname)); }; document.addEventListener('astro:page-load', handlePageLoad); return () => { clearTimeout(timer); document.removeEventListener('astro:page-load', handlePageLoad); }; }, []); // Kad se promijeni activeIndex (nova stranica), animiraj pill na novu poziciju useEffect(() => { if (!skipAnimation) { measureIndicator(); } }, [activeIndex]); // react-spring: animirani klizač // immediate=true na prvom renderu → pill se ne animira od lijevog ruba, već skače na pravo mjesto const springStyle = useSpring({ left: indicatorStyle.left, width: indicatorStyle.width, immediate: skipAnimation, config: { tension: 340, friction: 28 }, }); const currentItem = ITEMS[activeIndex]; return ( ); }