import { useEffect, useState } from "react";
import { NavLink, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
import { api } from "./api";
import { useAuth } from "./auth";
import Icon from "./components/Icon";
import ErrorBoundary from "./components/ErrorBoundary";
import BrandMark from "./components/BrandMark";
import Login from "./pages/Login";
import OidcCallback from "./pages/OidcCallback";
import SsoNeu from "./pages/SsoNeu";
import Dashboard from "./pages/Dashboard";
import Products from "./pages/Products";
import ItemList from "./pages/ItemList";
import Charges from "./pages/Charges";
import MinStock from "./pages/MinStock";
import ProductForm from "./pages/ProductForm";
import CheckIn from "./pages/CheckIn";
import CheckOut from "./pages/CheckOut";
import Groups from "./pages/Groups";
import Categories from "./pages/Categories";
import Shops from "./pages/Shops";
import ItemResolve from "./pages/ItemResolve";
import LocationResolve from "./pages/LocationResolve";
import Locations from "./pages/Locations";
import PackageTypes from "./pages/PackageTypes";
import Masse from "./pages/Masse";
import Units from "./pages/Units";
import Users from "./pages/Users";
import ShoppingList from "./pages/ShoppingList";
import Rezepte from "./pages/Rezepte";
import History from "./pages/History";
import Verliehen from "./pages/Verliehen";
import Transfer from "./pages/Transfer";
import Settings from "./pages/Settings";
import Konto from "./pages/Konto";
const navClass = ({ isActive }) => (isActive ? "nav-link active" : "nav-link");
// Die vier Bestandslisten stehen unter einer zuklappbaren Gruppe. Sie sind der
// laengste Block der Leiste und wird im Alltag selten gebraucht – wer einlagert,
// nutzt die Knoepfe darueber.
const LISTEN = [
{ to: "/lebensmittel", icon: "food", label: "Lebensmittel" },
{ to: "/gegenstaende", icon: "package", label: "Gegenstände" },
{ to: "/items", icon: "tag", label: "Einzelstücke" },
{ to: "/chargen", icon: "box", label: "Chargen" },
];
const LISTEN_OFFEN_KEY = "nav:listen-offen";
function NavItem({ to, icon, label, end }) {
return (
{label}
);
}
function Sidebar() {
const { user, isAdmin, logout } = useAuth();
const navigate = useNavigate();
const { pathname } = useLocation();
const [dashboards, setDashboards] = useState([]);
// Der Rezept-Eintrag erscheint nur, wenn Mealie eingerichtet ist. Ein Fehler
// hier darf die Leiste nicht stoeren, deshalb still abfangen.
const [mealie, setMealie] = useState(false);
// Zugeklappt als Vorgabe; der Zustand bleibt im Browser, wie die
// Spalteneinstellungen der Tabellen.
const [listenOffen, setListenOffen] = useState(
() => localStorage.getItem(LISTEN_OFFEN_KEY) === "1",
);
// Steht man auf einer dieser Seiten, ist die Gruppe offen – ohne den
// gemerkten Zustand zu ueberschreiben. Sonst saehe man nicht, wo man ist.
const aufEinerListe = LISTEN.some((e) => pathname.startsWith(e.to));
function klappeListen() {
setListenOffen((alt) => {
const neu = !alt;
localStorage.setItem(LISTEN_OFFEN_KEY, neu ? "1" : "0");
return neu;
});
}
// Die Dashboards des Benutzers stehen als eigene Eintraege in der Leiste.
// Sie aendern sich beim Anlegen und Loeschen, deshalb horcht die Leiste auf
// das Ereignis, das die Dashboard-Seite dabei ausloest.
useEffect(() => {
function laden() {
api.dashboards().then((a) => setDashboards(a.dashboards)).catch(() => {});
}
laden();
window.addEventListener("dashboards-geaendert", laden);
return () => window.removeEventListener("dashboards-geaendert", laden);
}, []);
useEffect(() => {
api.mealieVerfuegbar().then((a) => setMealie(!!a.available)).catch(() => {});
}, []);
function handleLogout() {
logout();
navigate("/login");
}
return (
);
}
/** Server nicht erreichbar – die Anmeldung bleibt bestehen, nur der Abruf nicht. */
function StartFehler({ text, erneut }) {
return (
{text}
Deine Anmeldung ist noch gültig – sobald der Server wieder antwortet,
geht es weiter.
;
if (startFehler) return ;
if (!user) return ;
if (adminOnly && !isAdmin) return ;
return (
{/* `wide` hebt die Lesebreite auf – nur fürs Dashboard-Raster sinnvoll. */}
{/* Je Seite eine eigene Grenze: stuerzt eine ab, bleibt wenigstens
die Navigation bedienbar. `key` setzt sie beim Seitenwechsel
zurueck, sonst haenge man nach einem Fehler dauerhaft fest.
Nur das ERSTE Pfadsegment: Sonst gilt /home/edit als andere Seite
als /home, der ganze Baum wird neu eingehaengt und laedt beim
Umschalten in den Bearbeitungsmodus alles noch einmal. */}
{children}
);
}
export default function App() {
const { user, loading } = useAuth();
return (
: }
/>
{/* Die Startseite hat einen Namen, damit in der Adresszeile steht, wo
man ist; "/" leitet sichtbar dorthin um. Der Bearbeitungsmodus ist
ein eigener Pfad statt eines Zustands im Speicher: So ueberlebt er
ein Neuladen, laesst sich verschicken, und die Zurueck-Taste
verlaesst ihn. Den Modus gibt die Prop vor, damit der Pfad nicht ein
zweites Mal in der Komponente steht. */}
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />
{/* Ohne adminOnly: Verleihen und Zurücknehmen ist eine Buchung wie Ein-
und Auslagern, keine Stammdatenänderung. */}
} />
} />
{/* Bewusst ohne adminOnly: die SSO-Verknuepfung braucht jeder selbst. */}
} />
} />
} />
} />
} />
} />
} />
} />
} />
{/* Rueckweg vom Single-Sign-on: MUSS oeffentlich sein (der Nutzer ist in
diesem Moment noch nicht angemeldet) und MUSS vor dem Auffangmuster
unten stehen – sonst schluckt "*" die Weiterleitung samt Token. */}
} />
{/* Ebenfalls oeffentlich: hier ist man noch nicht angemeldet. */}
} />
} />
);
}