From 1aa76ddd33f5c1c3d97336f99c43f7d3d0db076f Mon Sep 17 00:00:00 2001 From: azizu06 Date: Sun, 12 Jul 2026 15:31:03 -0400 Subject: [PATCH] Keep archived demo interfaces visible --- README.md | 4 +-- src/app/analyze/page.tsx | 11 +------ src/app/globals.css | 22 ------------- src/app/page.tsx | 7 +--- src/components/capcheck-app.test.tsx | 17 ++++++++++ src/components/capcheck-app.tsx | 8 +++-- src/components/intake-panel.tsx | 16 ++++++---- src/components/portfolio-demo-notice.test.tsx | 19 ----------- src/components/portfolio-demo-notice.tsx | 32 ------------------- src/components/refresh-feed-button.test.tsx | 25 +++++++++++++++ src/components/refresh-feed-button.tsx | 16 +++++++--- 11 files changed, 73 insertions(+), 104 deletions(-) delete mode 100644 src/components/portfolio-demo-notice.test.tsx delete mode 100644 src/components/portfolio-demo-notice.tsx create mode 100644 src/components/refresh-feed-button.test.tsx diff --git a/README.md b/README.md index 2d6d0ec..39a0bd2 100644 --- a/README.md +++ b/README.md @@ -180,8 +180,8 @@ duplicate) with no credentials. Live mode additionally requires a server-only The hosted portfolio deployment intentionally omits those private credentials. It reads the persisted catalog through the public Supabase anon key under RLS, -while live refresh and analysis render an archived-demo state instead of -calling external providers. +while the original refresh and analysis interfaces remain visible but disabled +instead of calling external providers. Opt-in live smoke test (never part of CI): diff --git a/src/app/analyze/page.tsx b/src/app/analyze/page.tsx index 2712b35..380f19a 100644 --- a/src/app/analyze/page.tsx +++ b/src/app/analyze/page.tsx @@ -1,7 +1,6 @@ import type { Metadata } from "next"; import { CapCheckApp } from "@/components/capcheck-app"; -import { PortfolioDemoNotice } from "@/components/portfolio-demo-notice"; import { isPortfolioDemoMode } from "@/lib/portfolio-mode"; export const metadata: Metadata = { @@ -10,13 +9,5 @@ export const metadata: Metadata = { }; export default function AnalyzePage() { - if (isPortfolioDemoMode()) { - return ( -
- -
- ); - } - - return ; + return ; } diff --git a/src/app/globals.css b/src/app/globals.css index fe6d938..34dfd4b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1453,28 +1453,6 @@ input:disabled { font-size: 0.92rem; } -.portfolio-demo-notice { - margin-top: 24px; -} - -.portfolio-demo-notice > svg { - color: var(--ink); -} - -.portfolio-demo-notice .kicker { - margin-bottom: 6px; -} - -.portfolio-demo-notice h2 { - margin: 0 0 6px; - color: var(--ink); - font: 600 clamp(1.2rem, 2.5vw, 1.6rem)/1.2 var(--display); -} - -.portfolio-demo-page { - max-width: 760px; -} - .feed-state-action { display: inline-flex; align-items: center; diff --git a/src/app/page.tsx b/src/app/page.tsx index e5e4da0..8f7be3e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,7 +2,6 @@ import { TriangleAlert } from "lucide-react"; import Link from "next/link"; import { FeedExplorer } from "@/components/feed/feed-explorer"; -import { PortfolioDemoNotice } from "@/components/portfolio-demo-notice"; import { RefreshFeedButton } from "@/components/refresh-feed-button"; import type { CatalogItem } from "@/domain/feed"; import { isPortfolioDemoMode } from "@/lib/portfolio-mode"; @@ -58,11 +57,7 @@ export default async function FeedHome({ only if its claims held up. Open one to see the Cap Score, the evidence, and the citations behind it.

- {portfolioDemo ? ( - - ) : ( - - )} + {failed ? ( diff --git a/src/components/capcheck-app.test.tsx b/src/components/capcheck-app.test.tsx index 526b384..ed3af92 100644 --- a/src/components/capcheck-app.test.tsx +++ b/src/components/capcheck-app.test.tsx @@ -52,6 +52,23 @@ describe("CapCheckApp", () => { expect(screen.getByText(/choose a video file/i)).toBeInTheDocument(); }); + it("preserves the analyzer UI while disabling retired production actions", () => { + const fetchMock = vi.fn(); + vi.stubGlobal("fetch", fetchMock); + const { container } = render(); + + expect( + screen.getByRole("heading", { name: /is that stock tip/i }), + ).toBeVisible(); + expect(landingUrlInput()).toBeDisabled(); + expect(checkItButton()).toBeDisabled(); + expect(screen.getByLabelText(/choose a video file/i)).toBeDisabled(); + expect(screen.getByText(/live analysis is disabled/i)).toBeVisible(); + + fireEvent.submit(container.querySelector(".intake form")!); + expect(fetchMock).not.toHaveBeenCalled(); + }); + it("submits a valid URL through the public stream and renders the scorecard", async () => { const fetchMock = vi.fn().mockResolvedValue( sseResponse( diff --git a/src/components/capcheck-app.tsx b/src/components/capcheck-app.tsx index e71c2a9..396c73a 100644 --- a/src/components/capcheck-app.tsx +++ b/src/components/capcheck-app.tsx @@ -18,7 +18,7 @@ const allowedScenarios = new Set([ "fatal", ]); -export function CapCheckApp() { +export function CapCheckApp({ readOnly = false }: { readOnly?: boolean } = {}) { const [url, setUrl] = useState(""); const [file, setFile] = useState(null); const [nextUrl, setNextUrl] = useState(""); @@ -52,6 +52,7 @@ export function CapCheckApp() { }, [uploadPreviewUrl]); const performAnalysis = async (submitUrl: string, submitFile: File | null) => { + if (readOnly) return; setValidation(""); setMiniError(""); setError(null); @@ -110,7 +111,7 @@ export function CapCheckApp() { }; const analyze = () => { - if (loading) return; + if (readOnly || loading) return; const validationMessage = validateSubmission(url, file); if (validationMessage) { setError(null); @@ -122,7 +123,7 @@ export function CapCheckApp() { const analyzeNext = (event: FormEvent) => { event.preventDefault(); - if (loading) return; + if (readOnly || loading) return; const validationMessage = validateSubmission(nextUrl, null); if (validationMessage) { setMiniError(validationMessage); @@ -209,6 +210,7 @@ export function CapCheckApp() { url={url} file={file} loading={loading} + readOnly={readOnly} error={error} validation={validation} onUrlChange={changeUrl} diff --git a/src/components/intake-panel.tsx b/src/components/intake-panel.tsx index 22128a0..5f62bf6 100644 --- a/src/components/intake-panel.tsx +++ b/src/components/intake-panel.tsx @@ -41,6 +41,7 @@ type Props = { url: string; file: File | null; loading: boolean; + readOnly?: boolean; error: ErrorEvent["error"] | null; validation: string; onUrlChange(value: string): void; @@ -59,6 +60,7 @@ export function IntakePanel({ url, file, loading, + readOnly = false, error, validation, onUrlChange, @@ -116,18 +118,20 @@ export function IntakePanel({ id={inputId} type="url" value={url} - disabled={loading || Boolean(file)} + disabled={readOnly || loading || Boolean(file)} aria-invalid={Boolean(validation)} aria-describedby={describedBy} placeholder="https://www.youtube.com/shorts/…" onChange={(event) => onUrlChange(event.target.value)} /> -

- YouTube, TikTok, and Reels links work. Analysis takes about a minute. + {readOnly + ? "Portfolio demo — live analysis is disabled." + : "YouTube, TikTok, and Reels links work. Analysis takes about a minute."}

{validation && (
or upload a video