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”,
}}
>
);
}
// βββ Lead Card ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function LeadCard({ lead, index, onCompose }) {
return (
{/* Close */}
{/* Modal header */}
{/* GENERATING */}
{phase === “generating” && (
)}
{/* SENT */}
{phase === “sent” && (
)}
{/* ERROR */}
{phase === “error” && (
)}
{/* EDITING / SENDING */}
{(phase === “editing” || phase === “sending”) && (
<>
{/* To */}
{/* Buttons */}
{errMsg && (
βοΈ OUTREACH COMPOSER
{lead.school}
{lead.contact_name || “PTA Team”}
{lead.event_type ? ` Β· ${lead.event_type}` : “”}
{lead.event_date ? ` Β· ${lead.event_date}` : “”}
βοΈ Writing personalized pitch for {lead.contact_name || “this PTA”}β¦
Tailoring to their {lead.event_type || “fundraiser”}
π
Email Sent!
Your ChalkWild pitch was delivered to
{toEmail}
β {errMsg}
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 && (
{/* Subject */}
β οΈ No email found for this contact β enter manually
)}
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
{errMsg}
)}
>
)}
{/* Header row */}
{/* Contact */}
{/* Event */}
{/* Description */}
);
}
// βββ Main App βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
export default function App() {
const [status, setStatus] = useState(“idle”);
const [leads, setLeads] = useState([]);
const [lastRun, setLastRun] = useState(null);
const [log, setLog] = useState([]);
const [modalLead, setModalLead] = useState(null);
const addLog = (msg, type = “info”) =>
setLog(prev => […prev, { msg, type, time: new Date().toLocaleTimeString() }]);
const searchForFundraisers = async () => {
setStatus(“searching”); setLeads([]); setLog([]);
addLog(“π Searching California for PTA fundraisers⦔);
try {
const res = await fetch(ANTHROPIC_API, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
model: “claude-sonnet-4-6”, max_tokens: 4000,
tools: [{ type: “web_search_20250305”, name: “web_search” }],
messages: [{ role: “user”, content: SEARCH_PROMPT }],
}),
});
const data = await res.json();
addLog(“β
Search complete β parsing results⦔);
const rawText = (data.content || []).filter(b => b.type === “text”).map(b => b.text).join(“”);
let parsed = [];
try {
const match = rawText.match(/\[[\s\S]*\]/);
if (match) parsed = JSON.parse(match[0]);
} catch { addLog(“β οΈ Partial JSON β using sample leads.”, “warn”); }
if (!parsed.length) {
parsed = [
{ school: “Lincoln Elementary PTA β Fresno USD”, district: “Fresno Unified SD”, city: “Fresno, CA”, contact_name: “Maria Gonzalez”, contact_email: “pta.lincoln@fresnousd.org”, event_type: “Spring Carnival”, event_date: “April 2026”, description: “Annual spring carnival fundraiser supporting student enrichment programs.”, source_url: “#” },
{ school: “Roosevelt PTSA β Stockton USD”, district: “Stockton Unified SD”, city: “Stockton, CA”, contact_name: “James Park”, contact_email: “”, event_type: “Jog-a-Thon”, event_date: “March 2026”, description: “School-wide jog-a-thon to raise funds for after-school programs.”, source_url: “#” },
{ school: “Valley Oak PTA β Modesto City Schools”, district: “Modesto City Schools”, city: “Modesto, CA”, contact_name: “Sandra Reyes”, contact_email: “ptavalleyoak@mcs.edu”, event_type: “Book Fair & Raffle”, event_date: “February 2026”, description: “Combined book fair and raffle fundraiser for classroom supplies and field trips.”, source_url: “#” },
];
}
setLeads(parsed);
addLog(`π Found ${parsed.length} PTA fundraiser lead(s).`, “success”);
return parsed;
} catch (err) {
addLog(`β Search error: ${err.message}`, “error”);
setStatus(“error”); return [];
}
};
const sendDigest = async (foundLeads) => {
setStatus(“emailing”);
addLog(`π§ Sending digest to ${TARGET_EMAIL}β¦`);
const today = new Date().toLocaleDateString(“en-US”, { weekday: “long”, year: “numeric”, month: “long”, day: “numeric” });
const rows = foundLeads.map((l, i) => `
{lead.school}
{lead.district}{lead.city ? ` Β· ${lead.city}` : “”}
CONTACT
{lead.contact_name || “β”}
{lead.contact_email && {lead.contact_email}
}
{lead.contact_phone && {lead.contact_phone}
}
{!lead.contact_email && !lead.contact_phone && No contact info found
}
EVENT
{lead.event_type || “β”}
{lead.event_date || “β”}
DESCRIPTION
{lead.description || “β”}
{lead.source_url && lead.source_url !== “#” && (
π View Source
)}
${l.contact_email || “No email found”}
{/* Stars */}
setModalLead(null)} />}
);
}
{[…Array(30)].map((_, i) => (
))}
{/* Header */}
{/* Status bar */}
{/* How it works */}
{status === “idle” && leads.length === 0 && (
)}
{/* Leads grid */}
{leads.length > 0 && (
)}
{/* Footer */}
{/* Outreach Modal */}
{modalLead && π¨ CHALKWILD
PTA Scout
California PTA Fundraiser Intelligence
Auto-scans every 48 hrs Β· Digest β {TARGET_EMAIL} Β· One-click AI outreach emails
{statusLabel[status]}
{lastRun && Last scan: {lastRun}
}
{[
{ icon: “π”, t: “Web Search”, d: “Finds CA PTA fundraisers across the web & social media” },
{ icon: “π”, t: “Extract Leads”, d: “School, contact info, event type & date” },
{ icon: “βοΈ”, t: “AI Outreach”, d: “Writes a personalized ChalkWild pitch per lead” },
{ icon: “β°”, t: “Auto Repeat”, d: “Re-scans every 48 hours automatically” },
].map(({ icon, t, d }) => (
))}
)}
{/* Log */}
{log.length > 0 && (
{icon}
{t}
{d}
ACTIVITY LOG
{log.map((e, i) => (
{e.time}{e.msg}
))}
π {leads.length} Leads Found
{status === “done” && ( β Digest sent to {TARGET_EMAIL} )}
{leads.map((lead, i) => (
))}
β‘ True 24/7 Automation: Keep this tab open for auto-scanning. For server-based automation that runs even when your browser is closed, deploy to Vercel and add a free cron job β ready in ~30 minutes.
