"use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useEffect, useState } from "react"; import { listProjects } from "../utils/api"; import type { ProjectDto } from "../types"; import { ThemeToggle } from "./ThemeToggle"; // Anchors on the project overview page (/projects/[id]). const PROJECT_SECTIONS = [ { id: "verlauf", label: "Verlauf", icon: "↺" }, { id: "verteilungen", label: "Verteilungen", icon: "⌁" }, { id: "projektgeraete", label: "Projektgeräte", icon: "⚙" }, ] as const; // Separate project sub-routes (/projects/[id]/). const PROJECT_ROUTES = [ { segment: "struktur", label: "Gebäudestruktur", icon: "▥" }, { segment: "grobbilanz", label: "Grobe Leistungsbilanz", icon: "Σ" }, ] as const; // Matches the project overview and all of its sub-routes (group 2 = rest). const PROJECT_PATH_PATTERN = /^\/projects\/([^/]+)(\/.*)?$/; interface SidebarProps { isCollapsed: boolean; onToggleCollapsed: () => void; } export function Sidebar({ isCollapsed, onToggleCollapsed }: SidebarProps) { const pathname = usePathname(); const projectMatch = pathname?.match(PROJECT_PATH_PATTERN); const activeProjectId = projectMatch ? projectMatch[1] : null; const projectSubPath = projectMatch?.[2] ?? ""; // Section anchors only exist (and are observed) on the overview itself. const overviewProjectId = activeProjectId && (projectSubPath === "" || projectSubPath === "/") ? activeProjectId : null; const [projects, setProjects] = useState([]); const [activeSection, setActiveSection] = useState(null); useEffect(() => { function refreshProjects() { listProjects() .then(setProjects) .catch(() => setProjects([])); } refreshProjects(); window.addEventListener("leistungsbilanz:projects-changed", refreshProjects); return () => window.removeEventListener("leistungsbilanz:projects-changed", refreshProjects); }, []); useEffect(() => { if (!overviewProjectId) { setActiveSection(null); return; } const elements = PROJECT_SECTIONS.map((section) => document.getElementById(section.id) ).filter((element): element is HTMLElement => element !== null); if (!elements.length) { return; } const observer = new IntersectionObserver( (entries) => { const visible = entries .filter((entry) => entry.isIntersecting) .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top); if (visible[0]) { setActiveSection(visible[0].target.id); } }, { rootMargin: "-20% 0px -70% 0px", threshold: 0 } ); elements.forEach((element) => observer.observe(element)); return () => observer.disconnect(); }, [overviewProjectId]); return ( ); }