1import{d as J,s as j,Y as V,l as Z,r as u,j as e,I as ie,B as g,aI as oe,m as K,n as $,o as G,p as R,y as de,E as le,T as ce}from"./index-BghHNOje.js";import{B as x}from"./badge-BCCCWQE0.js";import{D as me,a as xe,b as he,c as pe,f as ue}from"./dialog-Clo9IObs.js";import{S as fe}from"./switch-Dlrp5GND.js";import{L as b}from"./loader-circle-B8iw8PN7.js";import{P as ge}from"./plus-BjOZUKUH.js";import{T as je}from"./trash-2-DU-Ld5Bw.js";import{S as ye}from"./settings-f9xlGnz5.js";import{R as we}from"./refresh-cw-ByVvNyh0.js";import{C as ve}from"./chevron-right-C5onmajZ.js";import{R as O,Y,T as H,C as Ne,X as ke}from"./generateCategoricalChart-Cd6uk4K5.js";import{L as W,a as X}from"./LineChart-B7BO-pYg.js";import{M as be}from"./minus-B2ufVPVp.js";import{T as Ce}from"./trending-down-DjxqILDy.js";import"./index-B6H0OFpw.js";import"./index-YvgZ23nC.js";import"./tiny-invariant-BaFNuDhB.js";function ee(){return J({queryKey:["seo-tracked-keywords"],queryFn:async()=>{const{data:i,error:d}=await j.from("seo_tracked_keywords").select("*").order("keyword");if(d)throw d;return i??[]}})}function _e(i=90){return J({queryKey:["seo-rank-snapshots",i],queryFn:async()=>{const d=new Date;d.setUTCDate(d.getUTCDate()-i);const{data:c,error:w}=await j.from("seo_rank_snapshots").select("*").gte("snapshot_date",d.toISOString().split("T")[0]).order("snapshot_date",{ascending:!0});if(w)throw w;return c??[]}})}const De=({open:i,onOpenChange:d})=>{const{data:c=[],isLoading:w}=ee(),E=V(),{toast:C}=Z(),[v,q]=u.useState(""),[_,M]=u.useState(!1),D=()=>E.invalidateQueries({queryKey:["seo-tracked-keywords"]}),S=async()=>{const n=v.trim().toLowerCase();if(!n)return;M(!0);const{error:h}=await j.from("seo_tracked_keywords").insert({keyword:n,page_path:"/deeptech"});M(!1),h?C({title:"Could not add keyword",description:h.message,variant:"destructive"}):(q(""),D())},z=async n=>{const{error:h}=await j.from("seo_tracked_keywords").update({is_active:!n.is_active}).eq("id",n.id);h?C({title:"Update failed",description:h.message,variant:"destructive"}):D()},U=async n=>{if(!confirm(`Delete "${n.keyword}"? Snapshots will also be deleted.`))return;const{error:h}=await j.from("seo_tracked_keywords").delete().eq("id",n.id);h?C({title:"Delete failed",description:h.message,variant:"destructive"}):D()};return e.jsx(me,{open:i,onOpenChange:d,children:e.jsxs(xe,{className:"max-w-2xl",children:[e.jsx(he,{children:e.jsx(pe,{children:"Tracked keywords"})}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(ie,{placeholder:"Add keyword (e.g. deep tech zurich)",value:v,onChange:n=>q(n.target.value),onKeyDown:n=>n.key==="Enter"&&S()}),e.jsxs(g,{onClick:S,disabled:_||!v.trim(),children:[_?e.jsx(b,{className:"h-4 w-4 animate-spin"}):e.jsx(ge,{className:"h-4 w-4"}),"Add"]})]}),e.jsx("div",{className:"max-h-[400px] overflow-y-auto border rounded-md divide-y",children:w?e.jsx("div",{className:"p-6 text-center",children:e.jsx(b,{className:"h-5 w-5 animate-spin mx-auto"})}):c.length===0?e.jsx("div",{className:"p-6 text-center text-sm text-muted-foreground",children:"No keywords yet."}):c.map(n=>e.jsxs("div",{className:"flex items-center justify-between p-3 gap-3",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"font-medium text-sm truncate",children:n.keyword}),e.jsxs("div",{className:"text-xs text-muted-foreground",children:[n.page_path," · ",n.country.toUpperCase()," · target #",n.target_position]})]}),e.jsx(fe,{checked:n.is_active,onCheckedChange:()=>z(n)}),e.jsx(g,{size:"icon",variant:"ghost",onClick:()=>U(n),children:e.jsx(je,{className:"h-4 w-4 text-red-600"})})]},n.id))}),e.jsx(ue,{children:e.jsx(g,{variant:"outline",onClick:()=>d(!1),children:"Close"})})]})})};function Se(i,d){return i==null?d?e.jsx(x,{variant:"outline",className:"text-muted-foreground",children:"Not ranking (0 impressions)"}):e.jsx(x,{variant:"outline",children:"Awaiting first sync"}):i<=3?e.jsx(x,{className:"bg-emerald-600 hover:bg-emerald-600",children:"Top 3"}):i<=10?e.jsx(x,{className:"bg-blue-600 hover:bg-blue-600",children:"Top 10"}):i<=20?e.jsx(x,{className:"bg-amber-500 hover:bg-amber-500",children:"Page 2"}):i<=30?e.jsx(x,{variant:"secondary",children:"11â30"}):e.jsx(x,{variant:"outline",children:"Not ranking"})}function Te(i){return i==null||Math.abs(i)<.5?e.jsx(be,{className:"h-3 w-3 text-muted-foreground"}):i<0?e.jsx(ce,{className:"h-3 w-3 text-emerald-600"}):e.jsx(Ce,{className:"h-3 w-3 text-red-600"})}const Ye=()=>{const{toast:i}=Z(),d=V(),{data:c=[],isLoading:w}=ee(),{data:E=[],isLoading:C}=_e(90),[v,q]=u.useState(!1),[_,M]=u.useState(!1),[D,S]=u.useState(!1),[z,U]=u.useState(!1),[n,h]=u.useState(null),[B,se]=u.useState(new Set),F=u.useMemo(()=>{const s=new Map;for(const t of E){const r=s.get(t.keyword_id)??[];r.push(t),s.set(t.keyword_id,r)}for(const[,t]of s)t.sort((r,o)=>r.snapshot_date.localeCompare(o.snapshot_date));return s},[E]),T=u.useMemo(()=>{let s=0,t=0,r=0,o=0;for(const N of c){const l=F.get(N.id)??[];if(l.length===0)continue;const m=l[l.length-1].position;m!=null&&(m<=3&&s++,m<=10&&t++);const A=new Date(l[l.length-1].snapshot_date),L=new Date(A);L.setUTCDate(L.getUTCDate()-7);const k=[...l].reverse().find(y=>new Date(y.snapshot_date)<=L);if(k&&m!=null&&k.position!=null){const y=m-k.position;y<-.5?r++:y>.5&&o++}}return{top3:s,top10:t,improved:r,declined:o,total:c.filter(N=>N.is_active).length}},[c,F]),I=async(s,t,r)=>{r(!0);const{error:o}=await j.functions.invoke("seo-rank-tracker",{body:{days:s}});r(!1),o?i({title:`${t} failed`,description:o.message,variant:"destructive"}):(i({title:`${t} complete`,description:`Pulled last ${s} days of GSC data.`}),d.invalidateQueries({queryKey:["seo-rank-snapshots"]}))},te=async()=>{U(!0);const{data:s,error:t}=await j.functions.invoke("seo-rank-tracker",{body:{mode:"discover",days:90}});if(U(!1),t){i({title:"Discovery failed",description:t.message,variant:"destructive"});return}h((s==null?void 0:s.suggestions)??[])},ae=async s=>{const{error:t}=await j.from("seo_tracked_keywords").insert({keyword:s,page_path:"/"});if(t){i({title:"Could not add keyword",description:t.message,variant:"destructive"});return}h(r=>(r??[]).filter(o=>o.keyword!==s)),d.invalidateQueries({queryKey:["seo-tracked-keywords"]}),i({title:"Keyword added",description:`Now tracking "${s}". Run a backfill to pull history.`})},re=s=>{const t=new Set(B);t.has(s)?t.delete(s):t.add(s),se(t)},f=u.useMemo(()=>{let s=0;for(const t of c){const r=t.last_synced_at?new Date(t.last_synced_at).getTime():0;r>s&&(s=r)}return s?new Date(s):null},[c]),P=!!f&&Date.now()-f.getTime()<1e3*60*60*48,ne=!!f&&!P;return w||C?e.jsx("div",{className:"flex items-center justify-center py-12",children:e.jsx(b,{className:"h-8 w-8 animate-spin text-primary"})}):e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex flex-wrap gap-2 items-center",children:[e.jsxs(x,{variant:"outline",children:[T.total," keywords tracked"]}),e.jsxs(x,{className:"bg-emerald-600 hover:bg-emerald-600",children:[T.top3," in Top 3"]}),e.jsxs(x,{className:"bg-blue-600 hover:bg-blue-600",children:[T.top10," in Top 10"]}),e.jsxs(x,{variant:"outline",className:"text-emerald-700 border-emerald-300",children:["â ",T.improved," improved (7d)"]}),e.jsxs(x,{variant:"outline",className:"text-red-700 border-red-300",children:["â ",T.declined," declined (7d)"]}),f&&e.jsxs("span",{className:"text-xs text-muted-foreground ml-1",children:["Last synced ",f.toLocaleStr
1ing()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(g,{variant:"outline",size:"sm",onClick:te,disabled:z,children:[z?e.jsx(b,{className:"h-4 w-4 animate-spin mr-2"}):e.jsx(oe,{className:"h-4 w-4 mr-2"}),"Discover keywords"]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:()=>S(!0),children:[e.jsx(ye,{className:"h-4 w-4 mr-2"})," Edit keywords"]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:()=>I(7,"Refresh",q),disabled:v,children:[v?e.jsx(b,{className:"h-4 w-4 animate-spin mr-2"}):e.jsx(we,{className:"h-4 w-4 mr-2"}),"Refresh (7d)"]}),e.jsxs(g,{variant:"outline",size:"sm",onClick:()=>I(90,"Backfill",M),disabled:_,children:[_?e.jsx(b,{className:"h-4 w-4 animate-spin mr-2"}):null,"Backfill 90d"]})]})]}),n&&e.jsxs(K,{children:[e.jsx($,{children:e.jsx(G,{className:"text-base",children:"Queries you already rank for (last 90 days)"})}),e.jsx(R,{children:n.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Search Console reports no untracked queries with impressions in this period."}):e.jsx("div",{className:"divide-y",children:n.map(s=>e.jsxs("div",{className:"flex items-center justify-between gap-3 py-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-medium truncate",children:s.keyword}),e.jsxs("div",{className:"text-xs text-muted-foreground",children:[s.impressions.toLocaleString()," impressions · ",s.clicks," clicks · avg. position ",s.position.toFixed(1)]})]}),e.jsx(g,{size:"sm",variant:"outline",onClick:()=>ae(s.keyword),children:"Track"})]},s.keyword))})})]}),ne&&e.jsx(K,{className:"border-amber-300",children:e.jsxs(R,{className:"py-4 text-sm text-amber-800",children:["The last successful sync was ",f==null?void 0:f.toLocaleString(),' â more than 48 hours ago. The nightly job may not be running; use "Refresh (7d)" to sync now.']})}),c.length>0&&e.jsx(K,{children:e.jsx(R,{className:"py-4 text-sm text-muted-foreground",children:'Search Console can only report keywords the site already appears for. A keyword marked "Not ranking (0 impressions)" synced fine â Google simply never showed the site for it yet. Use "Discover keywords" to add terms that already bring in impressions.'})}),c.length===0?e.jsx(K,{children:e.jsx(R,{className:"py-12 text-center text-muted-foreground",children:'No keywords tracked yet. Add some with "Edit keywords".'})}):e.jsxs(K,{children:[e.jsx($,{children:e.jsx(G,{className:"text-base",children:"Keyword rankings (Google Switzerland)"})}),e.jsx(R,{className:"p-0",children:e.jsx("div",{className:"divide-y",children:c.map(s=>{const t=F.get(s.id)??[],r=t.length?t[t.length-1]:null,o=(r==null?void 0:r.position)??null,N=r?new Date(r.snapshot_date):null;let l=null;if(N&&o!=null){const a=new Date(N);a.setUTCDate(a.getUTCDate()-7);const p=[...t].reverse().find(Q=>new Date(Q.snapshot_date)<=a&&Q.position!=null);(p==null?void 0:p.position)!=null&&(l=o-p.position)}const m=t.slice(-7),A=m.length?Math.round(m.reduce((a,p)=>a+p.impressions,0)/m.length):0,L=m.length?m.reduce((a,p)=>a+p.clicks,0)/m.length:0,k=t.slice(-30).map(a=>({date:a.snapshot_date.slice(5),position:a.position??null,impressions:a.impressions,clicks:a.clicks})),y=B.has(s.id);return e.jsxs("div",{className:s.is_active?"":"opacity-50",children:[e.jsxs("div",{className:"p-4 grid grid-cols-12 gap-3 items-center",children:[e.jsxs("button",{onClick:()=>re(s.id),className:"col-span-12 md:col-span-4 flex items-start gap-2 text-left",children:[y?e.jsx(de,{className:"h-4 w-4 mt-1 shrink-0"}):e.jsx(ve,{className:"h-4 w-4 mt-1 shrink-0"}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"font-medium text-sm",children:s.keyword}),e.jsx("div",{className:"text-xs text-muted-foreground truncate",children:r!=null&&r.top_page?r.top_page.replace(/^https?:\/\/[^/]+/,""):"â"})]})]}),e.jsxs("div",{className:"col-span-3 md:col-span-2",children:[e.jsxs("div",{className:"flex items-baseline gap-1",children:[e.jsx("span",{className:"text-2xl font-bold",children:o!=null?o.toFixed(1):"â"}),l!=null&&e.jsxs("span",{className:`text-xs flex items-center gap-0.5 ${l<-.5?"text-emerald-600":l>.5?"text-red-600":"text-muted-foreground"}`,children:[Te(l),l<0?"+":"",(-l).toFixed(1)]})]}),e.jsx("div",{className:"mt-1",children:Se(o,!!s.last_synced_at)})]}),e.jsx("div",{className:"col-span-5 md:col-span-3",children:k.length>1?e.jsx(O,{width:"100%",height:48,children:e.jsxs(W,{data:k,margin:{top:2,right:2,left:2,bottom:2},children:[e.jsx(Y,{reversed:!0,domain:["dataMin - 1","dataMax + 1"],hide:!0}),e.jsx(H,{contentStyle:{fontSize:11,padding:"4px 8px"},formatter:a=>a==null?void 0:a.toFixed(1),labelFormatter:a=>a}),e.jsx(X,{type:"monotone",dataKey:"position",stroke:"#1E5CC8",strokeWidth:2,dot:!1,connectNulls:!0})]})}):e.jsx("span",{className:"text-xs text-muted-foreground italic",children:P?"No GSC traffic in last 90d":"No history yet"})}),e.jsxs("div",{className:"col-span-4 md:col-span-2 text-xs text-muted-foreground",children:[e.jsxs("div",{children:[A.toLocaleStr
1ing()," impr/day"]}),e.jsxs("div",{children:[L.toFixed(1)," clicks/day"]})]}),e.jsx("div",{className:"col-span-12 md:col-span-1 flex justify-end",children:(r==null?void 0:r.top_page)&&e.jsx("a",{href:r.top_page,target:"_blank",rel:"noreferrer",className:"text-muted-foreground hover:text-primary",children:e.jsx(le,{className:"h-4 w-4"})})})]}),y&&e.jsxs("div",{className:"px-4 pb-4 pl-10 bg-muted/30",children:[e.jsxs("div",{className:"mb-3",children:[e.jsx("div",{className:"text-xs text-muted-foreground mb-1",children:"90-day position trend"}),t.length>1?e.jsx(O,{width:"100%",height:180,children:e.jsxs(W,{data:t.map(a=>({date:a.snapshot_date.slice(5),position:a.position})),margin:{top:5,right:10,left:-10,bottom:0},children:[e.jsx(Ne,{strokeDasharray:"3 3",opacity:.3}),e.jsx(ke,{dataKey:"date",tick:{fontSize:10}}),e.jsx(Y,{reversed:!0,tick:{fontSize:10},domain:["dataMin - 1","dataMax + 1"]}),e.jsx(H,{}),e.jsx(X,{type:"monotone",dataKey:"position",stroke:"#1E5CC8",strokeWidth:2,dot:{r:2},connectNulls:!0})]})}):e.jsx("p",{className:"text-sm text-muted-foreground",children:'Not enough data. Run "Backfill 90d".'})]}),e.jsx("div",{className:"max-h-60 overflow-y-auto border rounded-md bg-background",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{className:"sticky top-0 bg-muted/50",children:e.jsxs("tr",{className:"text-left",children:[e.jsx("th",{className:"p-2",children:"Date"}),e.jsx("th",{className:"p-2",children:"Position"}),e.jsx("th",{className:"p-2",children:"Impressions"}),e.jsx("th",{className:"p-2",children:"Clicks"}),e.jsx("th",{className:"p-2",children:"URL"})]})}),e.jsx("tbody",{children:[...t].reverse().map(a=>{var p;return e.jsxs("tr",{className:"border-t",children:[e.jsx("td",{className:"p-2",children:a.snapshot_date}),e.jsx("td",{className:"p-2 font-medium",children:((p=a.position)==null?void 0:p.toFixed(1))??"â"}),e.jsx("td",{className:"p-2",children:a.impressions}),e.jsx("td",{className:"p-2",children:a.clicks}),e.jsx("td",{className:"p-2 truncate max-w-[200px]",children:a.top_page?e.jsx("a",{href:a.top_page,target:"_blank",rel:"noreferrer",className:"text-primary hover:underline",children:a.top_page.replace(/^https?:\/\/[^/]+/,"")}):"â"})]},a.id)})})]})})]})]},s.id)})})})]}),e.jsx(De,{open:D,onOpenChange:S})]})};export{Ye 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.