From e1e9ed6817cefb3f6f2c71fbeb787be6ab459889 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 15 Mar 2026 13:38:28 -0400 Subject: [PATCH 1/2] Add Manifold Markets prediction probability badges to bill cards Shows live prediction market probability next to bill numbers for bills that have associated Manifold Markets. Fetches probability on mount via the Manifold API and renders a clickable badge linking to the market. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/ManifoldBadge.jsx | 79 ++++++++++++++++++++++++++++++++ src/components/StatePanel.jsx | 21 ++++++--- src/data/manifoldMarkets.js | 16 +++++++ 3 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 src/components/ManifoldBadge.jsx create mode 100644 src/data/manifoldMarkets.js diff --git a/src/components/ManifoldBadge.jsx b/src/components/ManifoldBadge.jsx new file mode 100644 index 0000000..6be2b65 --- /dev/null +++ b/src/components/ManifoldBadge.jsx @@ -0,0 +1,79 @@ +import { useState, useEffect } from "react"; +import { colors, typography, spacing } from "../designTokens"; + +/** + * Displays a live prediction market probability badge from Manifold Markets. + * Fetches the current probability on mount and links to the market. + */ +export default function ManifoldBadge({ marketUrl }) { + const [prob, setProb] = useState(null); + + useEffect(() => { + if (!marketUrl) return; + + // Extract slug from URL: https://manifold.markets/User/slug -> slug + const slug = marketUrl.split("/").pop(); + if (!slug) return; + + fetch(`https://api.manifold.markets/v0/slug/${slug}`) + .then((r) => r.json()) + .then((data) => { + if (data.probability != null) { + setProb(data.probability); + } + }) + .catch(() => {}); + }, [marketUrl]); + + if (prob == null) return null; + + const pct = Math.round(prob * 100); + + return ( + e.stopPropagation()} + title="Prediction market probability (Manifold Markets)" + style={{ + display: "inline-flex", + alignItems: "center", + gap: spacing.xs, + padding: `2px ${spacing.sm}`, + borderRadius: spacing.radius.md, + backgroundColor: `${colors.primary[600]}12`, + border: `1px solid ${colors.primary[600]}30`, + color: colors.primary[700], + fontSize: typography.fontSize.xs, + fontWeight: typography.fontWeight.semibold, + fontFamily: typography.fontFamily.body, + textDecoration: "none", + flexShrink: 0, + transition: "background-color 0.15s ease", + }} + onMouseEnter={(e) => + (e.currentTarget.style.backgroundColor = `${colors.primary[600]}25`) + } + onMouseLeave={(e) => + (e.currentTarget.style.backgroundColor = `${colors.primary[600]}12`) + } + > + + + + {pct}% + + ); +} diff --git a/src/components/StatePanel.jsx b/src/components/StatePanel.jsx index 35ae81e..eeac9da 100644 --- a/src/components/StatePanel.jsx +++ b/src/components/StatePanel.jsx @@ -3,6 +3,8 @@ import { stateData } from "../data/states"; import { useData } from "../context/DataContext"; import ResearchCard from "./ResearchCard"; import ReformAnalyzer from "./reform/ReformAnalyzer"; +import ManifoldBadge from "./ManifoldBadge"; +import manifoldMarkets from "../data/manifoldMarkets"; import { colors, typography, spacing } from "../designTokens"; import { track } from "../lib/analytics"; import { BASE_PATH } from "../lib/basePath"; @@ -279,13 +281,18 @@ const StatePanel = memo(({ stateAbbr, onClose, initialBillId }) => {
-

{bill.bill}

+
+

{bill.bill}

+ {manifoldMarkets[bill.id] && ( + + )} +

Date: Wed, 18 Mar 2026 09:54:21 -0400 Subject: [PATCH 2/2] Migrate Manifold Markets URLs from static file to Supabase Replace hardcoded manifoldMarkets.js with a manifold_url column on the research table. The badge now reads from bill.manifoldUrl (populated via Supabase) instead of a static lookup. Co-Authored-By: Claude Opus 4.6 --- scripts/sql/007_add_manifold_url.sql | 4 ++++ src/components/StatePanel.jsx | 5 ++--- src/context/DataContext.jsx | 1 + src/data/manifoldMarkets.js | 16 ---------------- 4 files changed, 7 insertions(+), 19 deletions(-) create mode 100644 scripts/sql/007_add_manifold_url.sql delete mode 100644 src/data/manifoldMarkets.js diff --git a/scripts/sql/007_add_manifold_url.sql b/scripts/sql/007_add_manifold_url.sql new file mode 100644 index 0000000..0b10fee --- /dev/null +++ b/scripts/sql/007_add_manifold_url.sql @@ -0,0 +1,4 @@ +-- Add manifold_url column to research table for prediction market links +ALTER TABLE research ADD COLUMN IF NOT EXISTS manifold_url TEXT; + +COMMENT ON COLUMN research.manifold_url IS 'Manifold Markets prediction market URL for this bill'; diff --git a/src/components/StatePanel.jsx b/src/components/StatePanel.jsx index eeac9da..da0878c 100644 --- a/src/components/StatePanel.jsx +++ b/src/components/StatePanel.jsx @@ -4,7 +4,6 @@ import { useData } from "../context/DataContext"; import ResearchCard from "./ResearchCard"; import ReformAnalyzer from "./reform/ReformAnalyzer"; import ManifoldBadge from "./ManifoldBadge"; -import manifoldMarkets from "../data/manifoldMarkets"; import { colors, typography, spacing } from "../designTokens"; import { track } from "../lib/analytics"; import { BASE_PATH } from "../lib/basePath"; @@ -289,8 +288,8 @@ const StatePanel = memo(({ stateAbbr, onClose, initialBillId }) => { fontWeight: typography.fontWeight.semibold, fontFamily: typography.fontFamily.body, }}>{bill.bill}

- {manifoldMarkets[bill.id] && ( - + {bill.manifoldUrl && ( + )}