mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 09:16:22 +02:00
- Changed the API URL in the DashboardBreadcrumb component to include search space ID for fetching editor content, ensuring correct context is used when accessing documents.
272 lines
8.1 KiB
TypeScript
272 lines
8.1 KiB
TypeScript
"use client";
|
|
|
|
import { useAtomValue } from "jotai";
|
|
import { usePathname } from "next/navigation";
|
|
import { useTranslations } from "next-intl";
|
|
import React, { useEffect, useState } from "react";
|
|
import { activeChatAtom } from "@/atoms/chats/chat-query.atoms";
|
|
import {
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
BreadcrumbLink,
|
|
BreadcrumbList,
|
|
BreadcrumbPage,
|
|
BreadcrumbSeparator,
|
|
} from "@/components/ui/breadcrumb";
|
|
import { useSearchSpace } from "@/hooks/use-search-space";
|
|
|
|
interface BreadcrumbItemInterface {
|
|
label: string;
|
|
href?: string;
|
|
}
|
|
|
|
export function DashboardBreadcrumb() {
|
|
const t = useTranslations("breadcrumb");
|
|
const pathname = usePathname();
|
|
const { data: activeChatState } = useAtomValue(activeChatAtom);
|
|
// Extract search space ID and chat ID from pathname
|
|
const segments = pathname.split("/").filter(Boolean);
|
|
const searchSpaceId = segments[0] === "dashboard" && segments[1] ? segments[1] : null;
|
|
|
|
// Fetch search space details if we have an ID
|
|
const { searchSpace } = useSearchSpace({
|
|
searchSpaceId: searchSpaceId || "",
|
|
autoFetch: !!searchSpaceId,
|
|
});
|
|
|
|
// State to store document title for editor breadcrumb
|
|
const [documentTitle, setDocumentTitle] = useState<string | null>(null);
|
|
|
|
// Fetch document title when on editor page
|
|
useEffect(() => {
|
|
if (segments[2] === "editor" && segments[3] && searchSpaceId) {
|
|
const documentId = segments[3];
|
|
const token =
|
|
typeof window !== "undefined" ? localStorage.getItem("surfsense_bearer_token") : null;
|
|
|
|
if (token) {
|
|
fetch(
|
|
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/editor-content`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
)
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data.title) {
|
|
setDocumentTitle(data.title);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// If fetch fails, just use the document ID
|
|
setDocumentTitle(null);
|
|
});
|
|
}
|
|
} else {
|
|
setDocumentTitle(null);
|
|
}
|
|
}, [segments, searchSpaceId]);
|
|
|
|
// Parse the pathname to create breadcrumb items
|
|
const generateBreadcrumbs = (path: string): BreadcrumbItemInterface[] => {
|
|
const segments = path.split("/").filter(Boolean);
|
|
const breadcrumbs: BreadcrumbItemInterface[] = [];
|
|
|
|
// Always start with Dashboard
|
|
breadcrumbs.push({ label: t("dashboard"), href: "/dashboard" });
|
|
|
|
// Handle search space
|
|
if (segments[0] === "dashboard" && segments[1]) {
|
|
// Use the actual search space name if available, otherwise fall back to the ID
|
|
const searchSpaceLabel = searchSpace?.name || `${t("search_space")} ${segments[1]}`;
|
|
breadcrumbs.push({
|
|
label: searchSpaceLabel,
|
|
href: `/dashboard/${segments[1]}`,
|
|
});
|
|
|
|
// Handle specific sections
|
|
if (segments[2]) {
|
|
const section = segments[2];
|
|
let sectionLabel = section.charAt(0).toUpperCase() + section.slice(1);
|
|
|
|
// Map section names to more readable labels
|
|
const sectionLabels: Record<string, string> = {
|
|
researcher: t("researcher"),
|
|
documents: t("documents"),
|
|
connectors: t("connectors"),
|
|
sources: "Sources",
|
|
podcasts: t("podcasts"),
|
|
logs: t("logs"),
|
|
chats: t("chats"),
|
|
settings: t("settings"),
|
|
editor: t("editor"),
|
|
};
|
|
|
|
sectionLabel = sectionLabels[section] || sectionLabel;
|
|
|
|
// Handle sub-sections
|
|
if (segments[3]) {
|
|
const subSection = segments[3];
|
|
|
|
// Handle editor sub-sections (document ID)
|
|
if (section === "editor") {
|
|
const documentLabel = documentTitle || subSection;
|
|
breadcrumbs.push({
|
|
label: t("documents"),
|
|
href: `/dashboard/${segments[1]}/documents`,
|
|
});
|
|
breadcrumbs.push({
|
|
label: sectionLabel,
|
|
href: `/dashboard/${segments[1]}/documents`,
|
|
});
|
|
breadcrumbs.push({ label: documentLabel });
|
|
return breadcrumbs;
|
|
}
|
|
|
|
// Handle sources sub-sections
|
|
if (section === "sources") {
|
|
const sourceLabels: Record<string, string> = {
|
|
add: "Add Sources",
|
|
};
|
|
|
|
const sourceLabel = sourceLabels[subSection] || subSection;
|
|
breadcrumbs.push({
|
|
label: "Sources",
|
|
href: `/dashboard/${segments[1]}/sources`,
|
|
});
|
|
breadcrumbs.push({ label: sourceLabel });
|
|
return breadcrumbs;
|
|
}
|
|
|
|
// Handle documents sub-sections
|
|
if (section === "documents") {
|
|
const documentLabels: Record<string, string> = {
|
|
upload: t("upload_documents"),
|
|
youtube: t("add_youtube"),
|
|
webpage: t("add_webpages"),
|
|
};
|
|
|
|
const documentLabel = documentLabels[subSection] || subSection;
|
|
breadcrumbs.push({
|
|
label: t("documents"),
|
|
href: `/dashboard/${segments[1]}/documents`,
|
|
});
|
|
breadcrumbs.push({ label: documentLabel });
|
|
return breadcrumbs;
|
|
}
|
|
|
|
// Handle researcher sub-sections (chat IDs)
|
|
if (section === "researcher") {
|
|
// Use the actual chat title if available, otherwise fall back to the ID
|
|
const chatLabel = activeChatState?.chatDetails?.title || subSection;
|
|
breadcrumbs.push({
|
|
label: t("researcher"),
|
|
href: `/dashboard/${segments[1]}/researcher`,
|
|
});
|
|
breadcrumbs.push({ label: chatLabel });
|
|
return breadcrumbs;
|
|
}
|
|
|
|
// Handle connector sub-sections
|
|
if (section === "connectors") {
|
|
// Handle specific connector types
|
|
if (subSection === "add" && segments[4]) {
|
|
const connectorType = segments[4];
|
|
const connectorLabels: Record<string, string> = {
|
|
"github-connector": "GitHub",
|
|
"jira-connector": "Jira",
|
|
"confluence-connector": "Confluence",
|
|
"discord-connector": "Discord",
|
|
"linear-connector": "Linear",
|
|
"clickup-connector": "ClickUp",
|
|
"slack-connector": "Slack",
|
|
"notion-connector": "Notion",
|
|
"tavily-api": "Tavily API",
|
|
"serper-api": "Serper API",
|
|
"linkup-api": "LinkUp API",
|
|
"luma-connector": "Luma",
|
|
"elasticsearch-connector": "Elasticsearch",
|
|
"webcrawler-connector": "Web Pages",
|
|
};
|
|
|
|
const connectorLabel = connectorLabels[connectorType] || connectorType;
|
|
breadcrumbs.push({
|
|
label: "Connectors",
|
|
href: `/dashboard/${segments[1]}/connectors`,
|
|
});
|
|
breadcrumbs.push({
|
|
label: "Add Connector",
|
|
href: `/dashboard/${segments[1]}/connectors/add`,
|
|
});
|
|
breadcrumbs.push({ label: connectorLabel });
|
|
return breadcrumbs;
|
|
}
|
|
|
|
const connectorLabels: Record<string, string> = {
|
|
add: t("add_connector"),
|
|
manage: t("manage_connectors"),
|
|
};
|
|
|
|
const connectorLabel = connectorLabels[subSection] || subSection;
|
|
breadcrumbs.push({
|
|
label: t("connectors"),
|
|
href: `/dashboard/${segments[1]}/connectors`,
|
|
});
|
|
breadcrumbs.push({ label: connectorLabel });
|
|
return breadcrumbs;
|
|
}
|
|
|
|
// Handle other sub-sections
|
|
let subSectionLabel = subSection.charAt(0).toUpperCase() + subSection.slice(1);
|
|
const subSectionLabels: Record<string, string> = {
|
|
upload: t("upload_documents"),
|
|
youtube: t("add_youtube"),
|
|
webpage: t("add_webpages"),
|
|
add: t("add_connector"),
|
|
edit: t("edit_connector"),
|
|
manage: t("manage"),
|
|
};
|
|
|
|
subSectionLabel = subSectionLabels[subSection] || subSectionLabel;
|
|
|
|
breadcrumbs.push({
|
|
label: sectionLabel,
|
|
href: `/dashboard/${segments[1]}/${section}`,
|
|
});
|
|
breadcrumbs.push({ label: subSectionLabel });
|
|
} else {
|
|
breadcrumbs.push({ label: sectionLabel });
|
|
}
|
|
}
|
|
}
|
|
|
|
return breadcrumbs;
|
|
};
|
|
|
|
const breadcrumbs = generateBreadcrumbs(pathname);
|
|
|
|
if (breadcrumbs.length <= 1) {
|
|
return null; // Don't show breadcrumbs for root dashboard
|
|
}
|
|
|
|
return (
|
|
<Breadcrumb>
|
|
<BreadcrumbList>
|
|
{breadcrumbs.map((item, index) => (
|
|
<React.Fragment key={index}>
|
|
<BreadcrumbItem>
|
|
{index === breadcrumbs.length - 1 ? (
|
|
<BreadcrumbPage>{item.label}</BreadcrumbPage>
|
|
) : (
|
|
<BreadcrumbLink href={item.href}>{item.label}</BreadcrumbLink>
|
|
)}
|
|
</BreadcrumbItem>
|
|
{index < breadcrumbs.length - 1 && <BreadcrumbSeparator />}
|
|
</React.Fragment>
|
|
))}
|
|
</BreadcrumbList>
|
|
</Breadcrumb>
|
|
);
|
|
}
|