diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx
index 77cd4894a..843397608 100644
--- a/src/components/ClinicalDashboard.tsx
+++ b/src/components/ClinicalDashboard.tsx
@@ -5,7 +5,6 @@ import dynamic from "next/dynamic";
import {
CircleAlert,
BookOpen,
- ChevronDown,
Clock3,
ExternalLink,
FileImage,
@@ -42,7 +41,6 @@ import { readLocalProjectIdentity, unsafeLocalProjectMessage } from "@/lib/local
import { isDeployedClinicalKb } from "@/lib/deployed-app";
import {
appBackdrop,
- answerSurface,
cn,
EmptyState,
floatingControl,
@@ -54,11 +52,13 @@ import { useAuthSession } from "@/lib/supabase/client";
import { AccountSetupDialog } from "@/components/clinical-dashboard/account-setup-dialog";
import { CrossModeLinksSection } from "@/components/clinical-dashboard/cross-mode-links";
import { useEventCallback } from "@/components/clinical-dashboard/use-event-callback";
+import { useFavouritesAccess } from "@/components/clinical-dashboard/use-favourites-access";
import { AuthPanel } from "@/components/clinical-dashboard/auth-panel";
import { buildMobileSectionFabState, MobileSectionFab, ToolsHub } from "@/components/clinical-dashboard/dashboard-nav";
import { SettingsDialog } from "@/components/clinical-dashboard/settings-dialog";
import { useSidebarCollapsed } from "@/components/clinical-dashboard/use-sidebar-collapsed";
import { useTheme } from "@/components/clinical-dashboard/use-theme";
+import { PriorAnswerTurnSurface } from "@/components/clinical-dashboard/prior-answer-turn-surface";
import {
deriveSidebarIdentity,
ClinicalDesktopSidebar,
@@ -78,12 +78,7 @@ import {
} from "@/components/clinical-dashboard/DocumentManagerPanel";
import { GuideDialog, GuideTrigger, UtilityDrawer } from "@/components/clinical-dashboard/dashboard-shell";
import { sanitizeAnswerDisplayText, sanitizeDisplayText } from "@/components/clinical-dashboard/display-text";
-import {
- isPreformattedGroundedAnswer,
- NaturalLanguageAnswer,
- ScopeAndGovernanceNotice,
- UserQuestionBubble,
-} from "@/components/clinical-dashboard/answer-content";
+import { isPreformattedGroundedAnswer, ScopeAndGovernanceNotice } from "@/components/clinical-dashboard/answer-content";
import { AnswerEmptyState, AnswerProgressStepper, AnswerSkeleton } from "@/components/clinical-dashboard/answer-status";
import {
type AnswerProgressUpdate,
@@ -344,105 +339,6 @@ type AnswerTurn = {
const maxVisiblePriorTurns = 10;
-/**
- * Renders a collapsible, read-only view of a previous answer-thread turn with its question, answer, sources, and source-review notice.
- *
- * @param turn - The previous question and answer turn to display
- * @param copied - Whether the turn's answer has been copied
- * @param collapsed - Whether the answer content is collapsed
- * @param onToggleCollapsed - Called when the answer visibility is toggled
- * @param onCopy - Called with the answer text when copying is requested
- */
-function PriorAnswerTurnSurface({
- turn,
- copied,
- collapsed,
- onToggleCollapsed,
- onCopy,
-}: {
- turn: AnswerTurn;
- copied: boolean;
- collapsed: boolean;
- onToggleCollapsed: () => void;
- onCopy: (text: string) => void;
-}) {
- const renderModel = useMemo(
- () => buildAnswerRenderModel(turn.answer, { sources: turn.sources }),
- [turn.answer, turn.sources],
- );
- const turnPreformatted = isPreformattedGroundedAnswer(turn.answer);
- const safeText = useMemo(
- () => sanitizeAnswerDisplayText(turn.answer.answer, { preformatted: turnPreformatted }),
- [turn.answer.answer, turnPreformatted],
- );
- const sourceCount =
- renderModel.primarySources.length ||
- turn.sources.length ||
- turn.answer.sources?.length ||
- turn.answer.citations.length;
- const previewText = safeText || turn.answer.answer;
- const needsSourceReview =
- turn.answer.answerQualityTier === "source_only" ||
- turn.answer.grounded === false ||
- renderModel.trust === "low" ||
- renderModel.trust === "unsupported";
-
- return (
-
-
-
-
- {collapsed ? (
-
{previewText}
- ) : (
- <>
-
onCopy(renderModel.copyText || previewText)}
- />
- {needsSourceReview ? (
-
-
-
- Review source match. Verify cited
- passages before relying on this previous answer.
-
-
- ) : null}
- >
- )}
-
-
- );
-}
-
type LibraryHealthTarget = "documents" | "setup" | "indexing" | "failures";
type IndexingMonitorFilter = "all" | "active" | "failed";
type UploadIndexingTab = "setup" | "upload" | "jobs" | "quality";
@@ -672,7 +568,6 @@ export function ClinicalDashboard({
const [activeHash, setActiveHash] = useState("#search");
const [guideOpen, setGuideOpen] = useState(false);
const [settingsOpen, setSettingsOpen] = useState(false);
- const [accountSetupOpen, setAccountSetupOpen] = useState(false);
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useSidebarCollapsed();
const [documentsDrawerOpen, setDocumentsDrawerOpen] = useState(false);
@@ -811,6 +706,8 @@ export function ClinicalDashboard({
authUnavailableFallback: browserAuthUnavailableDemoFallback,
localNoAuthMode,
});
+ const { favouritesAccessible, accountSetupOpen, accountSetupIntent, openAccountSetup, closeAccountSetup } =
+ useFavouritesAccess(authStatus === "authenticated", clientDemoMode);
const answerThreadOwnerId = auth.session?.user.id ?? (clientDemoMode ? demoRecentQueryOwnerId : null);
const previousAnswerThreadOwnerIdRef = useRef(answerThreadOwnerId);
useEffect(() => {
@@ -889,12 +786,12 @@ export function ClinicalDashboard({
(except?: "guide" | "settings" | "accountSetup" | "mobileSidebar" | "documents" | "upload") => {
if (except !== "guide") setGuideOpen(false);
if (except !== "settings") setSettingsOpen(false);
- if (except !== "accountSetup") setAccountSetupOpen(false);
+ if (except !== "accountSetup") closeAccountSetup();
if (except !== "mobileSidebar") setMobileSidebarOpen(false);
if (except !== "documents") setDocumentsDrawerOpen(false);
if (except !== "upload") setUploadDrawerOpen(false);
},
- [],
+ [closeAccountSetup],
);
const openGuide = useCallback(() => {
closeDashboardTransientSurfaces("guide");
@@ -914,9 +811,8 @@ export function ClinicalDashboard({
return;
}
closeDashboardTransientSurfaces("accountSetup");
- setAccountSetupOpen(true);
- }, [closeDashboardTransientSurfaces, sidebarIdentity.signedIn]);
- const closeAccountSetup = useCallback(() => setAccountSetupOpen(false), []);
+ openAccountSetup("default");
+ }, [closeDashboardTransientSurfaces, openAccountSetup, sidebarIdentity.signedIn]);
const prefetchApplications = useCallback(() => {
router.prefetch("/?mode=tools");
router.prefetch("/favourites");
@@ -2760,6 +2656,11 @@ export function ClinicalDashboard({
}
function selectSearchMode(mode: AppModeId) {
+ if (mode === "favourites" && !favouritesAccessible) {
+ closeDashboardTransientSurfaces("accountSetup");
+ openAccountSetup("favourites");
+ return;
+ }
modeChangeFromUiRef.current = true;
if (mode === "differentials") clearDifferentialModeResultState();
setQuery("");
@@ -3468,6 +3369,7 @@ export function ClinicalDashboard({
recentQueries={recentQueries}
identity={sidebarIdentity}
activeMode={searchMode}
+ showAccountLibrary={favouritesAccessible}
onCollapsedChange={setSidebarCollapsed}
onNewChat={startNewChat}
onPickRecent={pickRecentQuery}
@@ -3493,6 +3395,11 @@ export function ClinicalDashboard({
realDataReady={canRunSearch}
onQueryChange={setQuery}
onSearchModeChange={selectSearchMode}
+ canAccessFavourites={favouritesAccessible}
+ onRequestAccountSetup={() => {
+ closeDashboardTransientSurfaces("accountSetup");
+ openAccountSetup("favourites");
+ }}
onAsk={ask}
onClearQuery={() => {
setQuery("");
@@ -4244,12 +4151,13 @@ export function ClinicalDashboard({
onSignOut={auth.signOut}
onOpenGuide={openGuide}
/>
-
+
void;
onPickRecent: (query: string) => void;
onOpenGuide: () => void;
@@ -279,6 +285,44 @@ export function ClinicalSidebarContent({
})}
+
+ {showAccountLibrary ? (
+
+
+
+
+ ) : null}
@@ -350,6 +394,7 @@ function ClinicalCollapsedRail({
collapseLocked,
identity,
activeMode,
+ showAccountLibrary = false,
onCollapsedChange,
onNewChat,
onOpenGuide,
@@ -364,6 +409,7 @@ function ClinicalCollapsedRail({
collapseLocked: boolean;
identity: SidebarIdentity;
activeMode: AppModeId;
+ showAccountLibrary?: boolean;
onCollapsedChange: (collapsed: boolean) => void;
onNewChat: () => void;
onOpenGuide: () => void;
@@ -451,6 +497,24 @@ function ClinicalCollapsedRail({
);
})}
+ {showAccountLibrary
+ ? sidebarAccountLibraryItems.map((item) => {
+ const Icon = item.icon;
+ const active = activeMode === item.id;
+ return (
+
+
+
+ );
+ })
+ : null}