- Fix DOMPurify SSR crash (use isomorphic-dompurify) - Fix SanitizedHtml to sanitize by default - Add auth guards to studio/catalog maintenance pages - Add update/edit to vouchers CRUD - Add update/edit to rare-values CRUD - Add approve workflow to applications page - Add edit form to guilds detail page - Add SEO metadata to all public pages (21 pages) - Fix mobile nav accessibility (focus trap, aria attributes) - Fix missing labels and table accessibility - Add dynamic imports for heavy client components (6 components) - Fix silent error swallowing (40+ locations) - Add content scheduling for articles (publishAt, status) - Wire up 12 missing webhook notification triggers - Add global search to admin panel - Add bulk actions to admin users table - Fix JSON formatting and a11y issues
66 lines
1.9 KiB
TypeScript
66 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import { ChevronRight, Home } from "lucide-react";
|
|
import { Fragment, useEffect } from "react";
|
|
import { useCatalogManager } from "./use-catalog-store";
|
|
|
|
export function BreadcrumbBar() {
|
|
const { state, dispatch, catalogType } = useCatalogManager();
|
|
const { selectedPageId, ancestors } = state;
|
|
const catQs = catalogType === "bc" ? "&catalog=bc" : "";
|
|
|
|
useEffect(() => {
|
|
if (!selectedPageId || selectedPageId <= 0) {
|
|
dispatch({ type: "SET_ANCESTORS", ancestors: [] });
|
|
return;
|
|
}
|
|
const ac = new AbortController();
|
|
fetch(`/api/admin/catalog/tree?ancestors=${selectedPageId}${catQs}`, {
|
|
signal: ac.signal,
|
|
})
|
|
.then((r) => r.json())
|
|
.then((d) => {
|
|
if (!ac.signal.aborted) {
|
|
dispatch({ type: "SET_ANCESTORS", ancestors: d.ancestors ?? [] });
|
|
}
|
|
})
|
|
.catch((error) =>
|
|
console.error("[Breadcrumb] Failed to fetch ancestors:", error),
|
|
);
|
|
return () => ac.abort();
|
|
}, [selectedPageId, dispatch, catQs]);
|
|
|
|
if (!selectedPageId || ancestors.length === 0) return null;
|
|
|
|
return (
|
|
<div className="flex items-center gap-1 px-4 py-1 border-b bg-muted/20 text-xs text-muted-foreground overflow-x-auto shrink-0">
|
|
<Home className="h-3 w-3 shrink-0" />
|
|
{ancestors.map((a) => (
|
|
<Fragment key={a.id}>
|
|
<ChevronRight className="h-3 w-3 shrink-0" />
|
|
<button
|
|
type="button"
|
|
onClick={() => dispatch({ type: "SELECT_PAGE", id: a.id })}
|
|
className="shrink-0 hover:text-foreground transition-colors truncate max-w-40"
|
|
>
|
|
{a.caption}
|
|
</button>
|
|
</Fragment>
|
|
))}
|
|
{/* Current page */}
|
|
{(() => {
|
|
const node = state.nodes.get(selectedPageId);
|
|
if (!node) return null;
|
|
return (
|
|
<>
|
|
<ChevronRight className="h-3 w-3 shrink-0" />
|
|
<span className="shrink-0 text-foreground font-medium truncate max-w-40">
|
|
{node.caption}
|
|
</span>
|
|
</>
|
|
);
|
|
})()}
|
|
</div>
|
|
);
|
|
}
|