fdf2d741a1
- docs route renders without the app shell (own header, back-to-app link, theme toggle) - nav entry renamed to Hilfe (de) / Help (en) - reference links now case-insensitive (schema/tag slugs) - handbook rewritten as 17-page user guide with links into the API reference - release notes v0.8.1 added
94 lines
3.1 KiB
TypeScript
94 lines
3.1 KiB
TypeScript
import { Fragment } from "react";
|
|
import { useLocation, Link } from "wouter";
|
|
import { useTranslation } from "react-i18next";
|
|
import type { TFunction } from "i18next";
|
|
import { useGetTool, getGetToolQueryKey } from "@workspace/api-client-react";
|
|
import {
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
BreadcrumbLink,
|
|
BreadcrumbList,
|
|
BreadcrumbPage,
|
|
BreadcrumbSeparator,
|
|
} from "@/components/ui/breadcrumb";
|
|
|
|
type Crumb = { href?: string; label: string };
|
|
|
|
function buildCrumbs(location: string, t: TFunction): Crumb[] {
|
|
const crumbs: Crumb[] = [];
|
|
|
|
if (location.startsWith("/tools")) {
|
|
crumbs.push({ href: "/tools", label: t("nav.browseTools") });
|
|
const match = location.match(/^\/tools\/(\d+)/);
|
|
if (match) {
|
|
crumbs.push({ href: `/tools/${match[1]}`, label: t("browse.tool") });
|
|
if (location.includes("/edit")) crumbs.push({ label: t("common.edit") });
|
|
} else if (location.startsWith("/tools/new")) {
|
|
crumbs.push({ label: t("nav.addTool") });
|
|
}
|
|
} else if (location.startsWith("/admin")) {
|
|
crumbs.push({ href: "/admin", label: t("nav.admin") });
|
|
if (location.startsWith("/admin/redundancy")) crumbs.push({ label: t("nav.redundancy") });
|
|
} else if (location.startsWith("/watchlist")) {
|
|
crumbs.push({ label: t("nav.watchlist") });
|
|
} else if (location.startsWith("/trash")) {
|
|
crumbs.push({ label: t("nav.trash") });
|
|
} else if (location.startsWith("/compare")) {
|
|
crumbs.push({ label: t("compare.title") });
|
|
} else if (location.startsWith("/analytics")) {
|
|
crumbs.push({ label: t("nav.analytics") });
|
|
} else if (location.startsWith("/login")) {
|
|
crumbs.push({ label: t("auth.signIn") });
|
|
}
|
|
|
|
return crumbs;
|
|
}
|
|
|
|
export function Breadcrumbs() {
|
|
const [location] = useLocation();
|
|
const { t } = useTranslation();
|
|
|
|
const detailMatch = location.match(/^\/tools\/(\d+)/);
|
|
const toolId = detailMatch ? parseInt(detailMatch[1], 10) : 0;
|
|
const { data: tool } = useGetTool(Number.isFinite(toolId) ? toolId : 0, {
|
|
query: {
|
|
queryKey: getGetToolQueryKey(toolId),
|
|
enabled: Number.isFinite(toolId) && toolId > 0,
|
|
},
|
|
});
|
|
|
|
const crumbs = buildCrumbs(location, t);
|
|
if (crumbs.length <= 1) return null;
|
|
|
|
if (toolId > 0 && tool?.name) {
|
|
const idx = crumbs.findIndex((c) => c.href === `/tools/${toolId}`);
|
|
if (idx >= 0) crumbs[idx].label = tool.name;
|
|
}
|
|
|
|
return (
|
|
<Breadcrumb className="min-w-0">
|
|
<BreadcrumbList className="flex-nowrap">
|
|
{crumbs.map((crumb, i) => {
|
|
const isLast = i === crumbs.length - 1;
|
|
return (
|
|
<Fragment key={i}>
|
|
{i > 0 && <BreadcrumbSeparator />}
|
|
<BreadcrumbItem className="min-w-0">
|
|
{isLast || !crumb.href ? (
|
|
<BreadcrumbPage className="text-sm truncate">{crumb.label}</BreadcrumbPage>
|
|
) : (
|
|
<BreadcrumbLink asChild className="text-sm">
|
|
<Link href={crumb.href} className="truncate">
|
|
{crumb.label}
|
|
</Link>
|
|
</BreadcrumbLink>
|
|
)}
|
|
</BreadcrumbItem>
|
|
</Fragment>
|
|
);
|
|
})}
|
|
</BreadcrumbList>
|
|
</Breadcrumb>
|
|
);
|
|
}
|