// /app/(app)/layout.tsx 'use client'; import { useState, type ReactNode, useEffect } from 'react'; import { Dialog, DialogBackdrop, DialogPanel, Menu, MenuButton, MenuItem, MenuItems, TransitionChild, } from '@headlessui/react'; import { Bars3Icon, BellIcon, CalendarIcon, CameraIcon, ChartPieIcon, Cog6ToothIcon, DocumentDuplicateIcon, FolderIcon, ComputerDesktopIcon, UserIcon, HomeIcon, UsersIcon, XMarkIcon, } from '@heroicons/react/24/outline'; import { ChevronDownIcon, MagnifyingGlassIcon } from '@heroicons/react/20/solid'; import { usePathname, useRouter } from 'next/navigation'; import { useSession, signOut } from 'next-auth/react'; import { Skeleton } from '@/components/ui/Skeleton'; import ScanModal from '@/components/ScanModal'; import DeviceDetailModal from './devices/DeviceDetailModal'; import PersonAvatar from '@/components/ui/UserAvatar'; import UserMenu from '@/components/UserMenu'; import GlobalSearch from '@/components/GlobalSearch'; const navigation = [ { name: 'Dashboard', href: '/dashboard', icon: HomeIcon }, { name: 'Geräte', href: '/devices', icon: ComputerDesktopIcon }, { name: 'Personen', href: '/users', icon: UserIcon }, ]; const userNavigation = [ { name: 'Your profile', href: '#' }, { name: 'Abmelden', href: '#' }, ]; function classNames(...classes: Array) { return classes.filter(Boolean).join(' '); } /* ───────── Layout ───────── */ export default function AppLayout({ children }: { children: ReactNode }) { const [sidebarOpen, setSidebarOpen] = useState(false); const [scanOpen, setScanOpen] = useState(false); const [detailOpen, setDetailOpen] = useState(false); const [detailInventoryNumber, setDetailInventoryNumber] = useState(null); const pathname = usePathname(); const router = useRouter(); const { data: session, status } = useSession(); const rawName = status === 'authenticated' ? (session?.user?.name ?? session?.user?.email ?? '') : ''; const displayName = rawName; const avatarName = rawName; const avatarUrl = session?.user?.image ?? null; const handleScanResult = (code: string) => { const trimmed = code.trim(); if (!trimmed) return; // 1) Versuch: QR-Code ist eine URL try { const url = new URL(trimmed); const isSameOrigin = typeof window !== 'undefined' && url.origin === window.location.origin; if (isSameOrigin) { // Beispiel: /devices/123456 const parts = url.pathname.split('/').filter(Boolean); const idx = parts.indexOf('devices'); if (idx >= 0 && parts[idx + 1]) { const inv = decodeURIComponent(parts[idx + 1]); setDetailInventoryNumber(inv); setDetailOpen(true); return; } } // Andere Domain → im Browser extern öffnen window.open(trimmed, '_blank', 'noopener,noreferrer'); return; } catch { // Kein gültiger URL → weiter unten behandeln } // 2) Kein URL → pure Inventarnummer in der Webapp setDetailInventoryNumber(trimmed); setDetailOpen(true); }; // Automatisches Logout, wenn unser eigenes Ablaufdatum erreicht ist useEffect(() => { if (status !== 'authenticated') return; const s = session as any; const expiresAt: number | undefined = s?.customExpires; // Falls aus irgendeinem Grund noch nicht gesetzt → nichts tun if (!expiresAt) return; const msUntilExpire = expiresAt - Date.now(); if (msUntilExpire <= 0) { // Session abgelaufen → ausloggen signOut({ callbackUrl: '/login' }); return; } const timeoutId = window.setTimeout(() => { signOut({ callbackUrl: '/login' }); }, msUntilExpire); return () => window.clearTimeout(timeoutId); }, [session, status]); return (
{/* Mobile Sidebar */}
{/* Sidebar mobil */}
Inventar
{/* Sidebar Desktop */}
Inventar
{/* Topbar + Inhalt */}