PageSourceSearch

https://testivo.tech/assets/admin.outreach-lBiVXiFi.js

js testivo.tech collected 2026-09-24 15:13:32 UTC 28,955 bytes, 1 lines download raw bytes

1import{r as m,j as e}from"./react-RN8z7mqm.js";import{g as W}from"./index-BwX0UczS.js";import{s as b}from"./supabase-D7bm-uKx.js";import{toast as u}from"./sonner-CSA395gJ.js";import"./bip-archetypes-M0ZT2xRn.js";import{q as B,as as A,bm as G,bn as K,y as q,ah as P,n as U,bo as X,g as J,bg as L,a3 as v,I as z,ad as C,bd as H,aM as $,ap as Q,C as R,a4 as M,T as V,u as Z,X as D}from"./icons-CFy5kY0-.js";const Y=[{id:"helpful_answer",name:"Helpful answer",hook:"They asked something or have a problem — just genuinely help. The #1 way Olly got noticed.",icon:"Lightbulb",color:"text-violet-500",promptHint:"Directly help with the exact thing in their tweet. Give one concrete, specific tip or answer they can act on. No fluff, no 'great question!', no pitch. If you don't actually know, don't pretend — pick a different move."},{id:"add_insight",name:"Add an angle",hook:"Agree, then add one useful thing they didn't say. Makes you look sharp, not needy.",icon:"Plus",color:"text-sky-500",promptHint:"Briefly agree or build on their point, then add ONE genuinely useful angle/detail/example they didn't mention. It should make a reader smarter. Never contradict just to stand out."},{id:"share_experience",name:"Share my experience",hook:"“I hit this too — here's what happened.” Builds trust through a real story.",icon:"MessageSquare",color:"text-emerald-500",promptHint:"Relate a short, real experience from building Testivo solo that connects to their tweet. Be specific and honest (including the messy part). Use ONLY facts the founder provides in his rough idea — never invent numbers or events. If he gave no detail, keep it general and clearly true."},{id:"ask_question",name:"Curious question",hook:"A real follow-up question. Cheap, friendly, and often starts a conversation.",icon:"HelpCircle",color:"text-amber-500",promptHint:"Ask ONE genuine, specific follow-up question about their tweet — the kind that shows you actually read it and invites them to say more. Not 'thoughts?', not a setup for a pitch."},{id:"supportive",name:"Genuine support",hook:"Short, warm, human. Won't go viral but quietly builds the relationship.",icon:"Heart",color:"text-rose-500",caveat:"Use sparingly and only when you mean it. A feed full of 'love this!' reads as empty. Pair it with a real detail so it lands.",promptHint:"Write a short, sincere, human reaction that references a SPECIFIC detail of their tweet so it doesn't read as generic praise. No emojis required, no pitch, no call to action."},{id:"soft_relevant",name:"Natural mention",hook:"Only when they're literally talking about testimonials/social proof. Helpful first, product as a quiet aside.",icon:"Sparkles",color:"text-fuchsia-500",caveat:"DANGER ZONE — easy to look like a shill. Only use when Testivo genuinely answers their exact need. Help first; the product is one short, honest aside at the end (or skip it).",promptHint:"They're discussing testimonials, social proof, reviews, or 'wall of love' style marketing. Lead with real help on their actual question. Only IF it's a natural fit, add ONE short, honest aside that Testivo does this — framed as 'I'm building X' not 'you should buy X'. It is completely fine to omit the mention entirely if it would feel forced."}],ee=Object.fromEntries(Y.map(t=>[t.id,t])),se=[{label:"Just got first paying customer",query:'"first paying customer" (landing page OR testimonials OR launch) lang:en -filter:replies min_faves:2',why:"The #1 buying trigger — they just earned proof and now need it on the page."},{label:"Asking how to collect testimonials",query:'("how do I" OR "best way to") ("collect testimonials" OR "get testimonials" OR "ask for testimonials") lang:en',why:"Literally asking the problem Testivo solves. Warmest possible reply."},{label:"Roast my landing page",query:'"roast my landing page" lang:en -filter:links',why:"Olly's exact lane. Give real CRO help; weak social proof is often the fix."},{label:"Launching this week",query:'("just launched" OR "launching this week" OR "launch day") ("landing page" OR "my site") lang:en -filter:replies',why:"Going live and attentive — a helpful reply lands when they need proof fast."},{label:"Landing page not converting",query:'("my landing page" OR "our landing page") (convert OR conversion OR "no signups" OR "not converting") lang:en 
1-filter:replies',why:"Trust/credibility pain — social proof is a real, honest fix to talk about."},{label:"Testivo Wall / social proof wanted",query:'("wall of love" OR "social proof") (need OR add OR building OR "looking for") lang:en -filter:links',why:"They already believe in social proof and want to add it."},{label:"Competitor complaints (warmest)",query:'(testimonial.to OR famewall OR senja OR shoutout OR trustmary) (alternative OR "too expensive" OR pricing OR cheaper OR switch) lang:en',why:"Already sold on the category, hit a wall. Highest intent — never trash a rival."},{label:"Just wrapped a client / cohort",query:'("just finished" OR "just wrapped") ("a client" OR "the cohort" OR "a project") (testimonial OR feedback OR review) lang:en',why:"Best moment to ask for a testimonial — you can help them capture it."},{label:"Course / sales page proof",query:'("launching my course" OR "new cohort" OR "sales page") (testimonials OR "social proof" OR proof) lang:en -filter:replies',why:"Course/info-product sales pages live or die on student results."},{label:"Got praise, nowhere to put it",query:'("look what" OR "a customer said" OR "client just said") (😭 OR 🥹 OR ❤️) lang:en -filter:links min_faves:3',why:"They have proof in a DM/screenshot but no wall to display it on."},{label:"#buildinpublic proof talk",query:'#buildinpublic ("landing page" OR testimonials OR "social proof" OR "first customer") lang:en -filter:replies',why:"Your core crowd — the build-in-public founders Olly grew inside of."},{label:"Freelancer / agency asking",query:'(freelancer OR agency OR consultant) ("ask for" OR collecting OR "how to get") testimonials lang:en',why:"Asking clients feels awkward to them — you can make the ask easy."}],te="A bootstrapped solo founder or creator selling on a landing page, building in public, who just earned (or is about to need) proof from real customers — but finds asking for and showing testimonials awkward, manual, and ugly.",ae=[{id:"indie_saas",name:"Indie / bootstrapped SaaS",who:"Solo or 2-person SaaS makers, $0–10k MRR, ship in public on X.",pain:"Landing page has no proof; no clean way to collect a quote/video and embed a good-looking wall.",trigger:"First paying customers, a launch week, or a landing-page redesign."},{id:"course_infoproduct",name:"Course / info-product seller",who:"Sells courses, cohorts, ebooks, templates via Gumroad/Teachable/Lemon Squeezy.",pain:"Student praise scattered across DMs & screenshots; sales page needs outcome proof near the buy button.",trigger:"Launching/relaunching a course; first cohort just finished happy."},{id:"coach_consultant",name:"Coach / consultant",who:"Coaches, consultants, fractional execs selling high-trust services.",pain:"Asking clients for a testimonial feels awkward; needs a 2-minute ask + a credible wall.",trigger:"Just wrapped a happy engagement; raising prices; new program."},{id:"freelancer_agency",name:"Freelancer / small agency",who:"Solo freelancers and 2–10 person studios (design, dev, marketing, video).",pain:"Testimonials trapped in Slack/email; portfolio looks unproven next to bigger rivals.",trigger:"Just delivered a project; revamping the portfolio; pitching bigger clients."},{id:"newsletter_creator",name:"Newsletter / audience creator",who:"Runs a paid/free newsletter; monetizes via subs, sponsors, products.",pain:"Reader praise buried in replies; needs a 'loved by N readers' wall for sponsors/subs.",trigger:"Launching a paid tier or sponsor kit; a subscriber milestone."},{id:"competitor_switcher",name:"Competitor switcher (warmest)",who:"Already using/evaluating Testimonial.to, Famewall, Senja, Shoutout, etc.",pain:"Hit a price/feature/UX wall; wants cheaper/cleaner/easier.",trigger:"Complaining about their tool's price/limits or asking for an alternative."}],re=["Big companies / enterprises (have procurement & in-house tooling)","Mega-creators / celebs (>~200k followers) — wrong altitude for a small founder's DM","Pure B2C / e-commerce needing a reviews platform (Yotpo/Trustpilot territory)","Crypto / NFT / forex / dropshipping / MLM / get-rich-quick accounts","Engagement-farm / bot / reply-spam accounts (generic bio, f4f, link-in-bio spam)","Vulnerable / sensitive niches (medical, consumer financial advice)",'People with no product or offer yet ("aspiring", "wantrepreneur", idea-stage)',"Anyone who already said no or is hostile to tools/pitches"],ne={Lightbulb:L,Plus:J,MessageSquare:X,HelpCircle:U,Heart:P,Sparkles:q},j={lead:{label:"Lead",cls:"bg-slate-100 text-slate-700"},drafted:{label:"Drafted",cls:"bg-violet-100 text-violet-700"},sent:{label:"Sent",cls:"bg-sky-100 text-sky-700"},replied:{label:"They replied",cls:"bg-emerald-100 text-emerald-700"},followup:{label:"Follow up",cls:"bg-amber-100 text-amber-700"},converted:{label:"Converted 🎉",cls:"bg-green-100 text-green-700"},skip:{label:"Skip",cls:"bg-rose-50 text-rose-600"}},E=["lead","drafted","sent","replied","followup","converted","skip"];function k(t){return t.trim().replace(/^@/,"").replace(/^https?:\/\/(x|twitter)\.com\//i,"").split(/[/?]/)[0]}async function S(){const{data:{session:t}}=await b.auth.getSession();if(!t?.access_token)throw new Error("Not signed in");return{"Content-Type":"application/json",Authorization:`Bearer ${t.access_token}`}}function be(){const{user:t}=W(),[n,a]=m.useState("reply"),[c,r]=m.useState([]),[g,h]=m.useState(!0),p=()=>{t&&(h(!0),b.from("outreach_targets").select("id,handle,display_name,kind,source_url,source_text,status,draft,notes,updated_at").eq("user_id",t.id).order("updated_at",{ascending:!1}).limit(300).then(({data:s,error:l})=>{l?u.error("Couldn't load list: "+l.message):r(s??[]),h(!1)}))};m.useEffect(p,[t]);const i=[{id:"reply",label:"Reply composer",icon:B},{id:"find",label:"Find leads",icon:A},{id:"list",label:`My list${c.length?` (${c.length})`:""}`,icon:G}];return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3 text-xs text-amber-900",children:[e.jsx(K,{className:"mt-0.5 h-4 w-4 shrink-0"}),e.jsxs("p",{children:[e.jsx("strong",{children:"Play the long game."})," This drafts replies & DMs in your voice — you send every one ",e.jsx("strong",{children:"manually"}),", one at a time. Help first, sell almost never. The same message blasted to everyone is spam and gets you banned. Real, useful replies are how Olly grew Senja."]})]}),e.jsx("div",{className:"flex gap-1 rounded-lg border bg-card p-1",children:i.map(s=>e.jsxs("button",{onClick:()=>
1a(s.id),className:`inline-flex flex-1 items-center justify-center gap-1.5 rounded-md px-3 py-2 text-sm transition ${n===s.id?"bg-primary/10 font-medium text-primary":"text-muted-foreground hover:text-foreground"}`,children:[e.jsx(s.icon,{className:"h-4 w-4"}),s.label]},s.id))}),n==="reply"&&e.jsx(oe,{onSaved:p}),n==="find"&&e.jsx(ie,{onSaved:p,leads:c.filter(s=>s.status==="lead")}),n==="list"&&e.jsx(le,{targets:c,loading:g,onChanged:p})]})}function oe({onSaved:t}){const[n,a]=m.useState(""),[c,r]=m.useState(""),[g,h]=m.useState(""),[p,i]=m.useState("helpful_answer"),[s,l]=m.useState(!1),[y,d]=m.useState([]),f=ee[p],N=async()=>{if(!c.trim()){u.error("Paste the tweet you're replying to first.");return}l(!0),d([]);try{const o=await fetch("/api/outreach/reply",{method:"POST",headers:await S(),body:JSON.stringify({archetype:p,original_tweet:c,author_handle:k(n),raw_idea:g})}),x=await o.json();if(!o.ok)throw x.error==="ai_unavailable_set_ANTHROPIC_API_KEY"?new Error("AI key not set on the Worker yet (ANTHROPIC_API_KEY)."):new Error(x.error??"generation_failed");d(x.drafts??[]),x.drafts?.length||u.error("No drafts came back — try again.")}catch(o){u.error(o instanceof Error?o.message:"Generation failed")}finally{l(!1)}},w=async o=>{const x=k(n);if(!x){u.error("Add their @handle so it can be saved to your list.");return}const{data:{session:_}}=await b.auth.getSession(),O=_?.user?.id;if(!O)return;const{error:T}=await b.from("outreach_targets").upsert({user_id:O,handle:x,kind:"reply",source_url:n.includes("/status/")?n:"",source_text:c.slice(0,2e3),draft:o,status:"drafted"},{onConflict:"user_id,handle,source_url"});T?u.error("Couldn't save: "+T.message):(u.success(`Saved a draft for @${x} to your list`),t())};return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-3 rounded-xl border bg-card p-4",children:[e.jsxs("div",{className:"grid gap-3 sm:grid-cols-[1fr_2fr]",children:[e.jsxs("label",{className:"block text-sm",children:[e.jsx("span",{className:"mb-1 block font-medium",children:"Their @handle / tweet link"}),e.jsx("input",{value:n,onChange:o=>a(o.target.value),placeholder:"@founder or x.com/.../status/123",className:"w-full rounded-md border bg-background px-3 py-2 text-sm"})]}),e.jsxs("label",{className:"block text-sm",children:[e.jsxs("span",{className:"mb-1 block font-medium",children:["Their tweet ",e.jsx("span",{className:"text-muted-foreground",children:"(paste exactly)"})]}),e.jsx("textarea",{value:c,onChange:o=>r(o.target.value),rows:2,placeholder:"Paste the tweet you want to reply to…",className:"w-full resize-y rounded-md border bg-background px-3 py-2 text-sm"})]})]}),e.jsxs("label",{className:"block text-sm",children:[e.jsxs("span",{className:"mb-1 block font-medium",children:["Your rough idea"," ",e.jsx("span",{className:"font-normal text-muted-foreground",children:"— optional, in any English. I'll fix it."})]}),e.jsx("textarea",{value:g,onChange:o=>h(o.target.value),rows:2,placeholder:"e.g. i want say testimonial help me get trust, my customer say good thing",className:"w-full resize-y rounded-md border bg-background px-3 py-2 text-sm"})]}),e.jsxs("div",{children:[e.jsx("span",{className:"mb-2 block text-sm font-medium",children:"Pick a reply move"}),e.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:Y.map(o=>{const x=ne[o.icon]??L,_=p===o.id;return e.jsxs("button",{onClick:()=>i(o.id),className:`flex items-start gap-2.5 rounded-lg border p-2.5 text-left transition ${_?"border-primary bg-primary/5":"hover:bg-accent"}`,children:[e.jsx(x,{className:`mt-0.5 h-4 w-4 shrink-0 ${o.color}`}),e.jsxs("span",{children:[e.jsx("span",{className:"block text-sm font-medium",children:o.name}),e.jsx("span",{className:"block text-xs text-muted-foreground",children:o.hook})]})]},o.id)})}),f.caveat&&e.jsxs("p",{className:"mt-2 rounded-md bg-amber-50 px-2.5 py-1.5 text-xs text-amber-800",children:["⚠ ",f.caveat]})]}),e.jsxs("button",{onClick:N,disabled:s,className:"inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-60",children:[s?e.jsx(v,{className:"h-4 w-4 animate-spin"}):e.jsx(q,{className:"h-4 w-4"}),s?"Writing 3 replies…":"Write 3 replies"]})]}),y.length>0&&e.jsx("ul",{className:"space-y-2",children:y.map((o,x)=>e.jsx(F,{text:o,onSave:()=>w(o),saveLabel:"Save to list"},x))})]})}function ie({onSaved:t,leads:n}){const[a,c]=m.useState(""),[r,g]=m.useState(""),[h,p]=m.useState(!1),[i,s]=m.useState(null),l=async()=>
1{if(!r.trim()){u.error("Paste their bio or a tweet first.");return}p(!0),s(null);try{const d=await fetch("/api/outreach/qualify",{method:"POST",headers:await S(),body:JSON.stringify({bio_or_tweet:r,handle:k(a)})}),f=await d.json();if(!d.ok)throw f.error==="ai_unavailable_set_ANTHROPIC_API_KEY"?new Error("AI key not set on the Worker yet (ANTHROPIC_API_KEY)."):new Error(f.error??"qualify_failed");s(f)}catch(d){u.error(d instanceof Error?d.message:"Check failed")}finally{p(!1)}},y=async d=>{const f=k(a);if(!f){u.error("Add their @handle to save them.");return}const{data:{session:N}}=await b.auth.getSession(),w=N?.user?.id;if(!w)return;const{error:o}=await b.from("outreach_targets").upsert({user_id:w,handle:f,kind:d?"friend":"dm",source_text:r.slice(0,2e3),draft:i?.opening_message||null,notes:i?`${i.reason}${i.angle?` · Angle: ${i.angle}`:""}`:null,status:"lead"},{onConflict:"user_id,handle,source_url"});o?u.error("Couldn't save: "+o.message):(u.success(`Added @${f} to your list`),t())};return e.jsxs("div",{className:"space-y-4",children:[e.jsx(ce,{}),n.length>0&&e.jsxs("div",{className:"rounded-xl border bg-card p-4",children:[e.jsxs("div",{className:"mb-1 flex items-center gap-2",children:[e.jsx(z,{className:"h-4 w-4 text-violet-500"}),e.jsxs("h3",{className:"text-sm font-semibold",children:["New leads to review (",n.length,")"]})]}),e.jsx("p",{className:"mb-3 text-xs text-muted-foreground",children:"People the scraper found, with their X bio. Check the fit, then keep or skip — one at a time, like a human."}),e.jsx("ul",{className:"space-y-2",children:n.map(d=>e.jsx(de,{lead:d,onChanged:t},d.id))})]}),e.jsxs("div",{className:"rounded-xl border bg-card p-4",children:[e.jsx("h3",{className:"mb-1 text-sm font-semibold",children:"Where to find people"}),e.jsx("p",{className:"mb-3 text-xs text-muted-foreground",children:"Each button opens an X search in a new tab. Read the conversations, then come back and paste a good person's bio/tweet below to check the fit."}),e.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:se.map(d=>e.jsxs("a",{href:`https://x.com/search?q=${encodeURIComponent(d.query)}&f=live`,target:"_blank",rel:"noopener noreferrer",className:"group flex items-start gap-2 rounded-lg border p-2.5 transition hover:bg-accent",children:[e.jsx(A,{className:"mt-0.5 h-4 w-4 shrink-0 text-sky-500"}),e.jsxs("span",{children:[e.jsxs("span",{className:"flex items-center gap-1 text-sm font-medium",children:[d.label,e.jsx(C,{className:"h-3 w-3 opacity-0 transition group-hover:opacity-60"})]}),e.jsx("span",{className:"block text-xs text-muted-foreground",children:d.why})]})]},d.label))})]}),e.jsxs("div",{className:"space-y-3 rounded-xl border bg-card p-4",children:[e.jsx("h3",{className:"text-sm font-semibold",children:"Is this a good person to reach out to?"}),e.jsx("input",{value:a,onChange:d=>c(d.target.value),placeholder:"@handle",className:"w-full rounded-md border bg-background px-3 py-2 text-sm"}),e.jsx("textarea",{value:r,onChange:d=>g(d.target.value),rows:3,placeholder:"Paste their bio or a recent tweet…",className:"w-full resize-y rounded-md border bg-background px-3 py-2 text-sm"}),e.jsxs("button",{onClick:l,disabled:h,className:"inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-60",children:[h?e.jsx(v,{className:"h-4 w-4 animate-spin"}):e.jsx(H,{className:"h-4 w-4"}),h?"Checking…":"Check this person"]}),i&&e.jsxs("div",{className:"space-y-3 rounded-lg border bg-background p-3",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx("span",{className:`rounded-full px-2 py-0.5 text-xs font-medium ${i.warmth==="skip"?"bg-rose-100 text-rose-700":i.warmth==="ok_to_dm"?"bg-emerald-100 text-emerald-700":"bg-sky-100 text-sky-700"}`,children:i.warmth==="skip"?"Skip":i.warmth==="ok_to_dm"?"OK to DM":"Engage in public first"}),e.jsx("span",{className:"text-muted-foreground",children:i.reason})]}),i.angle&&e.jsxs("p",{className:"text-xs text-muted-foreground",children:[e.jsx("strong",{children:"Talk about:"})," ",i.angle]}),i.opening_message&&e.jsx(F,{text:i.opening_message,onSave:()=>y(i.warmth!=="ok_to_dm"),saveLabel:"Save to list"}),i.warmth!=="skip"&&e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:()=>y(!1),className:"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs hover:bg-accent",children:[e.jsx($,{className:"h-3.5 w-3.5"})," Save as DM lead"]}),e.jsxs("button",{onClick:()=>y(!0),className:"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs hover:bg-accent",children:[e.jsx(P,{className:"h-3.5 w-3.5"})," Save as friend"]})]})]})]})]})}function le({targets:t,loading:n,onChanged:a}){const[c,r]=m.useState("all"),g=m.useMemo(()=>{const s={};for(const l of t)s[l.status]=(s[l.status]??0)+1;return s},[t]),h=c==="all"?t:t.filter(s=>s.status===c),p=async(s,l)=>{const{error:y}=await b.from("outreach_targets").update({status:l}).eq("id",s);y?u.error(y.message):a()},i=async s=>{const{error:l}=await b.from("outreach_targets").delete().eq("id",s);l?u.error(l.message):(u.success("Removed"),a())};return n?e.jsx("div",{className:"grid place-items-center py-12 text-sm text-muted-foreground",children:e.jsx(v,{className:"h-5 w-5 animate-spin"})}):t.length===0?e.jsxs("div",{className:"rounded-xl border border-dashed bg-card p-8 text-center text-sm text-muted-foreground",children:["No one on your list yet. Use ",e.jsx("strong",{children:"Reply composer"})," or ",e.jsx("strong",{children:"Find leads"})," ","to add people, then track them here."]}):e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[e.jsxs(I,{active:c==="all",onClick:()=>r("all"),children:["All (",t.length,")"]}),E.filter(s=>g[s]).map(s=>e.jsxs(I,{active:c===s,onClick:()=>r(s),children:[j[s].label," (",g[s],")"]},s))]}),e.jsx("ul",{className:"space-y-2",children:h.map(s=>e.jsxs("li",{className:"rounded-lg border bg-card p-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("a",{href:s.source_url||`https://x.com/${s.handle}`,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm font-medium hover:underline",children:["@",s.handle,e.jsx(C,{className:"h-3 w-3 opacity-50"})]}
1),e.jsx("span",{className:"ml-2 rounded bg-muted px-1.5 py-0.5 text-[10px] uppercase text-muted-foreground",children:s.kind})]}),e.jsx("span",{className:`shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium ${j[s.status].cls}`,children:j[s.status].label})]}),s.source_text&&e.jsxs("p",{className:"mt-1.5 line-clamp-2 text-xs text-muted-foreground",children:["“",s.source_text,"”"]}),s.draft&&e.jsxs("div",{className:"mt-2 rounded-md bg-background p-2",children:[e.jsx("p",{className:"whitespace-pre-wrap text-sm",children:s.draft}),e.jsx(me,{text:s.draft})]}),s.notes&&e.jsxs("p",{className:"mt-1.5 text-xs text-muted-foreground",children:["📝 ",s.notes]}),e.jsxs("div",{className:"mt-2.5 flex flex-wrap items-center gap-1.5",children:[e.jsx("select",{value:s.status,onChange:l=>p(s.id,l.target.value),className:"rounded-md border bg-background px-2 py-1 text-xs",children:E.map(l=>e.jsx("option",{value:l,children:j[l].label},l))}),e.jsxs("button",{onClick:()=>i(s.id),className:"ml-auto inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs text-rose-600 hover:bg-rose-50",children:[e.jsx(Q,{className:"h-3.5 w-3.5"})," Remove"]})]})]},s.id))})]})}function ce(){const[t,n]=m.useState(!1);return e.jsxs("div",{className:"rounded-xl border bg-card p-4",children:[e.jsxs("button",{onClick:()=>n(a=>!a),className:"flex w-full items-start gap-2 text-left",children:[e.jsx(V,{className:"mt-0.5 h-4 w-4 shrink-0 text-emerald-500"}),e.jsxs("span",{className:"flex-1",children:[e.jsxs("span",{className:"flex items-center gap-1 text-sm font-semibold",children:["Who we target",e.jsx(Z,{className:`h-3.5 w-3.5 transition ${t?"rotate-180":""}`})]}),e.jsx("span",{className:"mt-0.5 block text-xs text-muted-foreground",children:te})]})]}),t&&e.jsxs("div",{className:"mt-3 space-y-3 border-t pt-3",children:[e.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:ae.map(a=>e.jsxs("div",{className:"rounded-lg border p-2.5",children:[e.jsx("p",{className:"text-sm font-medium",children:a.name}),e.jsx("p",{className:"mt-0.5 text-xs text-muted-foreground",children:a.who}),e.jsxs("p",{className:"mt-1 text-xs",children:[e.jsx("span",{className:"text-emerald-700",children:"Buy when:"})," ",e.jsx("span",{className:"text-muted-foreground",children:a.trigger})]})]},a.id))}),e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 text-xs font-medium text-rose-700",children:"Skip these:"}),e.jsx("ul",{className:"space-y-0.5",children:re.map((a,c)=>e.jsxs("li",{className:"flex items-start gap-1.5 text-xs text-muted-foreground",children:[e.jsx(D,{className:"mt-0.5 h-3 w-3 shrink-0 text-rose-400"}),a]},c))})]})]})]})}function de({lead:t,onChanged:n}){const[a,c]=m.useState(!1),[r,g]=m.useState(null),h=async()=>{c(!0);try{const s=await fetch("/api/outreach/qualify",{method:"POST",headers:await S(),body:JSON.stringify({bio_or_tweet:t.source_text??"",handle:t.handle})}),l=await s.json();if(!s.ok)throw l.error==="ai_unavailable_set_ANTHROPIC_API_KEY"?new Error("AI key not set on the Worker yet (ANTHROPIC_API_KEY)."):new Error(l.error??"qualify_failed");g(l)}catch(s){u.error(s instanceof Error?s.message:"Check failed")}finally{c(!1)}},p=async()=>{const{error:s}=await b.from("outreach_targets").update({kind:"dm",status:"drafted",draft:r?.opening_message||t.draft||null,notes:r?`${r.reason}${r.angle?` · Angle: ${r.angle}`:""}`:t.notes}).eq("id",t.id);s?u.error(s.message):(u.success(`Kept @${t.handle} — see My list`),n())},i=async()=>{const{error:s}=await b.from("outreach_targets").update({status:"skip"}).eq("id",t.id);s?u.error(s.message):n()};return e.jsxs("li",{className:"rounded-lg border bg-background p-3",children:[e.jsx("div",{className:"flex items-center gap-2",children:e.jsxs("a",{href:t.source_url||`https://x.com/${t.handle}`,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1 text-sm font-medium hover:underline",children:["@",t.handle,e.jsx(C,{className:"h-3 w-3 opacity-50"})]})}),t.source_text&&e.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:t.source_text}),r&&e.jsxs("div",{className:"mt-2 space-y-1.5 rounded-md border bg-card p-2 text-xs",children:[e.jsx("span",{className:`inline-block rounded-full px-2 py-0.5 font-medium ${r.warmth==="skip"?"bg-rose-100 text-rose-700":r.warmth==="ok_to_dm"?"bg-emerald-100 text-emerald-700":"bg-sky-100 text-sky-700"}
1`,children:r.warmth==="skip"?"Skip":r.warmth==="ok_to_dm"?"OK to DM":"Engage in public first"}),e.jsx("span",{className:"ml-1 text-muted-foreground",children:r.reason}),r.opening_message&&e.jsx("p",{className:"mt-1 whitespace-pre-wrap rounded bg-background p-1.5",children:r.opening_message})]}),e.jsxs("div",{className:"mt-2 flex flex-wrap gap-1.5",children:[e.jsxs("button",{onClick:h,disabled:a,className:"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs hover:bg-accent disabled:opacity-60",children:[a?e.jsx(v,{className:"h-3.5 w-3.5 animate-spin"}):e.jsx(H,{className:"h-3.5 w-3.5"}),"Check fit"]}),e.jsxs("button",{onClick:p,className:"inline-flex items-center gap-1 rounded-md bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary hover:bg-primary/20",children:[e.jsx(R,{className:"h-3.5 w-3.5"})," Keep"]}),e.jsxs("button",{onClick:i,className:"ml-auto inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs text-rose-600 hover:bg-rose-50",children:[e.jsx(D,{className:"h-3.5 w-3.5"})," Skip"]})]})]})}function I({active:t,onClick:n,children:a}){return e.jsx("button",{onClick:n,className:`rounded-full border px-2.5 py-1 text-xs transition ${t?"border-primary bg-primary/10 font-medium text-primary":"hover:bg-accent"}`,children:a})}function me({text:t}){const[n,a]=m.useState(!1);return e.jsxs("button",{onClick:async()=>{await navigator.clipboard.writeText(t),a(!0),u.success("Copied — paste it into X"),setTimeout(()=>a(!1),1500)},className:"mt-1.5 inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs hover:bg-accent",children:[n?e.jsx(R,{className:"h-3 w-3 text-emerald-600"}):e.jsx(M,{className:"h-3 w-3"}),"Copy"]})}function F({text:t,onSave:n,saveLabel:a}){const[c,r]=m.useState(!1),g=async()=>{await navigator.clipboard.writeText(t),r(!0),u.success("Copied — paste it into X"),setTimeout(()=>r(!1),1500)},h=t.length>280;return e.jsxs("li",{className:"list-none rounded-lg border bg-background p-3",children:[e.jsx("p",{className:"whitespace-pre-wrap text-sm",children:t}),e.jsxs("div",{className:"mt-2 flex items-center gap-2",children:[e.jsxs("span",{className:`text-[11px] ${h?"text-rose-600":"text-muted-foreground"}`,children:[t.length,"/280"]}),e.jsxs("div",{className:"ml-auto flex gap-1.5",children:[e.jsxs("button",{onClick:g,className:"inline-flex items-center gap-1 rounded-md border px-2.5 py-1 text-xs hover:bg-accent",children:[c?e.jsx(R,{className:"h-3.5 w-3.5 text-emerald-600"}):e.jsx(M,{className:"h-3.5 w-3.5"}),"Copy"]}),e.jsxs("button",{onClick:n,className:"inline-flex items-center gap-1 rounded-md bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary hover:bg-primary/20",children:[e.jsx($,{className:"h-3.5 w-3.5"})," ",a]})]})]})]})}export{be as component};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.