PageSourceSearch

https://pulsecars.co.uk/assets/admin.analytics-0jy2L1-x.js

js pulsecars.co.uk collected 2026-10-08 06:12:40 UTC 24,933 bytes, 1 lines download raw bytes

1import{c as ge,k as De,l as Le,r as o,s as H,j as s,L as Ae,C as $e,a as Te}from"./main-BGPPHxHM.js";import{A as Pe}from"./arrow-left-CFvWDBUx.js";import{T as E}from"./trending-up-DKtw2-KX.js";import{B as me}from"./banknote-BGmYL8KK.js";import{A as ue}from"./award-DFcq4IL6.js";import{P as he}from"./package-B9jNKOt0.js";import{T as We}from"./triangle-alert-BRqkkNuQ.js";import{E as Re}from"./eye-5ih1ta9I.js";import{S as Ee}from"./search-DQkFYzwY.js";import{E as Ue}from"./external-link-CK93yfK1.js";const Be=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]],He=ge("globe",Be);const Ve=[["rect",{width:"14",height:"20",x:"5",y:"2",rx:"2",ry:"2",key:"1yt0o3"}],["path",{d:"M12 18h.01",key:"mhygvu"}]],Ge=ge("smartphone",Ve),_=i=>new Intl.NumberFormat("en-GB",{style:"currency",currency:"GBP",maximumFractionDigits:0}).format(i);function Fe(i,y){return Math.max(0,Math.floor((y.getTime()-i.getTime())/864e5))}function es(){const{user:i,isAdmin:y,loading:v,roleChecked:S}=De(),l=Le(),[u,pe]=o.useState([]),[D,xe]=o.useState([]),[V,fe]=o.useState([]),[G,ye]=o.useState([]),[ve,Y]=o.useState(!0),[k,be]=o.useState("90"),[C,P]=o.useState(""),[A,W]=o.useState(""),[$,R]=o.useState("");o.useEffect(()=>{v||!S||(!i||!y)&&l({to:"/admin/login"})},[v,S,i,y,l]),o.useEffect(()=>{!i||!y||(async()=>{Y(!0);const[e,t,r,a]=await Promise.all([H.from("sold_cars").select("make,model,listed_price,sold_price,purchase_price,prep_cost,days_in_stock,sold_at,payment_method,lead_source,customer_postcode,salesperson,part_ex_taken,part_ex_value").order("sold_at",{ascending:!1}),H.from("cars").select("id,status,make,model,price,listed_at,created_at").order("created_at",{ascending:!1}),H.from("page_views").select("path,referrer,session_token,country,region,city,device,viewed_at").order("viewed_at",{ascending:!1}).limit(1e4),H.from("search_queries").select("query,filters,results_count,country,city,viewed_at").order("viewed_at",{ascending:!1}).limit(5e3)]);pe(e.data??[]),xe(t.data??[]),fe(r.data??[]),ye(a.data??[]),Y(!1)})()},[i,y]);const je=o.useMemo(()=>{const e=new Set;for(const t of u)e.add(t.sold_at.slice(0,7));return Array.from(e).sort().reverse()},[u]),we=o.useMemo(()=>{const e=new Set;for(const t of u)e.add(t.sold_at.slice(0,4));return Array.from(e).sort().reverse()},[u]),Ne=o.useMemo(()=>{const e=new Set;for(const t of u){const r=Number(t.sold_at.slice(0,4)),a=Number(t.sold_at.slice(5,7)),n=Math.floor((a-1)/3)+1;e.add(`${r}-Q${n}`)}return Array.from(e).sort().reverse()},[u]),{cutoff:N,endCutoff:M,periodLabel:Se}=o.useMemo(()=>{if(C){const[r,a]=C.split("-").map(Number),n=new Date(Date.UTC(r,a-1,1)),g=new Date(Date.UTC(r,a,1)),b=n.toLocaleDateString("en-GB",{month:"long",year:"numeric",timeZone:"UTC"});return{cutoff:n,endCutoff:g,periodLabel:b}}if($){const[r,a]=$.split("-Q"),n=Number(r),g=Number(a),b=new Date(Date.UTC(n,(g-1)*3,1)),L=new Date(Date.UTC(n,g*3,1));return{cutoff:b,endCutoff:L,periodLabel:`Q${g} ${n}`}}if(A){const r=Number(A),a=new Date(Date.UTC(r,0,1)),n=new Date(Date.UTC(r+1,0,1));return{cutoff:a,endCutoff:n,periodLabel:`${r}`}}if(k==="all")return{cutoff:null,endCutoff:null,periodLabel:"All time"};const e=new Date;return e.setDate(e.getDate()-Number(k)),{cutoff:e,endCutoff:null,periodLabel:{7:"Last 7 days",30:"Last 30 days",90:"Last 90 days",365:"Last 365 days"}[k]??`Last ${k} days`}},[k,C,A,$]),F=e=>{const t=new Date(e);return!(N&&t<N||M&&t>=M)},c=o.useMemo(()=>N||M?u.filter(e=>F(e.sold_at)):u,[u,N,M]),h=o.useMemo(()=>{const e=c.length,t=c.reduce((j,w)=>j+Number(w.sold_price??0),0),r=e?t/e:0,a=c.map(j=>j.days_in_stock??0).filter(j=>j>0),n=a.length?Math.round(a.reduce((j,w)=>j+w,0)/a.length):0,g=c.reduce((j,w)=>j+(Number(w.sold_price??0)-Number(w.listed_price??0)),0),b=c.filter(j=>j.purchase_price!=null),L=b.reduce((j,w)=>j+(Number(w.sold_price??0)-Number(w.purchase_price??0)-Number(w.prep_cost??0)-(w.part_ex_taken?Number(w.part_ex_value??0):0)),0),B=b.length?L/b.length:0,de=b.reduce((j,w)=>j+Number(w.purchase_price??0)+Number(w.prep_cost??0),0),Ce=de>0?L/de*100:0;return{count:e,revenue:t,avgPrice:r,avgDays:n,margin:g,grossProfit:L,avgProfit:B,marginPct:Ce,withCostCount:b.length}},[c]),K=o.useMemo(()=>{const e=new Map;for(const t of c){const r=t.lead_source??"Not recorded",a=e.get(r)??{count:0,revenue:0};a.count+=1,a.revenue+=Number(t.sold_price??0),e.set(r,a)}return Array.from(e.entries()).map(([t,r])=>({source:t,...r})).sort((t,r)=>r.count-t.count)},[c]),X=o.useMemo(()=>{const e=new Map;for(const t of c)e.set(t.payment_method??"Not recorded",(e.get(t.payment_method??"Not recorded")??0)+1);return Array.from(e.entries()).sort((t,r)=>r[1]-t[1])},[c]),Z=o.useMemo(()=>{const e=new Map;for(const t of c){if(!t.salesperson)continue;
1const r=e.get(t.salesperson)??{count:0,revenue:0};r.count+=1,r.revenue+=Number(t.sold_price??0),e.set(t.salesperson,r)}return Array.from(e.entries()).map(([t,r])=>({name:t,...r})).sort((t,r)=>r.count-t.count)},[c]),J=o.useMemo(()=>{const e={B:"Birmingham",BD:"Bradford",BS:"Bristol",CB:"Cambridge",CF:"Cardiff",CH:"Chester",CO:"Colchester",CR:"Croydon",CV:"Coventry",DE:"Derby",DH:"Durham",DL:"Darlington",DN:"Doncaster",DT:"Dorchester",DY:"Dudley",E:"London (East)",EC:"London (City)",EH:"Edinburgh",EX:"Exeter",G:"Glasgow",GL:"Gloucester",GU:"Guildford",HA:"Harrow",HD:"Huddersfield",HG:"Harrogate",HP:"Hemel Hempstead",HR:"Hereford",HU:"Hull",HX:"Halifax",IG:"Ilford",IP:"Ipswich",KT:"Kingston upon Thames",L:"Liverpool",LE:"Leicester",LN:"Lincoln",LS:"Leeds",LU:"Luton",M:"Manchester",ME:"Medway",MK:"Milton Keynes",N:"London (North)",NE:"Newcastle",NG:"Nottingham",NN:"Northampton",NR:"Norwich",NW:"London (NW)",OL:"Oldham",OX:"Oxford",PE:"Peterborough",PL:"Plymouth",PO:"Portsmouth",PR:"Preston",RG:"Reading",RH:"Redhill",RM:"Romford",S:"Sheffield",SE:"London (SE)",SG:"Stevenage",SK:"Stockport",SL:"Slough",SM:"Sutton",SN:"Swindon",SO:"Southampton",SP:"Salisbury",SR:"Sunderland",SS:"Southend-on-Sea",ST:"Stoke-on-Trent",SW:"London (SW)",SY:"Shrewsbury",TA:"Taunton",TF:"Telford",TN:"Tunbridge Wells",TQ:"Torquay",TR:"Truro",TS:"Teesside",TW:"Twickenham",UB:"Uxbridge",W:"London (West)",WA:"Warrington",WC:"London (WC)",WD:"Watford",WF:"Wakefield",WN:"Wigan",WR:"Worcester",WS:"Walsall",WV:"Wolverhampton",YO:"York",DV:"Dover",WC1:"London (WC)"},t=new Map;for(const r of c){if(!r.customer_postcode)continue;const a=r.customer_postcode.toUpperCase().replace(/\s+/g," ").trim(),n=a.includes(" ")?a:a.length>3?`${a.slice(0,-3)} ${a.slice(-3)}`:a,g=n.match(/^[A-Z]+/)?.[0]??"",b=e[g]??"—",L=n,B=t.get(L)??{postcode:n,city:b,count:0};B.count+=1,t.set(L,B)}return Array.from(t.values()).sort((r,a)=>a.count-r.count).slice(0,20)},[c]),q=o.useMemo(()=>{const e=c.filter(r=>r.part_ex_taken),t=e.reduce((r,a)=>r+Number(a.part_ex_value??0),0);return{count:e.length,value:t,rate:c.length?e.length/c.length*100:0}},[c]),T=o.useMemo(()=>{const e=new Map;for(const t of c){const r=`${t.make}|${t.model}`,a=e.get(r)??{make:t.make,model:t.model,count:0,revenue:0,days:[]};a.count+=1,a.revenue+=Number(t.sold_price??0),t.days_in_stock&&t.days_in_stock>0&&a.days.push(t.days_in_stock),e.set(r,a)}return Array.from(e.values()).map(t=>({...t,avgDays:t.days.length?Math.round(t.days.reduce((r,a)=>r+a,0)/t.days.length):null})).sort((t,r)=>r.count-t.count)},[c]),ee=o.useMemo(()=>[...T].filter(e=>e.avgDays!==null&&e.count>=1).sort((e,t)=>(e.avgDays??9999)-(t.avgDays??9999)).slice(0,5),[T]),se=o.useMemo(()=>[...T].filter(e=>e.avgDays!==null&&e.count>=1).sort((e,t)=>(t.avgDays??0)-(e.avgDays??0)).slice(0,5),[T]),te=o.useMemo(()=>{const e=new Map;for(const t of D)e.set(t.status,(e.get(t.status)??0)+1);return Array.from(e.entries()).sort((t,r)=>r[1]-t[1])},[D]),Me=o.useMemo(()=>D.filter(e=>e.status==="available").reduce((e,t)=>e+Number(t.price??0),0),[D]),O=o.useMemo(()=>{const e=new Date;return D.filter(t=>t.status==="available").map(t=>({...t,age:Fe(new Date(t.listed_at??t.created_at),e)})).filter(t=>t.age>=60).sort((t,r)=>r.age-t.age).slice(0,10)},[D]),d=o.useMemo(()=>N||M?V.filter(e=>F(e.viewed_at)):V,[V,N,M]),z=o.useMemo(()=>N||M?G.filter(e=>F(e.viewed_at)):G,[G,N,M]),I=o.useMemo(()=>{const e=d.length,t=new Set(d.map(n=>n.session_token).filter(Boolean)).size,r=t,a=r>0?e/r:0;return{totalViews:e,uniqueVisitors:t,pagesPerSession:a}},[d]),re=o.useMemo(()=>{let e,t;C&&N&&M?(e=new Date(N),t=Math.round((M.getTime()-N.getTime())/864e5)):(t=k==="all"?90:Math.min(Number(k),90),e=new Date,e.setDate(e.getDate()-(t-1)));const r=[];for(let n=0;n<t;n++){const g=new Date(e);g.setDate(e.getDate()+n),r.push({date:g.toISOString().slice(0,10),views:0,visitors:new Set})}const a=new Map(r.map((n,g)=>[n.date,g]));for(const n of d){const g=n.viewed_at.slice(0,10),b=a.get(g);b!=null&&(r[b].views+=1,n.session_token&&r[b].visitors.add(n.session_token))}return r.map(n=>({date:n.date,views:n.views,visitors:n.visitors.size}))},[d,k,C,N,M]),_e=o.useMemo(()=>{const e=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],t=[0,0,0,0,0,0,0];for(const r of d)t[new Date(r.viewed_at).getDay()]+=1;return e.map((r,a)=>({day:r,count:t[a]}))},[d]),ke=o.useMemo(()=>{const e=new Array(24).fill(0);for(const t of d)e[new Date(t.viewed_at).getHours()]+=1;return e.map((t,r)=>({hour:r,count:t}))},[d]),ae=o.useMemo(()=>{const e=new Map;for(const t of d)e.set(t.path,(e.get(t.path)??0)+1);return Array.from(e.entries()).sort((t,r)=>r[1]-t[1]).slice(0,10)},[d]),oe=o.useMemo(()=>{const e=new Map;for(const t of d){let r="Direct";if(t.referrer)try{r=new URL(t.referrer).hostname.replace(/^www\./,"")}catch{r="Other"}e.set(r,(e.get(r)??0)+1)}return Array.from(e.entries()).sort((t,r)=>r[1]-t[1]).slice(0,10)},[d]),ne=o.useMemo(()=>{const e=new Map;for(const t of d)e.set(t.country??"Unknown",(e.get(t.country??"Unknown")??0)+1);return Array.from(e.entries()).sort((t,r)=>r[1]-t[1]).slice(0,10)},[d]),ie=o.useMemo(()=>{const e=new Map;for(const t of d){if(!t.city)continue;const r=t.city,a=e.get(r)??{count:0,region:t.region};a.count+=1,e.set(r,a)}return Array.from(e.entries()).map(([t,r])=>({city:t,region:r.region,count:r.count})).sort((t,r)=>r.count-t.count).slice(0,15)},[d]),le=o.useMemo(()=>{const e=new Map;for(const t of d)e.set(t.device??"unknown",(e.get(t.device??"unknown")??0)+1);return Array.from(e.entries()).sort((t,r)=>r[1]-t[1])},[d]),U=o.useMemo(()=>{const e=new Map;for(const t of z){const r=(t.query??"").trim().toLowerCase();if(!r)continue;const a=e.get(r)??{count:0,zero:0};a.count+=1,(t.results_count??0)===0&&(a.zero+=1),e.set(r,a)}return Array.from(e.entries()).map(([t,r])=>({q:t,...r})).sort((t,r)=>r.count-t.count).slice(0,15)},[z]),ce=o.useMemo(()=>U.filter(e=>e.zero>0).slice(0,10),[U]);return v||!S||!i||!y?s.jsx("div",{className:"flex min-h-screen items-center justify-center text-muted-foreground",children:"Loading…"}):s.jsxs("div",{className:"min-h-screen bg-surface",children:[s.jsx("header",{className:"border-b border-border bg-card",children:s.jsxs("div",{className:"mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8",children:[s.jsxs(Ae,{to:"/admin",className:"inline-flex items-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground",children:[s.jsx(Pe,{className:"h-4 w-4"})," Back to admin"]}),s.jsx("h1",{className:"font-display text-lg font-bold",children:"Sales Analytics"}),s.jsx("div",{})]})}),s.jsxs("main",{className:"mx-auto max-w-7xl px-3 py-6 sm:px-6 sm:py-8 lg:px-8",children:[s.jsxs("div",{className:"mb-6 flex flex-wrap items-center gap-2",children:[s.jsx("span",{className:"text-sm text-muted-foreground",children:"Qu
1ick range:"}),[{v:"7",label:"Week"},{v:"30",label:"Month"},{v:"90",label:"Quarter"},{v:"365",label:"Year"},{v:"all",label:"All time"}].map(({v:e,label:t})=>s.jsx("button",{onClick:()=>{be(e),P(""),W(""),R("")},className:`rounded-full border px-3 py-1 text-xs font-medium transition ${k===e&&!C&&!A&&!$?"border-primary bg-primary text-primary-foreground":"border-border bg-card hover:bg-muted"}`,children:t},e)),s.jsx("span",{className:"ml-2 text-sm text-muted-foreground",children:"Month:"}),s.jsxs("select",{value:C,onChange:e=>{P(e.target.value),e.target.value&&(W(""),R(""))},className:`rounded-full border px-3 py-1 text-xs font-medium transition ${C?"border-primary bg-primary text-primary-foreground":"border-border bg-card hover:bg-muted"}`,children:[s.jsx("option",{value:"",children:"— Any —"}),je.map(e=>{const[t,r]=e.split("-").map(Number),a=new Date(Date.UTC(t,r-1,1)).toLocaleDateString("en-GB",{month:"long",year:"numeric",timeZone:"UTC"});return s.jsx("option",{value:e,children:a},e)})]}),s.jsx("span",{className:"text-sm text-muted-foreground",children:"Quarter:"}),s.jsxs("select",{value:$,onChange:e=>{R(e.target.value),e.target.value&&(P(""),W(""))},className:`rounded-full border px-3 py-1 text-xs font-medium transition ${$?"border-primary bg-primary text-primary-foreground":"border-border bg-card hover:bg-muted"}`,children:[s.jsx("option",{value:"",children:"— Any —"}),Ne.map(e=>s.jsx("option",{value:e,children:e},e))]}),s.jsx("span",{className:"text-sm text-muted-foreground",children:"Year:"}),s.jsxs("select",{value:A,onChange:e=>{W(e.target.value),e.target.value&&(P(""),R(""))},className:`rounded-full border px-3 py-1 text-xs font-medium transition ${A?"border-primary bg-primary text-primary-foreground":"border-border bg-card hover:bg-muted"}`,children:[s.jsx("option",{value:"",children:"— Any —"}),we.map(e=>s.jsx("option",{value:e,children:e},e))]}),(C||A||$)&&s.jsx("button",{onClick:()=>{P(""),W(""),R("")},className:"rounded-full border border-border bg-card px-3 py-1 text-xs font-medium hover:bg-muted",children:"Clear"}),s.jsxs("span",{className:"basis-full text-xs text-muted-foreground sm:ml-auto sm:basis-auto",children:["Showing: ",Se]})]}),ve?s.jsx("p",{className:"py-12 text-center text-muted-foreground",children:"Loading…"}):s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[s.jsx(p,{icon:s.jsx(E,{className:"h-4 w-4"}),label:"Cars sold",value:String(h.count)}),s.jsx(p,{icon:s.jsx(me,{className:"h-4 w-4"}),label:"Revenue",value:_(h.revenue)}),s.jsx(p,{icon:s.jsx(ue,{className:"h-4 w-4"}),label:"Avg sold price",value:_(h.avgPrice)}),s.jsx(p,{icon:s.jsx($e,{className:"h-4 w-4"}),label:"Avg days in stock",value:h.avgDays?`${h.avgDays} d`:"—"})]}),s.jsxs("section",{className:"mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[s.jsx(p,{icon:s.jsx(he,{className:"h-4 w-4"}),label:"Active stock",value:String(D.filter(e=>e.status==="available").length),sub:`${D.length} total incl. reserved/sold pending`}),s.jsx(p,{icon:s.jsx(me,{className:"h-4 w-4"}),label:"Stock value",value:_(Me),sub:"Available cars"}),s.jsx(p,{icon:s.jsx(E,{className:"h-4 w-4"}),label:"Margin vs listed",value:`${h.margin>=0?"+":""}${_(h.margin)}`,sub:"Sold price − listed price"}),s.jsx(p,{icon:s.jsx(We,{className:"h-4 w-4"}),label:"Aging stock (60d+)",value:String(O.length),sub:"Available cars listed over 60 days"})]}),s.jsxs("section",{className:"mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[s.jsx(p,{icon:s.jsx(E,{className:"h-4 w-4"}),label:"Gross profit",value:`${h.grossProfit>=0?"":"−"}${_(Math.abs(h.grossProfit))}`,sub:h.withCostCount?`From ${h.withCostCount} of ${h.count} sales w/ purchase price`:"Add purchase prices to unlock"}),s.jsx(p,{icon:s.jsx(ue,{className:"h-4 w-4"}),label:"Avg profit per car",value:h.withCostCount?_(h.avgProfit):"—",sub:"Sold − purchase − prep − part-ex"}),s.jsx(p,{icon:s.jsx(E,{className:"h-4 w-4"}),label:"Margin %",value:h.withCostCount?`${h.marginPct.toFixed(1)}%`:"—",sub:"Profit ÷ (purchase + prep)"}),s.jsx(p,{icon:s.jsx(he,{className:"h-4 w-4"}),label:"Part-ex rate",value:`${q.rate.toFixed(0)}%`,sub:`${q.count} taken · ${_(q.value)} total`})]}),s.jsxs("section",{className:"mt-8 grid gap-6 lg:grid-cols-2",children:[s.jsx(m,{title:"Top selling models",children:T.length===0?s.jsx(x,{children:"No sales in this range."}):s.jsx(f,{rows:T.slice(0,8).map(e=>[`${e.make} ${e.model}`,`${e.count}
1 sold`,_(e.revenue),e.avgDays?`${e.avgDays} d avg`:"—"]),headers:["Model","Units","Revenue","Avg days"]})}),s.jsx(m,{title:"Fast movers (lowest days in stock)",children:ee.length===0?s.jsx(x,{children:"Need more data."}):s.jsx(f,{rows:ee.map(e=>[`${e.make} ${e.model}`,`${e.avgDays} days avg`,`${e.count} sold`]),headers:["Model","Avg days","Units"]})}),s.jsx(m,{title:"Slow movers (highest days in stock)",children:se.length===0?s.jsx(x,{children:"Need more data."}):s.jsx(f,{rows:se.map(e=>[`${e.make} ${e.model}`,`${e.avgDays} days avg`,`${e.count} sold`]),headers:["Model","Avg days","Units"]})}),s.jsx(m,{title:"Current stock by status",children:te.length===0?s.jsx(x,{children:"No stock."}):s.jsx(f,{rows:te.map(([e,t])=>[s.jsx("span",{className:"capitalize",children:e.replace("_"," ")},e),String(t)]),headers:["Status","Count"]})}),s.jsx(m,{title:"Aging available stock (60+ days)",className:"lg:col-span-2",children:O.length===0?s.jsx(x,{children:"Nothing aging — great turnover!"}):s.jsx(f,{rows:O.map(e=>[`${e.make} ${e.model}`,`${e.age} days`,_(Number(e.price))]),headers:["Car","Age","Listed price"]})})]}),s.jsxs("div",{className:"mt-12 mb-6 flex flex-wrap items-baseline justify-between gap-2",children:[s.jsx("h2",{className:"font-display text-xl font-bold sm:text-2xl",children:"Sales mix & customers"}),s.jsx("span",{className:"text-xs text-muted-foreground",children:"From the sold-car records"})]}),s.jsxs("section",{className:"grid gap-6 lg:grid-cols-2",children:[s.jsx(m,{title:"Where sales come from (lead source)",children:K.length===0?s.jsx(x,{children:"Capture lead source when marking cars sold to see this."}):s.jsx(f,{headers:["Source","Units","Revenue"],rows:K.map(e=>[e.source,String(e.count),_(e.revenue)])})}),s.jsx(m,{title:"Payment method mix",children:X.length===0?s.jsx(x,{children:"No data yet."}):s.jsx(f,{headers:["Method","Sales"],rows:X.map(([e,t])=>[e,String(t)])})}),s.jsx(m,{title:"Customers by area (UK postcode)",children:J.length===0?s.jsx(x,{children:"Add customer postcode when marking sold."}):s.jsx(f,{headers:["Postcode","City","Customers"],rows:J.map(e=>[e.postcode,e.city,String(e.count)])})}),s.jsx(m,{title:"Salesperson performance",children:Z.length===0?s.jsx(x,{children:"Record salesperson on the sold form to track performance."}):s.jsx(f,{headers:["Name","Sales","Revenue"],rows:Z.map(e=>[e.name,String(e.count),_(e.revenue)])})})]}),s.jsxs("div",{className:"mt-12 mb-6 flex flex-wrap items-baseline justify-between gap-2",children:[s.jsx("h2",{className:"font-display text-xl font-bold sm:text-2xl",children:"Website traffic & visitors"}),s.jsx("span",{className:"text-xs text-muted-foreground",children:"Filtered by time range above"})]}),s.jsxs("section",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:[s.jsx(p,{icon:s.jsx(Re,{className:"h-4 w-4"}),label:"Page views",value:I.totalViews.toLocaleString(),sub:"Every page load"}),s.jsx(p,{icon:s.jsx(He,{className:"h-4 w-4"}),label:"Unique visitors",value:I.uniqueVisitors.toLocaleString(),sub:"Distinct browsers"}),s.jsx(p,{icon:s.jsx(E,{className:"h-4 w-4"}),label:"Pages per visit",value:I.pagesPerSession.toFixed(1),sub:"Engagement depth"}),s.jsx(p,{icon:s.jsx(Ee,{className:"h-4 w-4"}),label:"Searches",value:z.length.toLocaleString(),sub:"On the inventory page"})]}),s.jsxs("section",{className:"mt-8 grid gap-6 lg:grid-cols-2",children:[s.jsx(m,{title:"Traffic by day",className:"lg:col-span-2",children:re.every(e=>e.views===0)?s.jsx(x,{children:"No traffic recorded yet. Visits will start appearing as soon as people browse the site."}):s.jsx(Q,{data:re.map(e=>({label:e.date.slice(5),value:e.views,secondary:e.visitors})),primaryLabel:"Views",secondaryLabel:"Unique"})}),s.jsx(m,{title:"Busiest days of the week",children:s.jsx(Q,{data:_e.map(e=>({label:e.day,value:e.count})),primaryLabel:"Views"})}),s.jsx(m,{title:"Busiest hours of the day",children:s.jsx(Q,{data:ke.map(e=>({label:e.hour.toString().padStart(2,"0"),value:e.count})),primaryLabel:"Views"})}),s.jsx(m,{title:"Top pages",children:ae.length===0?s.jsx(x,{children:"No data yet."}):s.jsx(f,{headers:["Page","Views"],rows:ae.map(([e,t])=>[e,String(t)])})}),s.jsx(m,{title:s.jsxs(s.Fragment,{children:[s.jsx(Ue,{className:"inline h-4 w-4 mr-1"}),"Where visitors come from"]}),children:oe.length===0?s.jsx(x,{children:"No data yet."}):s.jsx(f,{headers:["Source","Visits"],rows:oe.map(([e,t])=>[e,String(t)])})}),s.jsx(m,{title:s.jsxs(s.Fragment,{children:[s.jsx(Te,{className:"inline h-4 w-4 mr-1"}),"Top cities"]}),children:ie.length===0?s.jsx(x,{children:"City data appears once visitors browse the live site."}):s.jsx(f,{headers:["City","Region","Visits"],rows:ie.map(e=>
1[e.city,e.region??"—",String(e.count)])})}),s.jsx(m,{title:"Top countries",children:ne.length===0?s.jsx(x,{children:"No data yet."}):s.jsx(f,{headers:["Country","Visits"],rows:ne.map(([e,t])=>[e,String(t)])})}),s.jsx(m,{title:s.jsxs(s.Fragment,{children:[s.jsx(Ge,{className:"inline h-4 w-4 mr-1"}),"Devices"]}),children:le.length===0?s.jsx(x,{children:"No data yet."}):s.jsx(f,{headers:["Device","Visits"],rows:le.map(([e,t])=>[s.jsx("span",{className:"capitalize",children:e},e),String(t)])})}),s.jsx(m,{title:"What customers are searching for",className:"lg:col-span-2",children:U.length===0?s.jsx(x,{children:"No searches recorded yet. Anything typed into the inventory search bar will show up here."}):s.jsx(f,{headers:["Search term","Times searched","Returned 0 results"],rows:U.map(e=>[s.jsx("span",{className:"font-medium",children:e.q},e.q),String(e.count),e.zero>0?s.jsx("span",{className:"text-amber-600",children:e.zero}):"—"])})}),ce.length>0&&s.jsxs(m,{title:"Searches with no results (missed opportunities)",className:"lg:col-span-2",children:[s.jsx("p",{className:"mb-3 text-xs text-muted-foreground",children:"Customers are looking for these but you don't have matching stock. Worth considering for your next buy."}),s.jsx(f,{headers:["Search term","Times searched"],rows:ce.map(e=>[e.q,String(e.zero)])})]})]})]})]})]})}function p({icon:i,label:y,value:v,sub:S}){return s.jsxs("div",{className:"min-w-0 rounded-2xl border border-border bg-card p-4 shadow-card sm:p-5",children:[s.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[s.jsx("span",{className:"shrink-0",children:i}),s.jsx("span",{className:"truncate",children:y})]}),s.jsx("div",{className:"mt-2 break-words font-display text-xl font-bold leading-tight sm:text-2xl",children:v}),S&&s.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:S})]})}function m({title:i,children:y,className:v=""}){return s.jsxs("div",{className:`min-w-0 rounded-2xl border border-border bg-card p-4 shadow-card sm:p-5 ${v}`,children:[s.jsx("h2",{className:"font-display text-base font-semibold",children:i}),s.jsx("div",{className:"mt-3",children:y})]})}function Q({data:i,primaryLabel:y="Value",secondaryLabel:v}){const S=Math.max(1,...i.map(l=>Math.max(l.value,l.secondary??0)));return s.jsxs("div",{children:[s.jsx("div",{className:"flex h-40 items-end gap-1 overflow-x-auto",children:i.map((l,u)=>s.jsx("div",{className:"flex min-w-[14px] flex-1 flex-col items-center gap-0.5",title:`${l.label}: ${l.value}${l.secondary!==void 0?` (${l.secondary} unique)`:""}`,children:s.jsxs("div",{className:"flex w-full flex-1 items-end justify-center gap-0.5",children:[s.jsx("div",{className:"w-full rounded-t bg-primary/80",style:{height:`${l.value/S*100}%`,minHeight:l.value>0?2:0}}),l.secondary!==void 0&&s.jsx("div",{className:"w-1/2 rounded-t bg-primary/30",style:{height:`${l.secondary/S*100}%`,minHeight:l.secondary>0?2:0}})]})},u))}),s.jsx("div",{className:"mt-2 flex justify-between text-[10px] text-muted-foreground",children:i.filter((l,u)=>u%Math.max(1,Math.floor(i.length/8))===0).map((l,u)=>s.jsx("span",{children:l.label},u))}),s.jsxs("div",{className:"mt-2 flex items-center gap-3 text-[10px] text-muted-foreground",children:[s.jsxs("span",{className:"flex items-center gap-1",children:[s.jsx("span",{className:"inline-block h-2 w-2 rounded-sm bg-primary/80"})," ",y]}),v&&s.jsxs("span",{className:"flex items-center gap-1",children:[s.jsx("span",{className:"inline-block h-2 w-2 rounded-sm bg-primary/30"})," ",v]})]})]})}function x({children:i}){return s.jsx("p",{className:"py-6 text-center text-sm text-muted-foreground",children:i})}function f({headers:i,rows:y}){return s.jsx("div",{className:"overflow-x-auto",children:s.jsxs("table",{className:"w-full text-sm",children:[s.jsx("thead",{className:"text-left text-xs uppercase tracking-wide text-muted-foreground",children:s.jsx("tr",{children:i.map(v=>s.jsx("th",{className:"py-2 pr-4 font-medium",children:v},v))})}),s.jsx("tbody",{children:y.map((v,S)=>s.jsx("tr",{className:"border-t border-border",children:v.map((l,u)=>s.jsx("td",{className:"py-2 pr-4",children:l},u))},S))})]})})}export{es 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.