PTA fundraiser

import { useState, useEffect, useRef } from “react”; const ANTHROPIC_API = “https://api.anthropic.com/v1/messages”; const TARGET_EMAIL = “info@chalkwild.com”; const SENDER_NAME = “AnhViet”; const GMAIL_MCP = “https://gmail.mcp.claude.com/mcp”; const SEARCH_PROMPT = `Search the web for California PTA (Parent Teacher Association) fundraisers happening now or soon in 2025-2026. Look for: – School PTA/PTSA fundraising events in California (K-5 especially) – Contact information (email, phone, names of organizers) – Event dates, locations, school names, districts – Type of fundraiser (auction, carnival, jog-a-thon, etc.) Search queries to use: “California PTA fundraiser 2025”, “California PTSA fundraiser 2026”, “school PTA fundraiser California contact” Return a structured JSON array with these fields for each result: [{ “school”: “School Name”, “district”: “School District”, “city”: “City, CA”, “contact_name”: “Contact Person Name or PTA President”, “contact_email”: “email if found”, “contact_phone”: “phone if found”, “event_type”: “Type of fundraiser”, “event_date”: “Date or date range”, “description”: “Brief description”, “source_url”: “URL where found” }] Return ONLY the JSON array, no other text.`; function buildOutreachPrompt(lead) { return `You are a friendly, professional sales rep for ChalkWild, an educational company that makes the “CreateCalm Kit” β€” an erasable art canvas backpack paired with Social-Emotional Learning (SEL) curriculum for K-5 students. The kit has been clinically proven to increase student self-esteem by 41.3% using the Rosenberg Self-Esteem Scale. ChalkWild serves California ELOP and after-school programs and is aligned with LCAP priorities. Write a short, warm, personalized outreach email to a PTA contact about partnering on their upcoming fundraiser. The email should: – Open by acknowledging their specific fundraiser warmly – Briefly introduce ChalkWild and the CreateCalm Kit (2 sentences max) – Pitch the fundraiser partnership: the PTA sells CreateCalm Kits, keeps a percentage of profits, and students get an SEL art tool they love – Close with a soft, friendly call to action (quick call or reply to learn more) – Sign off as ${SENDER_NAME}, Co-Founder & Chief Advocate at ChalkWild Lead details: School: ${lead.school || “their school”} District: ${lead.district || “”} City: ${lead.city || “California”} Contact Name: ${lead.contact_name || “PTA Team”} Event Type: ${lead.event_type || “fundraiser”} Event Date: ${lead.event_date || “upcoming”} Description: ${lead.description || “”} Keep the email under 200 words. Warm, human, not salesy. No bullet points in the body. Start directly with “Hi [Name],” using their actual contact name. Return ONLY the email body text β€” no subject line, no extra commentary.`; } // ─── Spinner ────────────────────────────────────────────────────────────────── function Spinner({ size = 16 }) { return (
); } // ─── Outreach Modal ─────────────────────────────────────────────────────────── function OutreachModal({ lead, onClose }) { const [phase, setPhase] = useState(“generating”); const [subject, setSubject] = useState(“”); const [body, setBody] = useState(“”); const [toEmail, setToEmail] = useState(lead.contact_email || “”); const [errMsg, setErrMsg] = useState(“”); const [copied, setCopied] = useState(false); useEffect(() => { generateDraft(); }, []); const generateDraft = async () => { setPhase(“generating”); setErrMsg(“”); try { const res = await fetch(ANTHROPIC_API, { method: “POST”, headers: { “Content-Type”: “application/json” }, body: JSON.stringify({ model: “claude-sonnet-4-6”, max_tokens: 1000, messages: [{ role: “user”, content: buildOutreachPrompt(lead) }], }), }); const data = await res.json(); const text = (data.content || []).filter(b => b.type === “text”).map(b => b.text).join(“”).trim(); setBody(text); setSubject(`Partnership Opportunity: ${lead.school || “Your PTA”} Fundraiser Γ— ChalkWild CreateCalm Kit`); setPhase(“editing”); } catch (e) { setErrMsg(“Could not generate draft: ” + e.message); setPhase(“error”); } }; const sendOutreach = async () => { if (!toEmail) { setErrMsg(“Please enter a recipient email.”); return; } setPhase(“sending”); setErrMsg(“”); try { await fetch(ANTHROPIC_API, { method: “POST”, headers: { “Content-Type”: “application/json” }, body: JSON.stringify({ model: “claude-sonnet-4-6”, max_tokens: 500, mcp_servers: [{ type: “url”, url: GMAIL_MCP, name: “gmail-mcp” }], messages: [{ role: “user”, content: `Send this email via Gmail now: To: ${toEmail} From: ${TARGET_EMAIL} Subject: ${subject} ${body}`, }], }), }); setPhase(“sent”); } catch (e) { setErrMsg(“Send failed: ” + e.message); setPhase(“error”); } }; const copyAll = () => { navigator.clipboard.writeText(`To: ${toEmail}\nSubject: ${subject}\n\n${body}`); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const wordCount = body.split(/\s+/).filter(Boolean).length; return (
e.target === e.currentTarget && onClose()} style={{ position: “fixed”, inset: 0, zIndex: 200, background: “rgba(6,3,20,0.88)”, backdropFilter: “blur(10px)”, display: “flex”, alignItems: “center”, justifyContent: “center”, padding: “16px”, }} >
{/* Close */} {/* Modal header */}
βœ‰οΈ OUTREACH COMPOSER
{lead.school}
{lead.contact_name || “PTA Team”} {lead.event_type ? ` Β· ${lead.event_type}` : “”} {lead.event_date ? ` Β· ${lead.event_date}` : “”}
{/* GENERATING */} {phase === “generating” && (
✍️ Writing personalized pitch for {lead.contact_name || “this PTA”}…
Tailoring to their {lead.event_type || “fundraiser”}
)} {/* SENT */} {phase === “sent” && (
πŸŽ‰
Email Sent!
Your ChalkWild pitch was delivered to
{toEmail}
)} {/* ERROR */} {phase === “error” && (
❌ {errMsg}
)} {/* EDITING / SENDING */} {(phase === “editing” || phase === “sending”) && ( <> {/* To */}
setToEmail(e.target.value)} placeholder=”pta@school.org” style={{ width: “100%”, boxSizing: “border-box”, background: “rgba(255,255,255,0.06)”, border: “1px solid rgba(196,181,253,0.2)”, borderRadius: “9px”, padding: “10px 13px”, color: “#e8e0f5”, fontSize: “14px”, outline: “none”, transition: “border-color 0.2s”, }} /> {!toEmail && (
⚠️ No email found for this contact β€” enter manually
)}
{/* Subject */}
setSubject(e.target.value)} style={{ width: “100%”, boxSizing: “border-box”, background: “rgba(255,255,255,0.06)”, border: “1px solid rgba(196,181,253,0.2)”, borderRadius: “9px”, padding: “10px 13px”, color: “#e8e0f5”, fontSize: “14px”, outline: “none”, transition: “border-color 0.2s”, }} />
{/* Body */}
{wordCount} words