|
@@ -14,6 +14,7 @@ import { sortFilesByNameAsc } from "@/lib/frontend/explorer/sorters";
|
|
|
import { mapExplorerError } from "@/lib/frontend/explorer/errorMapping";
|
|
import { mapExplorerError } from "@/lib/frontend/explorer/errorMapping";
|
|
|
import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect";
|
|
import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect";
|
|
|
import { useExplorerQuery } from "@/lib/frontend/hooks/useExplorerQuery";
|
|
import { useExplorerQuery } from "@/lib/frontend/hooks/useExplorerQuery";
|
|
|
|
|
+import { useDebouncedVisibility } from "@/lib/frontend/hooks/useDebouncedVisibility";
|
|
|
import { buildPdfUrl } from "@/lib/frontend/explorer/pdfUrl";
|
|
import { buildPdfUrl } from "@/lib/frontend/explorer/pdfUrl";
|
|
|
|
|
|
|
|
import ExplorerBreadcrumbs from "@/components/explorer/breadcrumbs/ExplorerBreadcrumbs";
|
|
import ExplorerBreadcrumbs from "@/components/explorer/breadcrumbs/ExplorerBreadcrumbs";
|
|
@@ -36,18 +37,12 @@ import {
|
|
|
TableCaption,
|
|
TableCaption,
|
|
|
} from "@/components/ui/table";
|
|
} from "@/components/ui/table";
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * FilesExplorer
|
|
|
|
|
- *
|
|
|
|
|
- * Explorer leaf level: lists files (PDFs) for a day.
|
|
|
|
|
- * Loads years/months/days for breadcrumb dropdowns (fail-open).
|
|
|
|
|
- *
|
|
|
|
|
- * @param {{ branch: string, year: string, month: string, day: string }} props
|
|
|
|
|
- */
|
|
|
|
|
|
|
+const LOADING_DELAY_MS = 300;
|
|
|
|
|
+
|
|
|
export default function FilesExplorer({ branch, year, month, day }) {
|
|
export default function FilesExplorer({ branch, year, month, day }) {
|
|
|
const filesLoadFn = React.useCallback(
|
|
const filesLoadFn = React.useCallback(
|
|
|
() => getFiles(branch, year, month, day),
|
|
() => getFiles(branch, year, month, day),
|
|
|
- [branch, year, month, day]
|
|
|
|
|
|
|
+ [branch, year, month, day],
|
|
|
);
|
|
);
|
|
|
const filesQuery = useExplorerQuery(filesLoadFn, [filesLoadFn]);
|
|
const filesQuery = useExplorerQuery(filesLoadFn, [filesLoadFn]);
|
|
|
|
|
|
|
@@ -56,19 +51,27 @@ export default function FilesExplorer({ branch, year, month, day }) {
|
|
|
|
|
|
|
|
const monthsLoadFn = React.useCallback(
|
|
const monthsLoadFn = React.useCallback(
|
|
|
() => getMonths(branch, year),
|
|
() => getMonths(branch, year),
|
|
|
- [branch, year]
|
|
|
|
|
|
|
+ [branch, year],
|
|
|
);
|
|
);
|
|
|
const monthsQuery = useExplorerQuery(monthsLoadFn, [monthsLoadFn]);
|
|
const monthsQuery = useExplorerQuery(monthsLoadFn, [monthsLoadFn]);
|
|
|
|
|
|
|
|
const daysLoadFn = React.useCallback(
|
|
const daysLoadFn = React.useCallback(
|
|
|
() => getDays(branch, year, month),
|
|
() => getDays(branch, year, month),
|
|
|
- [branch, year, month]
|
|
|
|
|
|
|
+ [branch, year, month],
|
|
|
);
|
|
);
|
|
|
const daysQuery = useExplorerQuery(daysLoadFn, [daysLoadFn]);
|
|
const daysQuery = useExplorerQuery(daysLoadFn, [daysLoadFn]);
|
|
|
|
|
|
|
|
const mapped = React.useMemo(
|
|
const mapped = React.useMemo(
|
|
|
() => mapExplorerError(filesQuery.error),
|
|
() => mapExplorerError(filesQuery.error),
|
|
|
- [filesQuery.error]
|
|
|
|
|
|
|
+ [filesQuery.error],
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ const showLoadingUi = useDebouncedVisibility(
|
|
|
|
|
+ filesQuery.status === "loading",
|
|
|
|
|
+ {
|
|
|
|
|
+ delayMs: LOADING_DELAY_MS,
|
|
|
|
|
+ minVisibleMs: 0,
|
|
|
|
|
+ },
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
React.useEffect(() => {
|
|
@@ -80,7 +83,7 @@ export default function FilesExplorer({ branch, year, month, day }) {
|
|
|
: dayPath(branch, year, month, day);
|
|
: dayPath(branch, year, month, day);
|
|
|
|
|
|
|
|
window.location.replace(
|
|
window.location.replace(
|
|
|
- buildLoginUrl({ reason: LOGIN_REASONS.EXPIRED, next })
|
|
|
|
|
|
|
+ buildLoginUrl({ reason: LOGIN_REASONS.EXPIRED, next }),
|
|
|
);
|
|
);
|
|
|
}, [mapped?.kind, branch, year, month, day]);
|
|
}, [mapped?.kind, branch, year, month, day]);
|
|
|
|
|
|
|
@@ -128,7 +131,7 @@ export default function FilesExplorer({ branch, year, month, day }) {
|
|
|
</Button>
|
|
</Button>
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- if (filesQuery.status === "loading") {
|
|
|
|
|
|
|
+ if (showLoadingUi) {
|
|
|
return (
|
|
return (
|
|
|
<ExplorerPageShell
|
|
<ExplorerPageShell
|
|
|
title="Dateien"
|
|
title="Dateien"
|
|
@@ -143,6 +146,19 @@ export default function FilesExplorer({ branch, year, month, day }) {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (filesQuery.status === "loading") {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <ExplorerPageShell
|
|
|
|
|
+ title="Dateien"
|
|
|
|
|
+ description="Lieferscheine für den ausgewählten Tag."
|
|
|
|
|
+ breadcrumbs={breadcrumbsNode}
|
|
|
|
|
+ actions={actions}
|
|
|
|
|
+ >
|
|
|
|
|
+ <div className="h-16" aria-hidden="true" />
|
|
|
|
|
+ </ExplorerPageShell>
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (filesQuery.status === "error" && mapped) {
|
|
if (filesQuery.status === "error" && mapped) {
|
|
|
if (mapped.kind === "forbidden")
|
|
if (mapped.kind === "forbidden")
|
|
|
return <ForbiddenView attemptedBranch={branch} />;
|
|
return <ForbiddenView attemptedBranch={branch} />;
|
|
@@ -170,12 +186,7 @@ export default function FilesExplorer({ branch, year, month, day }) {
|
|
|
description="Sitzung abgelaufen — Weiterleitung zum Login…"
|
|
description="Sitzung abgelaufen — Weiterleitung zum Login…"
|
|
|
breadcrumbs={breadcrumbsNode}
|
|
breadcrumbs={breadcrumbsNode}
|
|
|
>
|
|
>
|
|
|
- <ExplorerSectionCard
|
|
|
|
|
- title="Weiterleitung"
|
|
|
|
|
- description="Bitte warten…"
|
|
|
|
|
- >
|
|
|
|
|
- <ExplorerLoading variant="table" count={6} />
|
|
|
|
|
- </ExplorerSectionCard>
|
|
|
|
|
|
|
+ <div className="h-16" aria-hidden="true" />
|
|
|
</ExplorerPageShell>
|
|
</ExplorerPageShell>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|