PageSourceSearch

https://knuckl.com/assets/AdminHomeownerLeads-DBOIRVOE.js

js knuckl.com collected 2026-09-24 14:47:50 UTC 16,703 bytes, 9 lines download raw bytes

1import{u as T,a as _,c as b,i as N,j as e}from"./vendor-query-BxlSJxfF.js";import{p as Q,B as x,r as w,s as l,T as O}from"./index-DO0aGFYL.js";import{A as P}from"./AdminPage-Bk0BX7la.js";import"./AdminCard-BSBYnc2T.js";import{A as C}from"./AdminSection-BEK1c5Y8.js";import{A as o}from"./AdminMetric-DUcp41Nj.js";import{A as z,a as H}from"./AdminLoading-DBn5PY9V.js";import{S as R,a as V,b as D,c as G}from"./sheet-KxRpZ2qz.js";import{T as W,a as Z,b as J}from"./tabs-BC396HY3.js";import{t as h}from"./use-toast-DIh7BwqI.js";import{t as X}from"./homeowner-trades-DdI3fK_X.js";import{S as E}from"./switch-CdLpmXwy.js";import{T as Y}from"./textarea-CY9O0h4D.js";import{L as ee}from"./link-2-D-T3A0gX.js";import{G as se}from"./globe-BblBOU8M.js";import{E as ae}from"./external-link-BMHb0t-K.js";import{S as I}from"./send-C6vNENnO.js";import{E as $}from"./eye-off-806MS8FD.js";import{H as te}from"./house-D1lgTCJ7.js";import{C as re}from"./circle-check-BzeG-ygw.js";import{C as L}from"./clock-TDYRjmmL.js";import{U as ne}from"./users-4wDC2oQA.js";import{M as ie}from"./message-square-CLxZQpIa.js";import"./vendor-react-DdNhUK9-.js";import"./vendor-supabase-Csx7YFID.js";import"./index-B9yCq0Ag.js";import"./index-Ce1IEqmK.js";import"./index-Bidgsva9.js";import"./skill-catalog-CiwHrT6-.js";/**
2 * @license lucide-react v0.462.0 - ISC
3 *
4 * This source code is licensed under the ISC license.
5 * See the LICENSE file in the root directory of this source tree.
6 */const de=Q("RotateCw",[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8",key:"1p45f6"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}]]);function le(){const g=T(),[u,q]=_.useState(null),[n,f]=_.useState(null),[t,v]=_.useState(""),[y,k]=_.useState(!1),{data:c}=b({queryKey:["admin-external-feed-health"],refetchInterval:12e4,queryFn:async()=>{const{data:s,error:r}=await l.rpc("admin_external_feed_health",{_days:14});if(r)throw r;return s??[]}}),{data:i}=b({queryKey:["admin-external-stats"],refetchInterval:6e4,queryFn:async()=>{const{data:s,error:r}=await l.rpc("admin_external_lead_stats");if(r)throw r;return s}}),{data:j}=b({queryKey:["admin-external-lead-runs"],refetchInterval:6e4,queryFn:async()=>{const{data:s,error:r}=await l.from("external_lead_runs").select("id, source, started_at, fetched, rejected, duplicates, inserted, error").order("started_at",{ascending:!1}).limit(12);if(r)throw r;return s??[]}}),{data:a,isLoading:d}=b({queryKey:["admin-external-leads",u],queryFn:async()=>{const{data:s,error:r}=await l.rpc("admin_external_leads",{_source:u,_limit:50,_offset:0});if(r)throw r;return s.leads??[]}}),p=()=>{g.invalidateQueries({queryKey:["admin-external-leads"]}),g.invalidateQueries({queryKey:["admin-external-stats"]})},S=N({mutationFn:async({src:s,enabled:r})=>{const{error:m}=await l.rpc("admin_external_source_set_enabled",{_source:s,_enabled:r});if(m)throw m},onSuccess:p,onError:s=>h({title:"Could not change source",description:s.message,variant:"destructive"})}),F=N({mutationFn:async({src:s,urls:r})=>{const{error:m}=await l.rpc("admin_external_source_set_urls",{_source:s,_urls:r});if(m)throw m},onSuccess:()=>{f(null),p(),h({title:"Watched links updated"})},onError:s=>h({title:"Could not save links",description:s.message,variant:"destructive"})}),K=N({mutationFn:async({id:s,hidden:r})=>{const{error:m}=await l.rpc("admin_external_lead_set_hidden",{_id:s,_hidden:r,_reason:r?"Hidden by admin":null});if(m)throw m},onSuccess:p,onError:s=>h({title:"Update failed",description:s.message,variant:"destructive"})}),U=N({mutationFn:async({src:s,name:r,enabled:m})=>{const{error:A}=await l.rpc("admin_external_feed_set_enabled",{_source:s,_name:r,_enabled:m});if(A)throw A},onSuccess:()=>g.invalidateQueries({queryKey:["admin-external-feed-health"]}),onError:s=>h({title:"Could not change feed",description:s.message,variant:"destructive"})}),B=(c??[]).filter(s=>s.kept>0).length,M=y?c??[]:(c??[]).filter(s=>s.kept===0);return e.jsxs(C,{label:"Off-platform leads",description:"Homeowner posts pulled from public sites",children:[e.jsxs("div",{className:"grid grid-cols-4 gap-2 mb-3",children:[e.jsx(o,{label:"Live",value:(i==null?void 0:i.live)??0}),e.jsx(o,{label:"Total",value:(i==null?void 0:i.total)??0}),e.jsx(o,{label:"7 days",value:(i==null?void 0:i.last_7_days)??0}),e.jsx(o,{label:"Hidden",value:(i==null?void 0:i.hidden)??0})]}),e.jsx("div",{className:"space-y-1.5 mb-4",children:((i==null?void 0:i.by_source)??[]).map(s=>e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3 text-sm",children:[e.jsxs("button",{className:`truncate text-left ${u===s.source?"font-semibold":""}`,onClick:()=>q(u===s.source?null:s.source),children:[s.label," ",e.jsxs("span",{className:"text-muted-foreground text-xs",children:[s.live,"/",s.total]})]}),e.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[e.jsx(x,{size:"icon",variant:"ghost",className:"h-7 w-7","aria-label":`Edit watched links for ${s.label}`,onClick:()=>{f(n===s.source?null:s.source),v((s.urls??[]).join(`
7`))},children:e.jsx(ee,{className:"w-3.5 h-3.5"})}),e.jsx(E,{checked:s.enabled,onCheckedChange:r=>S.mutate({src:s.source,enabled:r})})]})]}),n===s.source&&e.jsxs("div",{className:"rounded-lg border border-border/60 p-2 space-y-2",children:[e.jsx(Y,{value:t,onChange:r=>v(r.target.value),rows:5,className:"text-xs font-mono",placeholder:`https://www.facebook.com/groups/example/
8One link per line`}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(x,{size:"sm",variant:"ghost",onClick:()=>f(null),children:"Cancel"}),e.jsx(x,{size:"sm",disabled:F.isPending,onClick:()=>F.mutate({src:s.source,urls:t.split(`
9`).map(r=>
9r.trim()).filter(Boolean)}),children:"Save links"})]})]})]},s.source))}),!!(j!=null&&j.length)&&e.jsxs("div",{className:"mb-4 rounded-lg border border-border/60 p-2.5",children:[e.jsx("div",{className:"text-xs font-semibold mb-1.5",children:"Latest import runs"}),e.jsx("div",{className:"space-y-1",children:j.map(s=>e.jsxs("div",{className:"text-xs flex items-center justify-between gap-2",children:[e.jsxs("span",{className:"truncate",children:[s.source," ",e.jsx("span",{className:"text-muted-foreground",children:w(s.started_at)})]}),e.jsx("span",{className:s.error?"text-destructive truncate":"text-muted-foreground",children:s.error?s.error:`${s.fetched} fetched · ${s.inserted} new · ${s.rejected} rejected · ${s.duplicates} dupes`})]},s.id))})]}),!!(c!=null&&c.length)&&e.jsxs("div",{className:"mb-4 rounded-lg border border-border/60 p-2.5",children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsxs("div",{className:"text-xs font-semibold",children:["City feeds"," ",e.jsxs("span",{className:"text-muted-foreground font-normal",children:[B,"/",c.length," producing · 14 days"]})]}),e.jsx(x,{size:"sm",variant:"ghost",className:"h-6 px-2 text-xs",onClick:()=>k(s=>!s),children:y?"Show quiet only":`Show all (${c.length})`})]}),e.jsx("div",{className:"space-y-1 max-h-72 overflow-y-auto",children:M.map(s=>e.jsxs("div",{className:"text-xs flex items-center justify-between gap-2",children:[e.jsxs("span",{className:"truncate",children:[s.name,e.jsx("span",{className:"text-muted-foreground",children:[s.city,s.state].filter(Boolean).length?` · ${[s.city,s.state].filter(Boolean).join(", ")}`:""})]}),e.jsxs("span",{className:"flex items-center gap-2 shrink-0",children:[e.jsxs("span",{className:s.kept>0?"text-muted-foreground":"text-destructive",children:[s.kept," kept",s.last_kept_at?` · ${w(s.last_kept_at)}`:" · never"]}),e.jsx(E,{checked:!s.disabled,"aria-label":`Toggle ${s.name}`,onCheckedChange:r=>U.mutate({src:s.source,name:s.name,enabled:r})})]})]},`${s.source}:${s.name}`))})]}),d?e.jsx(z,{}):a!=null&&a.length?e.jsx("div",{className:"space-y-2",children:a.map(s=>e.jsxs("div",{className:"rounded-lg border border-border/60 p-2.5 text-sm",children:[e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"font-medium truncate",children:s.title||s.trade||s.source_label||s.source}),e.jsxs("div",{className:"text-xs text-muted-foreground",children:[[s.source_label||s.source,s.city,s.trade].filter(Boolean).join(" · ")," ·"," ",w(s.posted_at),s.score!=null&&` · ${s.score}`]})]}),e.jsxs("div",{className:"flex items-center gap-1 shrink-0",children:[s.source_url&&e.jsx(x,{size:"icon",variant:"ghost",className:"h-7 w-7",onClick:()=>window.open(s.source_url,"_blank","noopener,noreferrer"),children:e.jsx(ae,{className:"w-3.5 h-3.5"})}),e.jsx(x,{size:"icon",variant:"ghost",className:"h-7 w-7",onClick:()=>K.mutate({id:s.id,hidden:!s.hidden}),children:s.hidden?e.jsx(I,{className:"w-3.5 h-3.5"}):e.jsx($,{className:"w-3.5 h-3.5"})})]})]}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1 line-clamp-2",children:s.body})]},s.id))}):e.jsx(H,{Icon:se,title:"No off-platform leads yet."})]})}const oe=[{value:"all",label:"All"},{value:"open",label:"Open"},{value:"pending_verification",label:"Unverified"},{value:"closed",label:"Closed"},{value:"hidden",label:"Hidden"}];function Ue(){var j;const g=T(),[u,q]=_.useState("all"),[n,f]=_.useState(null),{data:t}=b({queryKey:["admin-homeowner-stats"],refetchInterval:6e4,queryFn:async()=>{const{data:a,error:d}=await l.rpc("admin_homeowner_lead_stats");if(d)throw d;return a}}),{data:v,isLoading:y}=b({queryKey:["admin-homeowner-leads",u],queryFn:async()=>{const{data:a,error:d}=await l.rpc("admin_homeowner_leads",{_status:u==="all"?null:u,_limit:100,_offset:0});if(d)throw d;return a.leads??[]}}),k=()=>{g.invalidateQueries({queryKey:["admin-homeowner-leads"]}),g.invalidateQueries({queryKey:["admin-homeowner-stats"]})},c=N({mutationFn:async({id:a,next:d})=>{const{data:p,error:S}=await l.rpc("admin_homeowner_lead_set_status",{_lead_id:a,_status:d,_reason:d==="hidden"?"Hidden by admin":null});if(S)throw S;return p},onSuccess:()=>{h({title:"Lead updated"}),f(null),k()},onError:a=>h({title:"Update failed",description:a.message,variant:"destructive"})}),i=N({mutationFn:async a=>{const{data:d,error:p}=await l.rpc("admin_homeowner_lead_refanout",{_lead_id:a});if(p)throw p;return d},onSuccess:a=>{h({title:`Sent to ${(a==null?void 0:a.notified)??0} pros`}),k()},onError:a=>h({title:"Fan-out failed",description:a.message,variant:"destructive"})});return e.jsxs(P,{title:"Homeowner Leads",subtitle:"Residential demand funnel: intake, verification, reach and replies.",children:[e.jsx(C,{label:"Funnel",children:e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-3",children:[e.jsx(o,{label:"Submitted",value:(t==null?void 0:t.submitted)??"—",Icon:te,hint:`${(t==null?void 0:t.last_7d)??0} in last 7d`}),e.jsx(o,{label:"Verified",value:(t==null?void 0:t.verified)??"—",Icon:re,tone:"good",hint:t?`${t.pending} awaiting code`:void 0}),e.jsx(o,{label:"Open",value:(t==null?void 0:t.open)??"—",Icon:L}),e.jsx(o,{label:"Hidden / closed",value:`${(t==null?void 0:t.hidden)??0} / ${(t==null?void 0:t.closed)??0}`,Icon:$})]})}),e.jsx(C,{label:"Reach & engagement",children:e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-3",children:[e.jsx(o,{label:"Zero-reach open leads",value:(t==null?void 0:t.zero_reach)??"—",Icon:O,tone:((t==null?void 0:t.zero_reach)??0)>0?"warn":"good",hint:"Verified but nobody notified"}),e.jsx(o,{label:"Avg pros notified",value:(t==null?void 0:t.avg_notified)??"—",Icon:ne}),e.jsx(o,{label:"Pro responses",value:(t==null?void 0:t.responses)??"—",Icon:ie,hint:t?`${t.leads_with_response} leads answered`:void 0}),e.jsx(o,{label:"Median time to reply",value:t?`${t.median_hours_to_first_response}h`:"—",Icon:L,hint:t?`${t.homeowner_replies} homeowner replies`:void 0})]})}),e.jsxs(C,{label:"Leads",children:[e.jsx(W,{value:u,onValueChange:q,children:e.jsx("div",{className:"overflow-x-auto -mx-1 px-1",children:e.jsx(Z,{className:"inline-flex w-auto",children:oe.map(a=>e.jsx(J,{value:a.value,className:"min-h-[40px]",children:a.label},a.value))})})}),y?e.jsx(z,{}):!v||v.length===0?e.jsx(H,{title:"No homeowner leads",description:"Nothing matches this filter yet."}):e.jsx("div",{className:"space-y-2",children:v.map(a=>e.jsxs("button",{type:"button",onClick:()=>f(a),className:"w-full text-left rounded-card border border-border/60 bg-card/80 backdrop-blur-xl p-3.5 hover:bg-muted/40 transition-colors",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"font-medium truncate",children:a.trade}),e.jsxs("div",{className:"text-xs text-muted-foreground truncate",children:[[a.city,a.state].filter(
9Boolean).join(", ")||"No location"," · ",X(a.timeline),a.budget_band?` · ${a.budget_band}`:""]})]}),e.jsx("span",{className:"text-caption text-muted-foreground shrink-0",children:w(a.created_at)})]}),e.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-1.5 text-caption",children:[e.jsx("span",{className:"px-2 py-0.5 rounded-full bg-muted capitalize",children:a.status.replace(/_/g," ")}),e.jsxs("span",{className:`px-2 py-0.5 rounded-full ${a.notified_pro_count===0&&a.status==="open"?"bg-amber-500/15 text-amber-600 dark:text-amber-400":"bg-muted"}`,children:[a.notified_pro_count," notified"]}),e.jsxs("span",{className:"px-2 py-0.5 rounded-full bg-muted",children:[a.response_count," replies"]}),!a.verified_at&&e.jsx("span",{className:"px-2 py-0.5 rounded-full bg-muted",children:"unverified"})]})]},a.id))})]}),e.jsx(R,{open:!!n,onOpenChange:a=>!a&&f(null),children:e.jsx(V,{side:"right",className:"w-full sm:max-w-lg overflow-y-auto",children:n&&e.jsxs(e.Fragment,{children:[e.jsx(D,{children:e.jsx(G,{className:"text-left",children:n.trade})}),e.jsxs("div",{className:"mt-4 space-y-4 text-sm",children:[e.jsx("div",{className:"text-muted-foreground whitespace-pre-wrap",children:n.description}),((j=n.photos)==null?void 0:j.length)>0&&e.jsx("div",{className:"grid grid-cols-3 gap-2",children:n.photos.map(a=>e.jsx("img",{src:a,alt:"Homeowner project",className:"rounded-lg object-cover aspect-square",loading:"lazy"},a))}),e.jsxs("dl",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsxs("div",{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Contact"}),e.jsx("dd",{children:n.contact_name})]}),e.jsxs("div",{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Email"}),e.jsx("dd",{className:"truncate",children:n.contact_email??"—"})]}),e.jsxs("div",{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Phone"}),e.jsx("dd",{children:n.contact_phone??"—"})]}),e.jsxs("div",{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Verified"}),e.jsx("dd",{children:n.verified_at?w(n.verified_at):"No"})]}),e.jsxs("div",{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Notified pros"}),e.jsx("dd",{children:n.notified_pro_count})]}),e.jsxs("div",{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Status"}),e.jsx("dd",{className:"capitalize",children:n.status.replace(/_/g," ")})]})]}),e.jsxs("div",{children:[e.jsxs("h3",{className:"text-caption uppercase tracking-[0.08em] text-muted-foreground mb-2",children:["Pro responses (",n.responses.length,")"]}),n.responses.length===0?e.jsx("p",{className:"text-xs text-muted-foreground",children:"No pro has replied yet."}):e.jsx("div",{className:"space-y-2",children:n.responses.map(a=>e.jsxs("div",{className:"rounded-xl border border-border/60 p-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"font-medium text-sm truncate",children:a.pro_name}),e.jsx("span",{className:"text-caption text-muted-foreground",children:w(a.created_at)})]}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1 whitespace-pre-wrap",children:a.intro_message}),e.jsxs("p",{className:"text-caption text-muted-foreground mt-1.5",children:[a.message_count," message",a.message_count===1?"":"s",a.homeowner_replied_at?" · homeowner replied":" · awaiting homeowner"]})]},a.id))})]}),e.jsxs("div",{className:"flex flex-wrap gap-2 pt-2",children:[e.jsxs(x,{size:"sm",variant:"outline",className:"gap-1.5",disabled:i.isPending||n.status!=="open"||!n.verified_at,onClick:()=>i.mutate(n.id),children:[e.jsx(de,{className:"w-3.5 h-3.5"})," Re-send to pros"]}),n.status==="hidden"?e.jsxs(x,{size:"sm",variant:"outline",className:"gap-1.5",onClick:()=>c.mutate({id:n.id,next:"open"}),children:[e.jsx(I,{className:"w-3.5 h-3.5"})," Unhide"]}):e.jsxs(x,{size:"sm",variant:"destructive",className:"gap-1.5",onClick:()=>c.mutate({id:n.id,next:"hidden"}),children:[e.jsx($,{className:"w-3.5 h-3.5"})," Hide"]})]})]})]})})}),e.jsx(le,{})]})}export{Ue as default};

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.