From d966d8659116b15ebf098a6d773d268685993577 Mon Sep 17 00:00:00 2001 From: kartik Date: Tue, 14 Jul 2026 18:22:00 +0530 Subject: [PATCH 1/5] add login-required dialog and gate ChatBox's LLM query behind login --- frontend/components/ChatBox.tsx | 13 ++++- frontend/components/login-required-dialog.tsx | 55 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 frontend/components/login-required-dialog.tsx diff --git a/frontend/components/ChatBox.tsx b/frontend/components/ChatBox.tsx index 3dacb07..a461770 100644 --- a/frontend/components/ChatBox.tsx +++ b/frontend/components/ChatBox.tsx @@ -12,7 +12,8 @@ import { } from "@/components/ui/select"; import { MarkdownRenderer } from "@/components/markdown-renderer"; import { MessageSquare, Send, Bot, User, Loader2 } from "lucide-react"; -import { getAccessToken } from "@auth0/nextjs-auth0/client"; +import { getAccessToken, useUser } from "@auth0/nextjs-auth0/client"; +import { LoginRequiredDialog } from "@/components/login-required-dialog"; type ModelId = "openai-model" | "local-model"; @@ -89,6 +90,8 @@ export const ChatBox: React.FC = ({ const [inputMessage, setInputMessage] = useState(""); const [isLoading, setIsLoading] = useState(false); const [selectedModel, setSelectedModel] = useState("openai-model"); + const [showLoginDialog, setShowLoginDialog] = useState(false); + const { user, isLoading: isUserLoading } = useUser(); const messagesEndRef = useRef(null); const inputRef = useRef(null); @@ -139,6 +142,11 @@ export const ChatBox: React.FC = ({ const handleSendMessage = async (overrideMessage?: string) => { const msg = overrideMessage ?? inputMessage; if (!msg.trim()) return; + if (isUserLoading) return; + if (!user) { + setShowLoginDialog(true); + return; + } // Build parameter context string let parameterContext = ""; if (parameters) { @@ -249,6 +257,8 @@ export const ChatBox: React.FC = ({ }; return ( + <> + @@ -406,5 +416,6 @@ export const ChatBox: React.FC = ({ )} + ); }; diff --git a/frontend/components/login-required-dialog.tsx b/frontend/components/login-required-dialog.tsx new file mode 100644 index 0000000..c23845c --- /dev/null +++ b/frontend/components/login-required-dialog.tsx @@ -0,0 +1,55 @@ +"use client"; + +import { usePathname, useSearchParams } from "next/navigation"; +import Link from "next/link"; +import { LogIn } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; + +interface LoginRequiredDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function LoginRequiredDialog({ + open, + onOpenChange, +}: LoginRequiredDialogProps) { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const query = searchParams.toString(); + const returnTo = query ? `${pathname}?${query}` : pathname; + const loginHref = `/auth/login?${new URLSearchParams({ returnTo }).toString()}`; + + return ( + + + + Log in to continue + + Log in to chat with the AI assistant or run an AI analysis. + You'll be brought right back here. + + + + + + + + + ); +} From 85b918396e16f1457a5250f3cd05f33aa6671d37 Mon Sep 17 00:00:00 2001 From: kartik Date: Tue, 14 Jul 2026 18:25:00 +0530 Subject: [PATCH 2/5] gate AI Analysis tab and its auto diagram-analysis on the biomodel detail page --- frontend/app/search/[bmid]/page.tsx | 32 ++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/frontend/app/search/[bmid]/page.tsx b/frontend/app/search/[bmid]/page.tsx index 199b8e3..207d12c 100644 --- a/frontend/app/search/[bmid]/page.tsx +++ b/frontend/app/search/[bmid]/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useParams } from "next/navigation"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { @@ -28,7 +28,8 @@ import { Briefcase, Cog, } from "lucide-react"; -import { getAccessToken } from "@auth0/nextjs-auth0/client"; +import { getAccessToken, useUser } from "@auth0/nextjs-auth0/client"; +import { LoginRequiredDialog } from "@/components/login-required-dialog"; interface Simulation { key: string; @@ -89,6 +90,9 @@ export default function BiomodelDetailPage() { const [diagramAnalysis, setDiagramAnalysis] = useState(""); const [analysisError, setAnalysisError] = useState(""); const [combinedMessages, setCombinedMessages] = useState([]); + const [showLoginDialog, setShowLoginDialog] = useState(false); + const { user, isLoading: isUserLoading } = useUser(); + const diagramFetchTriggeredRef = useRef(false); const quickActions = [ { @@ -150,7 +154,11 @@ export default function BiomodelDetailPage() { useEffect(() => { if (!data?.bmKey) return; - + if (activeTab !== "analysis") return; + if (isUserLoading || !user) return; + if (diagramFetchTriggeredRef.current) return; + diagramFetchTriggeredRef.current = true; + const fetchDiagramAnalysis = async () => { try { const token = await getAccessToken(); @@ -176,7 +184,7 @@ export default function BiomodelDetailPage() { }; fetchDiagramAnalysis(); - }, [data?.bmKey]); + }, [data?.bmKey, activeTab, isUserLoading, user]); // Create combined messages when diagram analysis is ready useEffect(() => { @@ -191,8 +199,22 @@ export default function BiomodelDetailPage() { const biomodelDiagramUrl = `https://vcell.cam.uchc.edu/api/v0/biomodel/${data.bmKey}/diagram`; + const handleTabChange = (value: string) => { + if (value !== "analysis") { + setActiveTab(value); + return; + } + if (isUserLoading) return; + if (!user) { + setShowLoginDialog(true); + return; + } + setActiveTab(value); + }; + return (
+
@@ -261,7 +283,7 @@ export default function BiomodelDetailPage() {
- + From 7e24ce30aee27a5e9c4005a1ccc216794876e18e Mon Sep 17 00:00:00 2001 From: kartik Date: Tue, 14 Jul 2026 18:29:00 +0530 Subject: [PATCH 3/5] gate Analyze Biomodel flow behind login --- frontend/app/analyze/[id]/page.tsx | 15 +++++++++++++-- frontend/app/analyze/page.tsx | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/app/analyze/[id]/page.tsx b/frontend/app/analyze/[id]/page.tsx index 0d05625..54d59c1 100644 --- a/frontend/app/analyze/[id]/page.tsx +++ b/frontend/app/analyze/[id]/page.tsx @@ -22,7 +22,8 @@ import { import { Button } from "@/components/ui/button"; import { ChatBox } from "@/components/ChatBox"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { getAccessToken } from "@auth0/nextjs-auth0/client"; +import { getAccessToken, useUser } from "@auth0/nextjs-auth0/client"; +import { LoginRequiredDialog } from "@/components/login-required-dialog"; interface AnalysisResults { title: string; @@ -63,6 +64,8 @@ export default function AnalysisResultsPage({ const [combinedMessages, setCombinedMessages] = useState([]); const [biomodelData, setBiomodelData] = useState(null); const [biomodelLoading, setBiomodelLoading] = useState(true); + const [showLoginDialog, setShowLoginDialog] = useState(false); + const { user, isLoading: isUserLoading } = useUser(); useEffect(() => { const fetchBiomodelData = async () => { @@ -88,6 +91,13 @@ export default function AnalysisResultsPage({ }, [id]); useEffect(() => { + if (isUserLoading) return; + if (!user) { + setShowLoginDialog(true); + setIsAnalysisLoading(false); + return; + } + const fetchDiagramAnalysis = async () => { setIsAnalysisLoading(true); setAnalysisError(""); @@ -154,7 +164,7 @@ export default function AnalysisResultsPage({ }; fetchBothAnalyses(); - }, [id, prompt]); + }, [id, prompt, isUserLoading, user]); // Create combined messages when analyses are ready useEffect(() => { @@ -247,6 +257,7 @@ export default function AnalysisResultsPage({ return (
+
diff --git a/frontend/app/analyze/page.tsx b/frontend/app/analyze/page.tsx index 023657a..65c26f4 100644 --- a/frontend/app/analyze/page.tsx +++ b/frontend/app/analyze/page.tsx @@ -18,6 +18,8 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { useUser } from "@auth0/nextjs-auth0/client"; +import { LoginRequiredDialog } from "@/components/login-required-dialog"; interface PromptTemplate { title: string; @@ -30,6 +32,8 @@ export default function AnalyzePage() { const router = useRouter(); const [biomodelId, setBiomodelId] = useState(""); const [prompt, setPrompt] = useState(""); + const [showLoginDialog, setShowLoginDialog] = useState(false); + const { user, isLoading: isUserLoading } = useUser(); const promptTemplates: PromptTemplate[] = [ { @@ -76,11 +80,17 @@ export default function AnalyzePage() { const handleAnalyze = () => { if (!biomodelId.trim() || !prompt.trim()) return; + if (isUserLoading) return; + if (!user) { + setShowLoginDialog(true); + return; + } router.push(`/analyze/${biomodelId}?prompt=${encodeURIComponent(prompt)}`); }; return (
+
{/* Header */}
From 6d4326f4db1537933562baf50f16af68312af199 Mon Sep 17 00:00:00 2001 From: kartik Date: Tue, 14 Jul 2026 18:35:00 +0530 Subject: [PATCH 4/5] point landing page hero CTAs at biomodel search instead of the login wall --- frontend/app/page.tsx | 5 +-- frontend/components/app-sidebar.tsx | 53 ++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 37e98b8..7e5d416 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -15,6 +15,7 @@ import Image from "next/image"; const loginHref = "/auth/login?returnTo=/chat"; const signupHref = "/auth/login?returnTo=/chat&screen_hint=signup"; +const exploreHref = "/search"; export default function LandingPage() { return ( @@ -78,7 +79,7 @@ export default function LandingPage() {

- + - +
From a2baeeed132f5c81542a2aea61d9b0cb1a5f272e Mon Sep 17 00:00:00 2001 From: kartik Date: Tue, 14 Jul 2026 18:45:00 +0530 Subject: [PATCH 5/5] make biomodel search, chat, and analyze routes public --- frontend/middleware.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/middleware.ts b/frontend/middleware.ts index fbeb1f1..de14757 100644 --- a/frontend/middleware.ts +++ b/frontend/middleware.ts @@ -2,16 +2,26 @@ import { NextResponse, type NextRequest } from 'next/server'; import { auth0 } from './lib/auth0'; const publicRoutes = new Set(['/']); +const publicRoutePrefixes = ['/search', '/chat', '/analyze']; const publicFilePattern = /\.(.*)$/; +function isPublicRoute(pathname: string): boolean { + return ( + publicRoutes.has(pathname) || + publicRoutePrefixes.some( + (prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`), + ) + ); +} + export async function middleware(request: NextRequest) { const { pathname, search } = request.nextUrl; const authResponse = await auth0.middleware(request); if ( - publicRoutes.has(pathname) || + isPublicRoute(pathname) || pathname.startsWith('/auth/') || publicFilePattern.test(pathname) ) {