Got it 👍 — here’s the entire Replit agent prompt as one single copy-and-paste block, no separate JSON or code boxes — everything inline, ready to paste directly: ⸻ I’ve finished the database view for campaign configuration and performance, and now I want the Campaigns tab in the dashboard to use it. There is a Postgres view called v_campaign_config_overview with these columns: month_uk (date), campaign_type (text), campaign_tag (text), name (text), outreached (bigint), replies (bigint), attended (bigint), reply_rate_pct (numeric, nullable), attended_rate_pct (numeric, nullable), generated_at_uk (timestamptz). An example row looks like: { “month_uk”: “2025-11-01”, “campaign_type”: “core_milestone”, “campaign_tag”: “renewal_18_new”, “name”: “18 months – New Car (End of Warranty Check)”, “outreached”: 0, “replies”: 0, “attended”: 0, “reply_rate_pct”: null, “attended_rate_pct”: null }. Your task is to make the Campaigns tab in the dashboard dynamic and data-driven. 1. In the backend (dashboard_app.py), create a new FastAPI endpoint at GET /api/campaigns. • Use the existing Supabase client to query v_campaign_config_overview. • Filter results to the current UK month using date_trunc(‘month’, now() at time zone ‘Europe/London’)::date. • Return JSON in this format: { “month”: “2025-11-01”, “campaigns”: [ { “campaign_type”: “core_milestone”, “campaign_tag”: “renewal_18_new”, “name”: “18 months – New Car (End of Warranty Check)”, “outreached”: 0, “replies”: 0, “attended”: 0, “reply_rate_pct”: null, “attended_rate_pct”: null } ] }. • Treat counts as integers and percentages as floats or null. • If the query fails, return { “error”: “could not load campaigns” } with status 500. • If an old /api/campaigns route exists, replace it completely with this version. 2. In the frontend (templates/index.html), make the Campaigns tab call this endpoint dynamically. • When the user clicks the “Campaigns” tab, highlight the tab as normal, then fetch /api/campaigns. • While data is loading, display “Loading campaigns…”. • If the fetch fails, display “Couldn’t load campaign data. Please try again.” 3. When data loads successfully, render grouped tables by campaign_type in this order: core_milestone, key_to_key, we_want_your_car, new_model_launch, sales_event, other. • For each group that has results, show a small heading such as “Core Milestones”, “Key-to-Key Upgrades”, “We Want Your Car”, “New Model Launch”, “Sales Event”, or “Other”. • Under each heading, create a table with these columns: Campaign (show name and smaller sub-text with campaign_tag), Sent (outreached), Replies (replies), Appointments (attended), Reply Rate (reply_rate_pct shown as “33.3%” or “—” if null), Attend Rate (attended_rate_pct shown as “33.3%” or “—” if null). 4. Match the style of the Contacts table with a subtle shadow, rounded corners, and alternating row colour. • The campaign name should be regular weight with the tag in smaller muted text beneath. • This tab is read-only; do not add buttons or edit functions yet. 5. After completing, restart the dashboard server. • Open /api/campaigns in the browser and confirm it returns the expected JSON. • Open the Campaigns tab and verify all campaigns appear grouped by type. • Ensure null percentage values display as “—”. • Ensure no other endpoints or tabs are affected (/api/kpis, /api/contacts, /api/tcard, Overview, Upload). When finished, summarise which files were modified (dashboard_app.py and templates/index.html) and provide a short JSON sample from /api/campaigns confirming successful output. ⸻