| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- "use client";
- import React from "react";
- import Link from "next/link";
- import { CalendarDays, RefreshCw } from "lucide-react";
- import { getYears } from "@/lib/frontend/apiClient";
- import { branchPath, yearPath } from "@/lib/frontend/routes";
- import { sortNumericStringsDesc } from "@/lib/frontend/explorer/sorters";
- import { mapExplorerError } from "@/lib/frontend/explorer/errorMapping";
- import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect";
- import { useExplorerQuery } from "@/lib/frontend/hooks/useExplorerQuery";
- import { useDebouncedVisibility } from "@/lib/frontend/hooks/useDebouncedVisibility";
- import ExplorerBreadcrumbs from "@/components/explorer/breadcrumbs/ExplorerBreadcrumbs";
- import ExplorerPageShell from "@/components/explorer/ExplorerPageShell";
- import ExplorerSectionCard from "@/components/explorer/ExplorerSectionCard";
- import ExplorerLoading from "@/components/explorer/states/ExplorerLoading";
- import ExplorerEmpty from "@/components/explorer/states/ExplorerEmpty";
- import ExplorerError from "@/components/explorer/states/ExplorerError";
- import ExplorerNotFound from "@/components/explorer/states/ExplorerNotFound";
- import ForbiddenView from "@/components/system/ForbiddenView";
- import { Button } from "@/components/ui/button";
- const LOADING_DELAY_MS = 300;
- export default function YearsExplorer({ branch }) {
- const loadFn = React.useCallback(() => getYears(branch), [branch]);
- const { status, data, error, retry } = useExplorerQuery(loadFn, [loadFn]);
- const mapped = React.useMemo(() => mapExplorerError(error), [error]);
- const showLoadingUi = useDebouncedVisibility(status === "loading", {
- delayMs: LOADING_DELAY_MS,
- minVisibleMs: 0,
- });
- React.useEffect(() => {
- if (mapped?.kind !== "unauthenticated") return;
- const next =
- typeof window !== "undefined"
- ? `${window.location.pathname}${window.location.search}`
- : branchPath(branch);
- window.location.replace(
- buildLoginUrl({ reason: LOGIN_REASONS.EXPIRED, next }),
- );
- }, [mapped?.kind, branch]);
- const breadcrumbsNode = <ExplorerBreadcrumbs branch={branch} />;
- const actions = (
- <Button variant="outline" size="sm" onClick={retry} title="Aktualisieren">
- <RefreshCw className="h-4 w-4" />
- Aktualisieren
- </Button>
- );
- if (showLoadingUi) {
- return (
- <ExplorerPageShell
- title="Jahre"
- description="Wählen Sie ein Jahr, um die Lieferscheine anzuzeigen."
- breadcrumbs={breadcrumbsNode}
- actions={actions}
- >
- <ExplorerSectionCard title="Verfügbare Jahre" description="Lade Daten…">
- <ExplorerLoading variant="grid" count={8} />
- </ExplorerSectionCard>
- </ExplorerPageShell>
- );
- }
- if (status === "loading") {
- return (
- <ExplorerPageShell
- title="Jahre"
- description="Wählen Sie ein Jahr, um die Lieferscheine anzuzeigen."
- breadcrumbs={breadcrumbsNode}
- actions={actions}
- >
- <div className="h-16" aria-hidden="true" />
- </ExplorerPageShell>
- );
- }
- if (status === "error" && mapped) {
- if (mapped.kind === "forbidden") {
- return <ForbiddenView attemptedBranch={branch} />;
- }
- if (mapped.kind === "notfound") {
- return (
- <ExplorerPageShell
- title="Jahre"
- description="Wählen Sie ein Jahr, um die Lieferscheine anzuzeigen."
- breadcrumbs={breadcrumbsNode}
- actions={actions}
- >
- <ExplorerNotFound branchRootHref={branchPath(branch)} />
- </ExplorerPageShell>
- );
- }
- if (mapped.kind === "unauthenticated") {
- return (
- <ExplorerPageShell
- title="Jahre"
- description="Sitzung abgelaufen — Weiterleitung zum Login…"
- breadcrumbs={breadcrumbsNode}
- >
- <div className="h-16" aria-hidden="true" />
- </ExplorerPageShell>
- );
- }
- return (
- <ExplorerPageShell
- title="Jahre"
- description="Wählen Sie ein Jahr, um die Lieferscheine anzuzeigen."
- breadcrumbs={breadcrumbsNode}
- actions={actions}
- >
- <ExplorerSectionCard title="Verfügbare Jahre" description="Fehler">
- <ExplorerError
- title={mapped.title}
- description={mapped.description}
- onRetry={retry}
- />
- </ExplorerSectionCard>
- </ExplorerPageShell>
- );
- }
- const years = Array.isArray(data?.years) ? data.years : [];
- const sorted = sortNumericStringsDesc(years);
- return (
- <ExplorerPageShell
- title="Jahre"
- description="Wählen Sie ein Jahr, um die Lieferscheine anzuzeigen."
- breadcrumbs={breadcrumbsNode}
- actions={actions}
- >
- <ExplorerSectionCard
- title="Verfügbare Jahre"
- description={`Niederlassung ${branch}`}
- headerRight={
- <span className="rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
- {sorted.length} Jahr{sorted.length === 1 ? "" : "e"}
- </span>
- }
- >
- {sorted.length === 0 ? (
- <ExplorerEmpty
- title="Keine Jahre gefunden"
- description="Für diese Niederlassung wurden keine Jahre gefunden."
- upHref={null}
- />
- ) : (
- <div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4">
- {sorted.map((y) => (
- <Button
- key={y}
- variant="outline"
- className="w-full justify-start"
- asChild
- >
- <Link href={yearPath(branch, y)}>
- <CalendarDays className="h-4 w-4" />
- {y}
- </Link>
- </Button>
- ))}
- </div>
- )}
- </ExplorerSectionCard>
- </ExplorerPageShell>
- );
- }
|