"use client"; import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { Loader2, PanelRight } from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; import { useParams, usePathname, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import type React from "react"; import { useEffect, useMemo, useState } from "react"; import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms"; import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer"; import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb"; import { LanguageSwitcher } from "@/components/LanguageSwitcher"; import { AppSidebarProvider } from "@/components/sidebar/AppSidebarProvider"; import { ThemeTogglerComponent } from "@/components/theme/theme-toggle"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import { useLLMPreferences } from "@/hooks/use-llm-configs"; import { useUserAccess } from "@/hooks/use-rbac"; import { cn } from "@/lib/utils"; export function DashboardClientLayout({ children, searchSpaceId, navSecondary, navMain, }: { children: React.ReactNode; searchSpaceId: string; navSecondary: any[]; navMain: any[]; }) { const t = useTranslations("dashboard"); const router = useRouter(); const pathname = usePathname(); const searchSpaceIdNum = Number(searchSpaceId); const { search_space_id, chat_id } = useParams(); const [chatUIState, setChatUIState] = useAtom(activeChathatUIAtom); const activeChatId = useAtomValue(activeChatIdAtom); const setActiveSearchSpaceIdState = useSetAtom(activeSearchSpaceIdAtom); const setActiveChatIdState = useSetAtom(activeChatIdAtom); const [showIndicator, setShowIndicator] = useState(false); const { isChatPannelOpen } = chatUIState; // Check if we're on the researcher page const isResearcherPage = pathname?.includes("/researcher"); // Show indicator when chat becomes active and panel is closed useEffect(() => { if (activeChatId && !isChatPannelOpen) { setShowIndicator(true); // Hide indicator after 5 seconds const timer = setTimeout(() => setShowIndicator(false), 5000); return () => clearTimeout(timer); } else { setShowIndicator(false); } }, [activeChatId, isChatPannelOpen]); const { loading, error, isOnboardingComplete } = useLLMPreferences(searchSpaceIdNum); const { access, loading: accessLoading } = useUserAccess(searchSpaceIdNum); const [hasCheckedOnboarding, setHasCheckedOnboarding] = useState(false); // Skip onboarding check if we're already on the onboarding page const isOnboardingPage = pathname?.includes("/onboard"); // Only owners should see onboarding - invited members use existing config const isOwner = access?.is_owner ?? false; // Translate navigation items const tNavMenu = useTranslations("nav_menu"); const translatedNavMain = useMemo(() => { return navMain.map((item) => ({ ...item, title: tNavMenu(item.title.toLowerCase().replace(/ /g, "_")), items: item.items?.map((subItem: any) => ({ ...subItem, title: tNavMenu(subItem.title.toLowerCase().replace(/ /g, "_")), })), })); }, [navMain, tNavMenu]); const translatedNavSecondary = useMemo(() => { return navSecondary.map((item) => ({ ...item, title: item.title === "All Search Spaces" ? tNavMenu("all_search_spaces") : item.title, })); }, [navSecondary, tNavMenu]); const [open, setOpen] = useState(() => { try { const match = document.cookie.match(/(?:^|; )sidebar_state=([^;]+)/); if (match) return match[1] === "true"; } catch { // ignore } return true; }); useEffect(() => { // Skip check if already on onboarding page if (isOnboardingPage) { setHasCheckedOnboarding(true); return; } // Wait for both preferences and access data to load if (!loading && !accessLoading && !hasCheckedOnboarding) { const onboardingComplete = isOnboardingComplete(); // Only redirect to onboarding if user is the owner and onboarding is not complete // Invited members (non-owners) should skip onboarding and use existing config if (!onboardingComplete && isOwner) { router.push(`/dashboard/${searchSpaceId}/onboard`); } setHasCheckedOnboarding(true); } }, [ loading, accessLoading, isOnboardingComplete, isOnboardingPage, isOwner, router, searchSpaceId, hasCheckedOnboarding, ]); // Synchronize active search space and chat IDs with URL useEffect(() => { const activeSeacrhSpaceId = typeof search_space_id === "string" ? search_space_id : Array.isArray(search_space_id) && search_space_id.length > 0 ? search_space_id[0] : ""; if (!activeSeacrhSpaceId) return; setActiveSearchSpaceIdState(activeSeacrhSpaceId); }, [search_space_id]); useEffect(() => { const activeChatId = typeof chat_id === "string" ? chat_id : Array.isArray(chat_id) && chat_id.length > 0 ? chat_id[0] : ""; if (!activeChatId) return; setActiveChatIdState(activeChatId); }, [chat_id, search_space_id]); // Show loading screen while checking onboarding status (only on first load) if (!hasCheckedOnboarding && (loading || accessLoading) && !isOnboardingPage) { return (
{t("loading_config")} {t("checking_llm_prefs")}
); } // Show error screen if there's an error loading preferences (but not on onboarding page) if (error && !hasCheckedOnboarding && !isOnboardingPage) { return (
{t("config_error")} {t("failed_load_llm_config")}

{error}

); } return ( {/* Use AppSidebarProvider which fetches user, search space, and recent chats */}
{/* Only show artifacts toggle on researcher page */} {isResearcherPage && ( { setChatUIState((prev) => ({ ...prev, isChatPannelOpen: !isChatPannelOpen, })); setShowIndicator(false); }} className={cn( "shrink-0 rounded-full p-2 transition-all duration-300 relative", showIndicator ? "bg-primary/20 hover:bg-primary/30 shadow-lg shadow-primary/25" : "hover:bg-muted", activeChatId && !showIndicator && "hover:bg-primary/10" )} title="Toggle Artifacts Panel" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {/* Pulsing indicator badge */} {showIndicator && (
)} )}
{children}
{/* Only render chat panel on researcher page */} {isResearcherPage && }
); }