@hogsend/cli 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hogsend/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"tsup": "^8.5.1",
|
|
33
33
|
"tsx": "^4.22.4",
|
|
34
34
|
"vitest": "^4.1.7",
|
|
35
|
-
"@hogsend/studio": "^0.
|
|
35
|
+
"@hogsend/studio": "^0.10.0",
|
|
36
36
|
"@repo/typescript-config": "0.0.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
@@ -247,4 +247,4 @@ Error generating stack: `+l.message+`
|
|
|
247
247
|
*
|
|
248
248
|
* This source code is licensed under the ISC license.
|
|
249
249
|
* See the LICENSE file in the root directory of this source tree.
|
|
250
|
-
*/const zE=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]],Rh=ze("Zap",zE),hb=Y.createContext(null);function DE({children:n}){const[s,i]=Y.useState([]),r=Y.useRef(0),c=Y.useCallback(y=>{i(p=>p.filter(m=>m.id!==y))},[]),d=Y.useCallback(y=>{const p=++r.current,m={id:p,title:y.title,description:y.description,variant:y.variant??"success"};i(b=>[...b,m]),window.setTimeout(()=>c(p),5e3)},[c]),h=Y.useMemo(()=>({toast:d}),[d]);return f.jsxs(hb.Provider,{value:h,children:[n,f.jsx("div",{className:"pointer-events-none fixed bottom-4 right-4 z-[60] flex w-full max-w-sm flex-col gap-2",children:s.map(y=>f.jsxs("div",{className:Je("pointer-events-auto flex items-start gap-3 rounded-md border bg-card p-4 shadow-lg",y.variant==="error"&&"border-destructive/40"),children:[y.variant==="success"?f.jsx(rb,{className:"mt-0.5 h-5 w-5 shrink-0 text-emerald-500"}):f.jsx(ob,{className:"mt-0.5 h-5 w-5 shrink-0 text-destructive"}),f.jsxs("div",{className:"flex-1 space-y-0.5",children:[f.jsx("p",{className:"text-sm font-medium",children:y.title}),y.description?f.jsx("p",{className:"text-sm text-muted-foreground",children:y.description}):null]}),f.jsx("button",{type:"button","aria-label":"Dismiss",className:"text-muted-foreground transition-colors hover:text-foreground",onClick:()=>c(y.id),children:f.jsx(jh,{className:"h-4 w-4"})})]},y.id))})]})}function vs(){const n=Y.useContext(hb);if(!n)throw new Error("useToast must be used within a ToastProvider");return n}const UE=new hS({defaultOptions:{queries:{staleTime:3e4,retry:(n,s)=>s instanceof Xt&&[401,403].includes(s.status)?!1:n<2,refetchOnWindowFocus:!1}}}),wr="https://docs.hogsend.com",ll={docs:wr,quickstart:`${wr}/docs/getting-started`,recipes:`${wr}/docs/recipes`,journeys:`${wr}/docs/guides/journeys`,buckets:`${wr}/docs/guides/buckets`},kE=[{label:"Overview",path:"/",icon:fE},{label:"Sends",path:"/sends",icon:Yr},{label:"Templates",path:"/templates",icon:ub},{label:"Journeys",path:"/journeys",icon:Sh},{label:"Buckets",path:"/buckets",icon:Y_},{label:"Contacts",path:"/contacts",icon:db},{label:"Suppressions",path:"/suppressions",icon:ib},{label:"Debug",path:"/debug",icon:sE},{label:"Settings",path:"/settings",icon:EE}];function LE(){const n=b2({select:s=>s.location.pathname});return f.jsxs("aside",{className:"flex h-full w-60 flex-col border-r bg-card",children:[f.jsx("div",{className:"flex h-14 items-center border-b px-5",children:f.jsx("span",{className:"text-sm font-semibold tracking-tight",children:"Hogsend Studio"})}),f.jsx("nav",{className:"flex-1 space-y-1 overflow-y-auto p-2",children:kE.map(s=>{const i=s.path==="/"?n==="/":n.startsWith(s.path),r=s.icon;return f.jsxs(bi,{to:s.path,className:Je("flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",i?"bg-accent text-accent-foreground":"text-muted-foreground hover:bg-accent hover:text-accent-foreground"),children:[f.jsx(r,{className:"h-4 w-4"}),s.label]},s.path)})}),f.jsx("div",{className:"border-t p-2",children:f.jsxs("a",{href:ll.docs,target:"_blank",rel:"noreferrer",className:"flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",children:[f.jsx(G_,{className:"h-4 w-4"}),"Docs",f.jsx(lb,{className:"ml-auto h-3.5 w-3.5 opacity-60"})]})})]})}function BE(){const{data:n}=H0();return f.jsxs("div",{className:"flex h-full",children:[f.jsx(LE,{}),f.jsxs("div",{className:"flex flex-1 flex-col overflow-hidden",children:[f.jsxs("header",{className:"flex h-14 items-center justify-end gap-3 border-b bg-card px-6",children:[n!=null&&n.user?f.jsx("span",{className:"text-sm text-muted-foreground",children:n.user.email}):null,f.jsxs(Ke,{variant:"ghost",size:"sm",onClick:()=>{LR()},children:[f.jsx(hE,{className:"h-4 w-4"}),"Sign out"]})]}),f.jsx("main",{className:"flex-1 overflow-y-auto p-6",children:f.jsx(D0,{})})]})]})}function Dt({className:n,...s}){return f.jsx("div",{className:Je("animate-pulse rounded-md bg-muted",n),...s})}function Ri({rows:n=6}){return f.jsxs("div",{className:"space-y-2",children:[f.jsx(Dt,{className:"h-9 w-full"}),Array.from({length:n}).map((s,i)=>f.jsx(Dt,{className:"h-12 w-full"},i))]})}function qE({count:n=4}){return f.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:Array.from({length:n}).map((s,i)=>f.jsx(Dt,{className:"h-28 w-full"},i))})}function ol({title:n,description:s,icon:i=rE,action:r}){return f.jsxs("div",{className:"flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed p-12 text-center",children:[f.jsx(i,{className:"h-8 w-8 text-muted-foreground"}),f.jsx("p",{className:"text-sm font-medium",children:n}),s?f.jsx("p",{className:"max-w-sm text-sm text-muted-foreground",children:s}):null,r?f.jsx("div",{className:"mt-2 flex flex-wrap items-center justify-center gap-2",children:r}):null]})}function Ut({error:n,onRetry:s}){const i=n instanceof Xt||n instanceof Error?n.message:"Something went wrong.";return f.jsxs("div",{className:"flex flex-col items-center justify-center gap-3 rounded-lg border border-destructive/40 bg-destructive/5 p-12 text-center",children:[f.jsx(F_,{className:"h-8 w-8 text-destructive"}),f.jsxs("div",{className:"space-y-1",children:[f.jsx("p",{className:"text-sm font-medium",children:"Failed to load"}),f.jsx("p",{className:"max-w-sm text-sm text-muted-foreground",children:i})]}),s?f.jsx(Ke,{variant:"outline",size:"sm",onClick:s,children:"Retry"}):null]})}function Ra({title:n,description:s,action:i}){return f.jsxs("div",{className:"flex items-start justify-between gap-4",children:[f.jsxs("div",{children:[f.jsx("h1",{className:"text-2xl font-semibold tracking-tight",children:n}),s?f.jsx("p",{className:"text-sm text-muted-foreground",children:s}):null]}),i?f.jsx("div",{className:"shrink-0",children:i}):null]})}const HE=V0("inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground",secondary:"border-transparent bg-secondary text-secondary-foreground",destructive:"border-transparent bg-destructive text-destructive-foreground",outline:"text-foreground"}},defaultVariants:{variant:"default"}});function ft({className:n,variant:s,...i}){return f.jsx("div",{className:Je(HE({variant:s}),n),...i})}function Hc({open:n,onClose:s,title:i,description:r,children:c,footer:d,className:h}){return Y.useEffect(()=>{if(!n)return;const y=p=>{p.key==="Escape"&&s()};return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[n,s]),n?f.jsxs("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4",children:[f.jsx("button",{type:"button","aria-label":"Close dialog",className:"absolute inset-0 bg-black/50",onClick:s}),f.jsxs("div",{role:"dialog","aria-modal":"true",className:Je("relative z-10 w-full max-w-lg rounded-lg border bg-card p-6 shadow-lg",h),children:[f.jsxs("div",{className:"mb-4 flex items-start justify-between gap-4",children:[f.jsxs("div",{className:"space-y-1",children:[f.jsx("h2",{className:"text-lg font-semibold leading-none tracking-tight",children:i}),r?f.jsx("p",{className:"text-sm text-muted-foreground",children:r}):null]}),f.jsx(Ke,{variant:"ghost",size:"icon",className:"-mr-2 -mt-2 h-8 w-8",onClick:s,"aria-label":"Close",children:f.jsx(jh,{className:"h-4 w-4"})})]}),c?f.jsx("div",{className:"space-y-4",children:c}):null,d?f.jsx("div",{className:"mt-6 flex justify-end gap-2",children:d}):null]})]}):null}function _i({open:n,onClose:s,onConfirm:i,title:r,description:c,confirmLabel:d="Confirm",cancelLabel:h="Cancel",destructive:y,loading:p}){return f.jsx(Hc,{open:n,onClose:s,title:r,description:c,footer:f.jsxs(f.Fragment,{children:[f.jsx(Ke,{variant:"outline",onClick:s,disabled:p,children:h}),f.jsx(Ke,{variant:y?"destructive":"default",onClick:i,disabled:p,children:p?"Working…":d})]})})}function xi({href:n,children:s,variant:i="outline",className:r}){return f.jsxs("a",{href:n,target:"_blank",rel:"noreferrer",className:Je(xh({variant:i,size:"sm"}),r),children:[s,f.jsx(lb,{className:"h-3.5 w-3.5 opacity-70"})]})}const cl=Y.forwardRef(({className:n,...s},i)=>f.jsx("div",{className:"relative w-full overflow-auto",children:f.jsx("table",{ref:i,className:Je("w-full caption-bottom text-sm",n),...s})}));cl.displayName="Table";const ul=Y.forwardRef(({className:n,...s},i)=>f.jsx("thead",{ref:i,className:Je("[&_tr]:border-b",n),...s}));ul.displayName="TableHeader";const fl=Y.forwardRef(({className:n,...s},i)=>f.jsx("tbody",{ref:i,className:Je("[&_tr:last-child]:border-0",n),...s}));fl.displayName="TableBody";const on=Y.forwardRef(({className:n,...s},i)=>f.jsx("tr",{ref:i,className:Je("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",n),...s}));on.displayName="TableRow";const Ce=Y.forwardRef(({className:n,...s},i)=>f.jsx("th",{ref:i,className:Je("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",n),...s}));Ce.displayName="TableHead";const we=Y.forwardRef(({className:n,...s},i)=>f.jsx("td",{ref:i,className:Je("p-2 align-middle [&:has([role=checkbox])]:pr-0",n),...s}));we.displayName="TableCell";const QE=Y.forwardRef(({className:n,...s},i)=>f.jsx("caption",{ref:i,className:Je("mt-4 text-sm text-muted-foreground",n),...s}));QE.displayName="TableCaption";function GE(){return Xe.get("/v1/admin/metrics/overview")}function VE(n){return Xe.get("/v1/admin/emails",{query:n})}function YE(n){return Xe.get(`/v1/admin/emails/${n}`)}function KE(n){return Xe.post(`/v1/admin/emails/${n}/resend`)}function PE(){return Xe.get("/v1/admin/templates")}function XE(n){return Xe.get(`/v1/admin/templates/${encodeURIComponent(n)}/preview`)}function ZE(n,s){return Xe.post(`/v1/admin/templates/${encodeURIComponent(n)}/send-test`,{json:{to:s}})}function JE(n){return Xe.get(`/v1/admin/reporting/templates/${encodeURIComponent(n)}`)}function FE(){return Xe.get("/v1/admin/metrics/journeys")}function IE(n){return Xe.get(`/v1/admin/metrics/journeys/${encodeURIComponent(n)}`)}function mb(){return Xe.get("/v1/admin/journeys",{query:{limit:100}})}function $E(n,s){return Xe.put(`/v1/admin/journeys/${encodeURIComponent(n)}`,{json:{enabled:s}})}function WE(){return Xe.get("/v1/admin/buckets",{query:{limit:100}})}function ew(n){return Xe.get(`/v1/admin/buckets/${encodeURIComponent(n)}`)}function tw(){return Xe.get("/v1/admin/metrics/buckets")}function nw(n,s){return Xe.get(`/v1/admin/metrics/buckets/${encodeURIComponent(n)}`,{query:{period:s}})}function aw(n,s){return Xe.patch(`/v1/admin/buckets/${encodeURIComponent(n)}`,{json:{enabled:s}})}function sw(n){return Xe.get("/v1/admin/contacts",{query:{search:n,limit:50}})}function lw(n){return Xe.get(`/v1/admin/contacts/${encodeURIComponent(n)}`)}function iw(n){return Xe.get(`/v1/admin/reporting/contacts/${encodeURIComponent(n)}/activity`)}function rw(n){return Xe.get(`/v1/admin/contacts/${encodeURIComponent(n)}/timeline`,{query:{limit:100}})}function yb(n,s){return Xe.put(`/v1/admin/contacts/${encodeURIComponent(n)}/preferences`,{json:s})}function ow(n){return Xe.get("/v1/admin/suppressions",{query:{type:n,limit:200}})}function cw(){return Xe.get("/v1/admin/api-keys",{query:{limit:100,includeRevoked:"true"}})}function uw(n){return Xe.post("/v1/admin/api-keys",{json:n})}function fw(n){return Xe.delete(`/v1/admin/api-keys/${encodeURIComponent(n)}`)}function dw(n){return Xe.post("/v1/admin/events",{json:{event:n.event,userId:n.userId,userEmail:n.userEmail,properties:n.properties??{}}})}const Pe={overview:["overview"],emails:n=>["emails",n],email:n=>["email",n],templates:["templates"],templatePreview:n=>["template-preview",n],templateReport:n=>["template-report",n],journeyMetrics:["journey-metrics"],journeys:["journeys"],journeyFunnel:n=>["journey-funnel",n],buckets:["buckets"],bucketMetrics:["bucket-metrics"],bucket:n=>["bucket",n],bucketTrend:n=>["bucket-trend",n],contacts:n=>["contacts",n],contact:n=>["contact",n],contactActivity:n=>["contact-activity",n],contactTimeline:n=>["contact-timeline",n],suppressions:n=>["suppressions",n],apiKeys:["api-keys"]};function Yn(n){if(!n)return"—";const s=new Date(n);return Number.isNaN(s.getTime())?"—":s.toLocaleString(void 0,{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function Mc(n){if(!n)return"—";const s=new Date(n);return Number.isNaN(s.getTime())?"—":s.toLocaleDateString(void 0,{month:"short",day:"numeric"})}function hw(n){if(!n)return"—";const s=new Date(n);if(Number.isNaN(s.getTime()))return"—";const i=Date.now()-s.getTime(),r=Math.round(i/1e3);if(r<60)return"just now";const c=Math.round(r/60);if(c<60)return`${c}m ago`;const d=Math.round(c/60);if(d<24)return`${d}h ago`;const h=Math.round(d/24);return h<30?`${h}d ago`:Mc(n)}function Si(n){return n==null||Number.isNaN(n)?"—":`${(n*100).toFixed(1)}%`}function ut(n){return n==null||Number.isNaN(n)?"—":n.toLocaleString()}function _h(n){if(n==null||Number.isNaN(n))return"—";if(n<60)return`${Math.round(n)}s`;const s=Math.floor(n/60);if(s<60)return`${s}m`;const i=Math.floor(s/60),r=s%60;if(i<24)return r?`${i}h ${r}m`:`${i}h`;const c=Math.floor(i/24),d=i%24;return d?`${c}d ${d}h`:`${c}d`}function mw(n){if(!n)return null;const s=(n.hours??0)*3600+(n.minutes??0)*60+(n.seconds??0);return s>0?_h(s):null}function yw(n,s=48){return n.length>s?`${n.slice(0,s-1)}…`:n}function lh({data:n,height:s=160,label:i="value"}){var c,d;if(n.length===0)return f.jsx("div",{className:"flex items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground",style:{height:s},children:"No data in range"});const r=Math.max(...n.map(h=>h.value),1);return f.jsxs("div",{className:"space-y-2",children:[f.jsx("div",{className:"flex items-end gap-1 rounded-md border bg-muted/20 p-3",style:{height:s},children:n.map(h=>{const y=h.value/r*100;return f.jsx("div",{className:"group flex flex-1 flex-col items-center justify-end",title:`${Mc(h.date)}: ${h.value} ${i}`,children:f.jsx("div",{className:"w-full min-w-[2px] rounded-t bg-primary/70 transition-colors group-hover:bg-primary",style:{height:`${Math.max(y,h.value>0?4:0)}%`}})},h.date)})}),f.jsxs("div",{className:"flex justify-between text-xs text-muted-foreground",children:[f.jsx("span",{children:Mc((c=n[0])==null?void 0:c.date)}),f.jsx("span",{children:Mc((d=n[n.length-1])==null?void 0:d.date)})]})]})}function pw({bucketId:n}){const s=lt({queryKey:Pe.bucketTrend(n),queryFn:()=>nw(n)});if(s.isPending)return f.jsx(Dt,{className:"h-48 w-full"});if(s.isError)return f.jsx(Ut,{error:s.error,onRetry:()=>s.refetch()});const i=s.data,r=i.points.map(y=>({date:y.date,value:y.entered})),c=i.points.map(y=>({date:y.date,value:y.left})),d=i.points.reduce((y,p)=>y+p.entered,0),h=i.points.reduce((y,p)=>y+p.left,0);return f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex flex-wrap gap-6 text-sm",children:[f.jsxs("div",{children:[f.jsx("span",{className:"block text-xs text-muted-foreground",children:"Current size"}),f.jsx("span",{className:"text-lg font-semibold",children:ut(i.size)})]}),f.jsxs("div",{children:[f.jsx("span",{className:"block text-xs text-muted-foreground",children:"Entered (range)"}),f.jsx("span",{className:"text-lg font-semibold",children:ut(d)})]}),f.jsxs("div",{children:[f.jsx("span",{className:"block text-xs text-muted-foreground",children:"Left (range)"}),f.jsx("span",{className:"text-lg font-semibold",children:ut(h)})]})]}),f.jsxs("div",{className:"grid gap-6 lg:grid-cols-2",children:[f.jsxs("div",{className:"space-y-2",children:[f.jsx("span",{className:"text-sm font-medium",children:"Entered over time"}),f.jsx(lh,{data:r,label:"joined"})]}),f.jsxs("div",{className:"space-y-2",children:[f.jsx("span",{className:"text-sm font-medium",children:"Left over time"}),f.jsx(lh,{data:c,label:"left"})]})]})]})}function gw(){var _,E;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(null),[c,d]=Y.useState(null),h=lt({queryKey:Pe.bucketMetrics,queryFn:tw}),y=lt({queryKey:Pe.buckets,queryFn:WE}),p=Sa({mutationFn:j=>aw(j.id,j.enabled),onSuccess:(j,z)=>{n({title:z.enabled?"Bucket enabled":"Bucket disabled"}),d(null),s.invalidateQueries({queryKey:Pe.buckets}),s.invalidateQueries({queryKey:Pe.bucketMetrics})},onError:j=>{n({variant:"error",title:"Update failed",description:j instanceof Xt?j.message:"Unexpected error."}),d(null)}}),m=h.isPending||y.isPending,b=h.isError||y.isError,v=new Map((((_=y.data)==null?void 0:_.buckets)??[]).map(j=>[j.id,j])),S=(((E=h.data)==null?void 0:E.buckets)??[]).map(j=>{const z=v.get(j.bucketId);return{...j,enabled:(z==null?void 0:z.enabled)??!1,kind:(z==null?void 0:z.kind)??"dynamic",timeBased:(z==null?void 0:z.timeBased)??!1}}),R=S.find(j=>j.bucketId===i)??null;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Buckets",description:"Real-time, code-defined membership. Sizes, enter/leave trends, and which journeys each bucket feeds — observe only, authoring stays in code."}),m?f.jsx(Ri,{}):b?f.jsx(Ut,{error:h.error??y.error,onRetry:()=>{h.refetch(),y.refetch()}}):S.length===0?f.jsx(ol,{title:"No buckets registered",description:"Buckets are real-time, code-defined audiences (defineBucket()). Add one to your app and members appear here as events arrive.",action:f.jsxs(f.Fragment,{children:[f.jsx(xi,{href:ll.buckets,children:"How to create a bucket"}),f.jsx(xi,{href:ll.recipes,variant:"ghost",children:"Recipes"})]})}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Bucket"}),f.jsx(Ce,{className:"text-right",children:"Current size"}),f.jsx(Ce,{className:"text-right",children:"Entered"}),f.jsx(Ce,{className:"text-right",children:"Left"}),f.jsx(Ce,{className:"text-right",children:"Avg dwell"}),f.jsx(Ce,{children:"Freshness"}),f.jsx(Ce,{children:"State"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:S.map(j=>f.jsxs(on,{className:"cursor-pointer","data-state":j.bucketId===i?"selected":void 0,onClick:()=>r(z=>z===j.bucketId?null:j.bucketId),children:[f.jsxs(we,{children:[f.jsx("span",{className:"font-medium",children:j.name}),f.jsx("span",{className:"block text-xs text-muted-foreground",children:j.bucketId})]}),f.jsx(we,{className:"text-right",children:ut(j.size)}),f.jsx(we,{className:"text-right",children:ut(j.entered)}),f.jsx(we,{className:"text-right",children:ut(j.left)}),f.jsx(we,{className:"text-right text-muted-foreground",children:_h(j.avgDwellSecs)}),f.jsx(we,{children:f.jsx(vw,{timeBased:j.timeBased})}),f.jsx(we,{children:f.jsx(ft,{variant:j.enabled?"default":"secondary",children:j.enabled?"Enabled":"Disabled"})}),f.jsx(we,{className:"text-right",children:f.jsx(Ke,{variant:"outline",size:"sm",onClick:z=>{z.stopPropagation(),d(j)},children:j.enabled?"Disable":"Enable"})})]},j.bucketId))})]})}),R?f.jsxs("div",{className:"space-y-6",children:[f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"text-base",children:[R.name," — feeds journeys"]})}),f.jsx(On,{children:f.jsx(bw,{bucketId:R.bucketId})})]}),f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"text-base",children:[R.name," — enter / leave over time"]})}),f.jsx(On,{children:f.jsx(pw,{bucketId:R.bucketId})})]})]}):null,f.jsx(_i,{open:c!==null,onClose:()=>d(null),onConfirm:()=>c&&p.mutate({id:c.bucketId,enabled:!c.enabled}),title:c!=null&&c.enabled?"Disable this bucket?":"Enable this bucket?",description:c!=null&&c.enabled?"Membership will stop being recomputed; in-flight members stay until they leave.":"Matching users will start being added to this bucket again.",confirmLabel:c!=null&&c.enabled?"Disable":"Enable",destructive:c==null?void 0:c.enabled,loading:p.isPending})]})}function vw({timeBased:n}){return n?f.jsx(ft,{variant:"outline",title:"Time-based — leaves lag the reconcile cron",children:"Building"}):f.jsx(ft,{variant:"outline",title:"Event-driven — transitions in real time",children:"Live"})}function bw({bucketId:n}){const s=lt({queryKey:Pe.bucket(n),queryFn:()=>ew(n)});if(s.isPending)return f.jsx(Dt,{className:"h-10 w-full"});if(s.isError)return f.jsx(Ut,{error:s.error,onRetry:()=>s.refetch()});const i=s.data.bucket.feedsJourneys,r=mw(s.data.bucket.maxDwell);return f.jsxs("div",{className:"space-y-3",children:[r?f.jsxs(ft,{variant:"outline",title:"Members are force-removed maxDwell after joining, regardless of criteria.",children:["Time-boxed · ",r]}):null,i.length===0?f.jsxs("div",{className:"space-y-1 text-sm text-muted-foreground",children:[f.jsxs("p",{children:["No journeys are bound to this bucket's transitions yet. Bind one to THIS bucket with the typed refs"," ",f.jsx("code",{className:"text-xs",children:"bucket.entered"})," /"," ",f.jsx("code",{className:"text-xs",children:"bucket.left"})," (e.g."," ",f.jsx("code",{className:"text-xs",children:"{ trigger: { event: bucket.entered } }"}),"), or colocate a reaction with"," ",f.jsx("code",{className:"text-xs",children:'bucket.on("enter", ...)'}),"."]}),f.jsxs("p",{children:["To react to ANY bucket, use the generic"," ",f.jsx("code",{className:"text-xs",children:"Events.BUCKET_ENTERED"})," /"," ",f.jsx("code",{className:"text-xs",children:"Events.BUCKET_LEFT"})," constants."]})]}):f.jsx("div",{className:"flex flex-wrap gap-2",children:i.map(c=>f.jsxs(ft,{variant:c.owned?"default":"secondary",title:c.trigger,children:[c.name,c.owned?f.jsx("span",{className:"ml-1.5 text-[10px] uppercase tracking-wide opacity-70",children:"owned"}):null]},`${c.id}-${c.trigger}`))})]})}const xw={delivered:{variant:"outline",className:"border-emerald-500/40 text-emerald-600 dark:text-emerald-400"},opened:{variant:"outline",className:"border-sky-500/40 text-sky-600 dark:text-sky-400"},clicked:{variant:"outline",className:"border-violet-500/40 text-violet-600 dark:text-violet-400"},sent:{variant:"secondary"},rendered:{variant:"secondary"},queued:{variant:"secondary"},bounced:{variant:"destructive"},complained:{variant:"destructive"},failed:{variant:"destructive"}};function Kc({status:n}){const s=xw[n]??{variant:"outline"};return f.jsx(ft,{variant:s.variant,className:Je("capitalize",s.className),children:n})}function pb({open:n,onClose:s,title:i,description:r,children:c,className:d}){return Y.useEffect(()=>{if(!n)return;const h=y=>{y.key==="Escape"&&s()};return document.addEventListener("keydown",h),()=>document.removeEventListener("keydown",h)},[n,s]),n?f.jsxs("div",{className:"fixed inset-0 z-50",children:[f.jsx("button",{type:"button","aria-label":"Close panel",className:"absolute inset-0 bg-black/50",onClick:s}),f.jsxs("div",{role:"dialog","aria-modal":"true",className:Je("absolute inset-y-0 right-0 flex w-full max-w-2xl flex-col border-l bg-card shadow-xl",d),children:[f.jsxs("div",{className:"flex items-start justify-between gap-4 border-b p-6",children:[f.jsxs("div",{className:"space-y-1",children:[f.jsx("h2",{className:"text-lg font-semibold leading-none tracking-tight",children:i}),r?f.jsx("p",{className:"break-all text-sm text-muted-foreground",children:r}):null]}),f.jsx(Ke,{variant:"ghost",size:"icon",className:"-mr-2 -mt-2 h-8 w-8 shrink-0",onClick:s,"aria-label":"Close",children:f.jsx(jh,{className:"h-4 w-4"})})]}),f.jsx("div",{className:"flex-1 overflow-y-auto p-6",children:c})]})]}):null}const Sw={event:Rh,journey:Sh,email:ub};function jw({entry:n}){const s=Sw[n.type],i=n.data,r=n.type==="event"?String(i.event??"event"):n.type==="journey"?`${String(i.journeyId??"journey")} · ${String(i.status??"")}`:String(i.subject??i.templateKey??"email");return f.jsxs("li",{className:"flex gap-3",children:[f.jsx("span",{className:"mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border bg-card",children:f.jsx(s,{className:"h-3.5 w-3.5 text-muted-foreground"})}),f.jsxs("div",{className:"min-w-0 flex-1 pb-3",children:[f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx("p",{className:"truncate text-sm font-medium",children:r}),n.type==="email"&&i.status?f.jsx(Kc,{status:String(i.status)}):null]}),f.jsx("p",{className:"text-xs text-muted-foreground",children:Yn(n.timestamp)})]})]})}function Rw({contactId:n,onClose:s}){var _,E;const i=n!==null,{toast:r}=vs(),c=ps(),[d,h]=Y.useState(!1),y=lt({queryKey:n?Pe.contact(n):["contact","none"],queryFn:()=>lw(n),enabled:i}),p=lt({queryKey:n?Pe.contactActivity(n):["activity","none"],queryFn:()=>iw(n),enabled:i}),m=lt({queryKey:n?Pe.contactTimeline(n):["timeline","none"],queryFn:()=>rw(n),enabled:i}),b=Sa({mutationFn:()=>yb(n,{suppressed:!1}),onSuccess:()=>{r({title:"Contact un-suppressed",description:"They can receive emails again."}),h(!1),n&&c.invalidateQueries({queryKey:Pe.contact(n)}),c.invalidateQueries({queryKey:["suppressions"]})},onError:j=>{r({variant:"error",title:"Un-suppress failed",description:j instanceof Xt?j.message:"Unexpected error."}),h(!1)}}),v=(_=y.data)==null?void 0:_.contact,S=(E=y.data)==null?void 0:E.preferences,R=(S==null?void 0:S.suppressed)||(S==null?void 0:S.unsubscribedAll);return f.jsxs(f.Fragment,{children:[f.jsx(pb,{open:i,onClose:s,title:(v==null?void 0:v.email)??(v==null?void 0:v.externalId)??"Contact",description:v?v.externalId:void 0,children:y.isPending?f.jsxs("div",{className:"space-y-4",children:[f.jsx(Dt,{className:"h-24 w-full"}),f.jsx(Dt,{className:"h-40 w-full"})]}):y.isError?f.jsx(Ut,{error:y.error,onRetry:()=>y.refetch()}):v?f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[S!=null&&S.suppressed?f.jsx(ft,{variant:"destructive",children:"Suppressed"}):null,S!=null&&S.unsubscribedAll?f.jsx(ft,{variant:"destructive",children:"Unsubscribed"}):null,S&&S.bounceCount>0?f.jsxs(ft,{variant:"secondary",children:[S.bounceCount," bounce",S.bounceCount===1?"":"s"]}):null,R?null:f.jsx(ft,{variant:"outline",className:"border-emerald-500/40 text-emerald-600 dark:text-emerald-400",children:"Subscribed"}),R?f.jsx(Ke,{size:"sm",variant:"outline",className:"ml-auto",onClick:()=>h(!0),children:"Un-suppress"}):null]}),f.jsxs("dl",{className:"grid grid-cols-2 gap-4",children:[f.jsxs("div",{children:[f.jsx("dt",{className:"text-xs text-muted-foreground",children:"First seen"}),f.jsx("dd",{className:"text-sm",children:Yn(v.firstSeenAt)})]}),f.jsxs("div",{children:[f.jsx("dt",{className:"text-xs text-muted-foreground",children:"Last seen"}),f.jsx("dd",{className:"text-sm",children:Yn(v.lastSeenAt)})]})]}),f.jsxs("section",{children:[f.jsxs("h3",{className:"mb-3 text-sm font-semibold",children:["Email activity",p.data?` (${p.data.total})`:""]}),p.isPending?f.jsx(Dt,{className:"h-20 w-full"}):p.isError?f.jsx("p",{className:"text-sm text-muted-foreground",children:"Could not load email activity."}):p.data&&p.data.sends.length>0?f.jsx("ul",{className:"space-y-2",children:p.data.sends.slice(0,10).map(j=>f.jsxs("li",{className:"flex items-center justify-between gap-3 rounded-md border p-3",children:[f.jsxs("div",{className:"min-w-0",children:[f.jsx("p",{className:"truncate text-sm font-medium",children:j.subject}),f.jsx("p",{className:"text-xs text-muted-foreground",children:Yn(j.createdAt)})]}),f.jsx(Kc,{status:j.status})]},j.id))}):f.jsx("p",{className:"text-sm text-muted-foreground",children:"No emails sent."})]}),f.jsxs("section",{children:[f.jsx("h3",{className:"mb-3 text-sm font-semibold",children:"Timeline"}),m.isPending?f.jsx(Dt,{className:"h-20 w-full"}):m.isError?f.jsx("p",{className:"text-sm text-muted-foreground",children:"Could not load timeline."}):m.data&&m.data.timeline.length>0?f.jsx("ul",{children:m.data.timeline.map((j,z)=>f.jsx(jw,{entry:j},`${j.type}-${j.timestamp}-${z}`))}):f.jsx("p",{className:"text-sm text-muted-foreground",children:"No activity yet."})]})]}):null}),f.jsx(_i,{open:d,onClose:()=>h(!1),onConfirm:()=>b.mutate(),title:"Un-suppress this contact?",description:"They will be eligible to receive emails again.",confirmLabel:"Un-suppress",loading:b.isPending})]})}function _w(){var p;const[n,s]=Y.useState(""),[i,r]=Y.useState(""),[c,d]=Y.useState(null);Y.useEffect(()=>{const m=window.setTimeout(()=>r(n.trim()),300);return()=>window.clearTimeout(m)},[n]);const h=lt({queryKey:Pe.contacts(i),queryFn:()=>sw(i||void 0),placeholderData:ch}),y=((p=h.data)==null?void 0:p.contacts)??[];return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Contacts",description:"Search contacts and review their full activity timeline."}),f.jsxs("div",{className:"relative max-w-sm",children:[f.jsx(jE,{className:"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"}),f.jsx(zt,{placeholder:"Search by email or external ID…",className:"pl-9",value:n,onChange:m=>s(m.target.value)})]}),h.isPending?f.jsx(Ri,{}):h.isError?f.jsx(Ut,{error:h.error,onRetry:()=>h.refetch()}):y.length===0?f.jsx(ol,{title:"No contacts found",description:i?"No contacts match your search.":"Contacts appear here as events are ingested."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Email"}),f.jsx(Ce,{children:"External ID"}),f.jsx(Ce,{className:"text-right",children:"Last seen"})]})}),f.jsx(fl,{children:y.map(m=>f.jsxs(on,{className:"cursor-pointer",onClick:()=>d(m.id),children:[f.jsx(we,{className:"font-medium",children:m.email??"—"}),f.jsx(we,{className:"text-muted-foreground",children:m.externalId}),f.jsx(we,{className:"text-right text-muted-foreground",children:hw(m.lastSeenAt)})]},m.id))})]})}),f.jsx(Rw,{contactId:c,onClose:()=>d(null)})]})}function Tt({className:n,...s}){return f.jsx("label",{className:Je("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",n),...s})}function Dv(){return`test_${Math.random().toString(36).slice(2,10)}`}function Ew(n){const s=n.trim();if(!s)return{ok:!0,value:{}};try{const i=JSON.parse(s);return i===null||typeof i!="object"||Array.isArray(i)?{ok:!1,error:"Properties must be a JSON object."}:{ok:!0,value:i}}catch(i){return{ok:!1,error:i instanceof Error?i.message:"Invalid JSON."}}}function ww(){const{toast:n}=vs(),[s,i]=Y.useState(""),[r,c]=Y.useState(Dv),[d,h]=Y.useState(""),[y,p]=Y.useState("{}"),[m,b]=Y.useState(null),[v,S]=Y.useState(null),R=lt({queryKey:Pe.journeys,queryFn:mb}),_=Y.useMemo(()=>{var B;const A=new Map;for(const N of((B=R.data)==null?void 0:B.journeys)??[]){const Q=A.get(N.trigger.event)??[];Q.push(N.name),A.set(N.trigger.event,Q)}return Array.from(A,([N,Q])=>({event:N,journeys:Q}))},[R.data]),E=Sa({mutationFn:A=>dw(A),onSuccess:A=>{S(A),n({title:"Event ingested",description:`Stored: ${A.stored} · ${A.exits.length} journey exit(s).`})},onError:A=>{n({variant:"error",title:"Ingest failed",description:A instanceof Xt?A.message:"Unexpected error."})}}),j=()=>{const A=Ew(y);if(!A.ok){b(A.error);return}b(null),E.mutate({event:s.trim(),userId:r.trim(),userEmail:d.trim()||void 0,properties:A.value})},z=!!s.trim()&&!!r.trim()&&!E.isPending;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Debug",description:"Fire events into the ingest pipeline — exactly what real events do. Trigger journeys locally without a PostHog tunnel."}),f.jsxs("div",{className:"grid gap-6 lg:grid-cols-3",children:[f.jsxs(Cn,{className:"lg:col-span-2",children:[f.jsxs(Mn,{children:[f.jsx(An,{className:"text-base",children:"Send a test event"}),f.jsxs(Lr,{children:["POSTs to ",f.jsx("code",{className:"text-xs",children:"/v1/ingest"}),". Journeys whose trigger matches will enrol this user."]})]}),f.jsxs(On,{className:"space-y-4",children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"event",children:"Event name"}),f.jsx(zt,{id:"event",list:"event-presets",placeholder:"e.g. user.created",value:s,onChange:A=>i(A.target.value)}),f.jsx("datalist",{id:"event-presets",children:_.map(A=>f.jsx("option",{value:A.event},A.event))})]}),f.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"userId",children:"User ID"}),f.jsxs("div",{className:"flex gap-2",children:[f.jsx(zt,{id:"userId",placeholder:"test_user",value:r,onChange:A=>c(A.target.value)}),f.jsx(Ke,{variant:"outline",size:"icon",title:"Generate a random test user ID",onClick:()=>c(Dv()),children:f.jsx(TE,{className:"h-4 w-4"})})]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"userEmail",children:"User email (optional)"}),f.jsx(zt,{id:"userEmail",type:"email",placeholder:"you@example.com",value:d,onChange:A=>h(A.target.value)})]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"props",children:"Properties (JSON)"}),f.jsx("textarea",{id:"props",spellCheck:!1,className:"flex min-h-[140px] w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",value:y,onChange:A=>{p(A.target.value),m&&b(null)}}),m?f.jsx("p",{className:"text-xs text-destructive",children:m}):null]}),f.jsx("div",{className:"flex justify-end",children:f.jsxs(Ke,{onClick:j,disabled:!z,children:[f.jsx(Rh,{className:"h-4 w-4"}),E.isPending?"Sending…":"Send event"]})})]})]}),f.jsxs("div",{className:"space-y-6",children:[f.jsxs(Cn,{children:[f.jsxs(Mn,{children:[f.jsx(An,{className:"text-base",children:"Journey triggers"}),f.jsx(Lr,{children:"Events that enrol a registered journey. Click to fill the form."})]}),f.jsx(On,{children:_.length===0?f.jsx("p",{className:"text-sm text-muted-foreground",children:"No journeys registered yet. Any event name still works — define a journey to see triggers here."}):f.jsx("div",{className:"flex flex-wrap gap-2",children:_.map(A=>f.jsx(Ke,{variant:"outline",size:"sm",title:`Triggers: ${A.journeys.join(", ")}`,onClick:()=>i(A.event),children:A.event},A.event))})})]}),v?f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"flex items-center gap-2 text-base",children:[f.jsx(rb,{className:"h-4 w-4 text-green-500"}),"Result"]})}),f.jsxs(On,{className:"space-y-3",children:[f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx(ft,{variant:v.stored?"default":"secondary",children:v.stored?"Stored":"Not stored"}),f.jsxs("span",{className:"text-sm text-muted-foreground",children:[v.exits.length," journey exit(s)"]})]}),f.jsx("pre",{className:"max-h-48 overflow-auto rounded-md border bg-muted/30 p-3 text-xs",children:JSON.stringify(v,null,2)}),f.jsxs("p",{className:"text-sm text-muted-foreground",children:["See the effect in"," ",f.jsx(bi,{to:"/journeys",className:"underline underline-offset-4",children:"Journeys"})," ","or the"," ",f.jsx(bi,{to:"/contacts",className:"underline underline-offset-4",children:"contact's timeline"}),". Make sure the worker is running."]})]})]}):null]})]})]})}function Tw({step:n,base:s}){const i=s>0?n.value/s:0,r=i*100;return f.jsxs("div",{className:"space-y-1",children:[f.jsxs("div",{className:"flex items-center justify-between text-sm",children:[f.jsx("span",{className:"font-medium",children:n.label}),f.jsxs("span",{className:"text-muted-foreground",children:[ut(n.value),s>0?f.jsxs("span",{className:"ml-1 text-xs",children:["(",Si(i),")"]}):null]})]}),f.jsx("div",{className:"h-2 overflow-hidden rounded-full bg-muted",children:f.jsx("div",{className:"h-full rounded-full bg-primary",style:{width:`${Math.min(r,100)}%`}})})]})}function Nw({journeyId:n}){const s=lt({queryKey:Pe.journeyFunnel(n),queryFn:()=>IE(n)});if(s.isPending)return f.jsx(Dt,{className:"h-48 w-full"});if(s.isError)return f.jsx(Ut,{error:s.error,onRetry:()=>s.refetch()});const i=s.data,r=[{label:"Enrolled",value:i.enrolled},{label:"Email sent",value:i.emailSent},{label:"Opened",value:i.emailOpened},{label:"Clicked",value:i.emailClicked},{label:"Completed",value:i.completed}];return f.jsxs("div",{className:"space-y-4",children:[f.jsx("div",{className:"space-y-3",children:r.map(c=>f.jsx(Tw,{step:c,base:i.enrolled},c.label))}),f.jsxs("div",{className:"flex gap-4 border-t pt-3 text-sm text-muted-foreground",children:[f.jsxs("span",{children:["Failed: ",ut(i.failed)]}),f.jsxs("span",{children:["Exited: ",ut(i.exited)]})]})]})}function Cw(){var _,E;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(null),[c,d]=Y.useState(null),h=lt({queryKey:Pe.journeyMetrics,queryFn:FE}),y=lt({queryKey:Pe.journeys,queryFn:mb}),p=Sa({mutationFn:j=>$E(j.id,j.enabled),onSuccess:(j,z)=>{n({title:z.enabled?"Journey enabled":"Journey disabled"}),d(null),s.invalidateQueries({queryKey:Pe.journeys}),s.invalidateQueries({queryKey:Pe.journeyMetrics})},onError:j=>{n({variant:"error",title:"Update failed",description:j instanceof Xt?j.message:"Unexpected error."}),d(null)}}),m=h.isPending||y.isPending,b=h.isError||y.isError,v=new Map((((_=y.data)==null?void 0:_.journeys)??[]).map(j=>[j.id,j.enabled])),S=(((E=h.data)==null?void 0:E.journeys)??[]).map(j=>({...j,enabled:v.get(j.journeyId)??!1})),R=S.find(j=>j.journeyId===i)??null;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Journeys",description:"Lifecycle journeys, completion rates, and per-journey funnels."}),m?f.jsx(Ri,{}):b?f.jsx(Ut,{error:h.error??y.error,onRetry:()=>{h.refetch(),y.refetch()}}):S.length===0?f.jsx(ol,{title:"No journeys registered",description:"Journeys are defined in code with defineJourney(). Add one to your app, then fire its trigger event from Debug to see it here.",action:f.jsxs(f.Fragment,{children:[f.jsx(xi,{href:ll.journeys,children:"How to create a journey"}),f.jsx(xi,{href:ll.recipes,variant:"ghost",children:"Recipes"})]})}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Journey"}),f.jsx(Ce,{className:"text-right",children:"Enrolled"}),f.jsx(Ce,{className:"text-right",children:"Active"}),f.jsx(Ce,{className:"text-right",children:"Completed"}),f.jsx(Ce,{className:"text-right",children:"Completion"}),f.jsx(Ce,{className:"text-right",children:"Avg duration"}),f.jsx(Ce,{children:"State"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:S.map(j=>f.jsxs(on,{className:"cursor-pointer","data-state":j.journeyId===i?"selected":void 0,onClick:()=>r(z=>z===j.journeyId?null:j.journeyId),children:[f.jsxs(we,{children:[f.jsx("span",{className:"font-medium",children:j.name}),f.jsx("span",{className:"block text-xs text-muted-foreground",children:j.journeyId})]}),f.jsx(we,{className:"text-right",children:ut(j.enrolled)}),f.jsx(we,{className:"text-right",children:ut(j.active)}),f.jsx(we,{className:"text-right",children:ut(j.completed)}),f.jsx(we,{className:"text-right",children:Si(j.completionRate)}),f.jsx(we,{className:"text-right text-muted-foreground",children:_h(j.avgDurationSecs)}),f.jsx(we,{children:f.jsx(ft,{variant:j.enabled?"default":"secondary",children:j.enabled?"Enabled":"Disabled"})}),f.jsx(we,{className:"text-right",children:f.jsx(Ke,{variant:"outline",size:"sm",onClick:z=>{z.stopPropagation(),d(j)},children:j.enabled?"Disable":"Enable"})})]},j.journeyId))})]})}),R?f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"text-base",children:[R.name," — funnel"]})}),f.jsx(On,{children:f.jsx(Nw,{journeyId:R.journeyId})})]}):null,f.jsx(_i,{open:c!==null,onClose:()=>d(null),onConfirm:()=>c&&p.mutate({id:c.journeyId,enabled:!c.enabled}),title:c!=null&&c.enabled?"Disable this journey?":"Enable this journey?",description:c!=null&&c.enabled?"New events will stop enrolling users into this journey.":"New matching events will start enrolling users into this journey.",confirmLabel:c!=null&&c.enabled?"Disable":"Enable",destructive:c==null?void 0:c.enabled,loading:p.isPending})]})}function Gs({label:n,value:s,hint:i,icon:r}){return f.jsxs(Cn,{children:[f.jsxs(Mn,{className:"flex flex-row items-center justify-between space-y-0 pb-2",children:[f.jsx(An,{className:"text-sm font-medium text-muted-foreground",children:n}),r?f.jsx(r,{className:"h-4 w-4 text-muted-foreground"}):null]}),f.jsxs(On,{children:[f.jsx("div",{className:"text-2xl font-semibold tracking-tight",children:s}),i?f.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:i}):null]})]})}function Mw(n){return n.totalContacts===0&&n.activeJourneys===0&&n.emailsSent30d===0}function Aw(){return f.jsxs(Cn,{className:"border-primary/30 bg-primary/5",children:[f.jsxs(Mn,{children:[f.jsx(An,{className:"text-base",children:"Welcome to Hogsend Studio"}),f.jsx(Lr,{children:"Studio observes your code-first lifecycle engine. Here's the path to your first running journey."})]}),f.jsxs(On,{className:"space-y-4",children:[f.jsxs("ol",{className:"space-y-1.5 text-sm text-muted-foreground",children:[f.jsxs("li",{children:[f.jsx("span",{className:"font-medium text-foreground",children:"1."})," Define a journey in code and start the worker."]}),f.jsxs("li",{children:[f.jsx("span",{className:"font-medium text-foreground",children:"2."})," Fire its trigger event to enrol a test user."]}),f.jsxs("li",{children:[f.jsx("span",{className:"font-medium text-foreground",children:"3."})," Watch enrolments, sends, and exits land here."]})]}),f.jsxs("div",{className:"flex flex-wrap gap-2",children:[f.jsxs(bi,{to:"/debug",className:Je(xh({size:"sm"})),children:[f.jsx(Rh,{className:"h-4 w-4"}),"Send a test event"]}),f.jsx(xi,{href:ll.quickstart,children:"Quickstart"}),f.jsx(xi,{href:ll.journeys,variant:"ghost",children:"Create a journey"})]})]})]})}function Ow(){const n=lt({queryKey:Pe.overview,queryFn:GE});return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Overview",description:"Key delivery and engagement metrics at a glance."}),n.isPending?f.jsx(qE,{count:6}):n.isError?f.jsx(Ut,{error:n.error,onRetry:()=>n.refetch()}):f.jsxs(f.Fragment,{children:[Mw(n.data)?f.jsx(Aw,{}):null,f.jsxs("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:[f.jsx(Gs,{label:"Total contacts",value:ut(n.data.totalContacts),icon:db}),f.jsx(Gs,{label:"Active journeys",value:ut(n.data.activeJourneys),hint:"Instances active or waiting",icon:Sh}),f.jsx(Gs,{label:"Sent (24h)",value:ut(n.data.emailsSent24h),icon:Yr}),f.jsx(Gs,{label:"Sent (7d)",value:ut(n.data.emailsSent7d),icon:zv}),f.jsx(Gs,{label:"Sent (30d)",value:ut(n.data.emailsSent30d),icon:zv}),f.jsx(Gs,{label:"Bounce rate (30d)",value:Si(n.data.bounceRate30d),hint:"Bounced / sent in last 30 days",icon:cb})]}),f.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:f.jsx(Gs,{label:"Unsubscribe rate",value:Si(n.data.unsubscribeRate),hint:"Unsubscribed / total preferences",icon:ME})})]})]})}const Qc=Y.forwardRef(({className:n,children:s,...i},r)=>f.jsxs("div",{className:"relative",children:[f.jsx("select",{ref:r,className:Je("flex h-9 w-full appearance-none rounded-md border border-input bg-transparent px-3 py-1 pr-8 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",n),...i,children:s}),f.jsx(Z_,{className:"pointer-events-none absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"})]}));Qc.displayName="Select";const zw={queued:fb,sent:Yr,delivered:P_,opened:nE,clicked:vE,bounced:cb,complained:ib,failed:ob};function Dw({event:n}){const s=zw[n.type]??fb;return f.jsxs("li",{className:"flex gap-3",children:[f.jsxs("div",{className:"flex flex-col items-center",children:[f.jsx("span",{className:"flex h-7 w-7 items-center justify-center rounded-full border bg-card",children:f.jsx(s,{className:"h-3.5 w-3.5 text-muted-foreground"})}),f.jsx("span",{className:"w-px flex-1 bg-border"})]}),f.jsxs("div",{className:"pb-4",children:[f.jsx("p",{className:"text-sm font-medium capitalize",children:n.type}),f.jsx("p",{className:"text-xs text-muted-foreground",children:Yn(n.timestamp)}),n.url?f.jsx("p",{className:"mt-0.5 break-all text-xs text-muted-foreground",children:n.url}):null]})]})}function $a({label:n,value:s}){return f.jsxs("div",{className:"space-y-0.5",children:[f.jsx("dt",{className:"text-xs text-muted-foreground",children:n}),f.jsx("dd",{className:"break-all text-sm",children:s})]})}function Uw({emailId:n,onClose:s}){const i=n!==null,{toast:r}=vs(),c=ps(),[d,h]=Y.useState(!1),y=lt({queryKey:n?Pe.email(n):["email","none"],queryFn:()=>YE(n),enabled:i}),p=Sa({mutationFn:()=>KE(n),onSuccess:()=>{r({title:"Resend queued",description:"A fresh send was queued from this template."}),h(!1),c.invalidateQueries({queryKey:["emails"]}),n&&c.invalidateQueries({queryKey:Pe.email(n)})},onError:v=>{r({variant:"error",title:"Resend failed",description:v instanceof Xt?v.message:"Unexpected error."}),h(!1)}}),m=y.data,b=m&&["failed","bounced"].includes(m.email.status);return f.jsxs(f.Fragment,{children:[f.jsx(pb,{open:i,onClose:s,title:"Email send",description:m==null?void 0:m.email.subject,children:y.isPending?f.jsxs("div",{className:"space-y-4",children:[f.jsx(Dt,{className:"h-24 w-full"}),f.jsx(Dt,{className:"h-40 w-full"})]}):y.isError?f.jsx(Ut,{error:y.error,onRetry:()=>y.refetch()}):m?f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex items-center justify-between gap-2",children:[f.jsx(Kc,{status:m.email.status}),b?f.jsxs(Ke,{size:"sm",variant:"outline",onClick:()=>h(!0),children:[f.jsx(Yr,{className:"h-4 w-4"}),"Resend"]}):null]}),f.jsxs("dl",{className:"grid grid-cols-2 gap-4",children:[f.jsx($a,{label:"To",value:m.email.toEmail}),f.jsx($a,{label:"From",value:m.email.fromEmail}),f.jsx($a,{label:"Template",value:m.email.templateKey??"—"}),f.jsx($a,{label:"Category",value:m.email.category??"—"}),f.jsx($a,{label:"Journey",value:m.email.journeyId??"—"}),f.jsx($a,{label:"User",value:m.email.userId??"—"}),f.jsx($a,{label:"Resend ID",value:m.email.resendId??"—"}),f.jsx($a,{label:"Created",value:Yn(m.email.createdAt)})]}),f.jsxs("section",{children:[f.jsx("h3",{className:"mb-3 text-sm font-semibold",children:"Timeline"}),m.events.length===0?f.jsx("p",{className:"text-sm text-muted-foreground",children:"No delivery events recorded yet."}):f.jsx("ul",{className:"[&>li:last-child>div:first-child>span:last-child]:hidden",children:m.events.map((v,S)=>f.jsx(Dw,{event:v},`${v.type}-${v.timestamp}-${S}`))})]}),f.jsxs("section",{children:[f.jsxs("h3",{className:"mb-3 text-sm font-semibold",children:["Tracked links (",m.trackedLinks.length,")"]}),m.trackedLinks.length===0?f.jsx("p",{className:"text-sm text-muted-foreground",children:"No tracked links in this email."}):f.jsx("ul",{className:"space-y-2",children:m.trackedLinks.map(v=>f.jsxs("li",{className:"flex items-center justify-between gap-3 rounded-md border p-3",children:[f.jsx("span",{className:"break-all text-sm",children:v.originalUrl}),f.jsxs("span",{className:"shrink-0 text-xs text-muted-foreground",children:[v.clickCount," click",v.clickCount===1?"":"s"]})]},v.id))})]})]}):null}),f.jsx(_i,{open:d,onClose:()=>h(!1),onConfirm:()=>p.mutate(),title:"Resend this email?",description:"A new send will be queued from the original template and recipient.",confirmLabel:"Resend",loading:p.isPending})]})}const kw=["queued","rendered","sent","delivered","opened","clicked","bounced","complained","failed"],Lw=["opened","clicked","bounced","complained"],Tr=25,Uv={templateKey:"",status:"",engagement:"",journeyId:"",userId:"",from:"",to:""};function Bw(){var N,Q;const[n,s]=Y.useState(Uv),[i,r]=Y.useState("createdAt"),[c,d]=Y.useState("desc"),[h,y]=Y.useState(0),[p,m]=Y.useState(null),b=n.from?new Date(`${n.from}T00:00:00`).toISOString():void 0,v=n.to?new Date(`${n.to}T23:59:59`).toISOString():void 0,S={limit:Tr,offset:h,templateKey:n.templateKey||void 0,status:n.status||void 0,engagement:n.engagement||void 0,journeyId:n.journeyId||void 0,userId:n.userId||void 0,from:b,to:v,sort:i,order:c},R=lt({queryKey:Pe.emails(S),queryFn:()=>VE(S),placeholderData:ch});function _(k){s(D=>({...D,...k})),y(0)}function E(k){i===k?d(D=>D==="asc"?"desc":"asc"):(r(k),d("desc")),y(0)}const j=Object.values(n).some(Boolean),z=((N=R.data)==null?void 0:N.total)??0,A=((Q=R.data)==null?void 0:Q.emails)??[];function B({column:k,label:D}){const O=i===k;return f.jsx(Ce,{children:f.jsxs("button",{type:"button",className:"inline-flex items-center gap-1 font-medium hover:text-foreground",onClick:()=>E(k),children:[D,O?c==="asc"?f.jsx(q_,{className:"h-3 w-3"}):f.jsx(k_,{className:"h-3 w-3"}):null]})})}return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Sends",description:"Every email sent — filter, sort, and drill into the timeline."}),f.jsxs("div",{className:"grid gap-3 rounded-lg border p-4 md:grid-cols-3 lg:grid-cols-4",children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-template",children:"Template"}),f.jsx(zt,{id:"f-template",placeholder:"template key",value:n.templateKey,onChange:k=>_({templateKey:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-status",children:"Status"}),f.jsxs(Qc,{id:"f-status",value:n.status,onChange:k=>_({status:k.target.value}),children:[f.jsx("option",{value:"",children:"All"}),kw.map(k=>f.jsx("option",{value:k,children:k},k))]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-engagement",children:"Engagement"}),f.jsxs(Qc,{id:"f-engagement",value:n.engagement,onChange:k=>_({engagement:k.target.value}),children:[f.jsx("option",{value:"",children:"Any"}),Lw.map(k=>f.jsx("option",{value:k,children:k},k))]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-journey",children:"Journey ID"}),f.jsx(zt,{id:"f-journey",placeholder:"journey id",value:n.journeyId,onChange:k=>_({journeyId:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-user",children:"User ID"}),f.jsx(zt,{id:"f-user",placeholder:"user id",value:n.userId,onChange:k=>_({userId:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-from",children:"From"}),f.jsx(zt,{id:"f-from",type:"date",value:n.from,onChange:k=>_({from:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-to",children:"To"}),f.jsx(zt,{id:"f-to",type:"date",value:n.to,onChange:k=>_({to:k.target.value})})]}),j?f.jsx("div",{className:"flex items-end",children:f.jsx(Ke,{variant:"ghost",size:"sm",onClick:()=>{s(Uv),y(0)},children:"Clear filters"})}):null]}),R.isPending?f.jsx(Ri,{}):R.isError?f.jsx(Ut,{error:R.error,onRetry:()=>R.refetch()}):A.length===0?f.jsx(ol,{title:"No sends found",description:j?"Try widening or clearing your filters.":"Emails will appear here once your journeys start sending."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Recipient"}),f.jsx(Ce,{children:"Subject"}),f.jsx(Ce,{children:"Template"}),f.jsx(Ce,{children:"Status"}),f.jsx(B,{column:"sentAt",label:"Sent"}),f.jsx(B,{column:"createdAt",label:"Created"})]})}),f.jsx(fl,{children:A.map(k=>f.jsxs(on,{className:"cursor-pointer",onClick:()=>m(k.id),children:[f.jsx(we,{className:"font-medium",children:k.toEmail}),f.jsx(we,{children:yw(k.subject,40)}),f.jsx(we,{className:"text-muted-foreground",children:k.templateKey??"—"}),f.jsx(we,{children:f.jsx(Kc,{status:k.status})}),f.jsx(we,{className:"text-muted-foreground",children:Yn(k.sentAt)}),f.jsx(we,{className:"text-muted-foreground",children:Yn(k.createdAt)})]},k.id))})]})}),z>0?f.jsxs("div",{className:"flex items-center justify-between text-sm text-muted-foreground",children:[f.jsxs("span",{children:[h+1,"–",Math.min(h+Tr,z)," of ",z]}),f.jsxs("div",{className:"flex gap-2",children:[f.jsx(Ke,{variant:"outline",size:"sm",disabled:h===0,onClick:()=>y(k=>Math.max(0,k-Tr)),children:"Previous"}),f.jsx(Ke,{variant:"outline",size:"sm",disabled:h+Tr>=z,onClick:()=>y(k=>k+Tr),children:"Next"})]})]}):null,f.jsx(Uw,{emailId:p,onClose:()=>m(null)})]})}const qw=["read","journey-admin","full-admin"];function Hw(){var A;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(!1),[c,d]=Y.useState(""),[h,y]=Y.useState(["read"]),[p,m]=Y.useState(null),[b,v]=Y.useState(null),S=lt({queryKey:Pe.apiKeys,queryFn:cw}),R=Sa({mutationFn:()=>uw({name:c.trim(),scopes:h}),onSuccess:B=>{r(!1),d(""),y(["read"]),m(B),s.invalidateQueries({queryKey:Pe.apiKeys})},onError:B=>{n({variant:"error",title:"Could not create key",description:B instanceof Xt?B.message:"Unexpected error."})}}),_=Sa({mutationFn:B=>fw(B),onSuccess:()=>{n({title:"API key revoked"}),v(null),s.invalidateQueries({queryKey:Pe.apiKeys})},onError:B=>{n({variant:"error",title:"Revoke failed",description:B instanceof Xt?B.message:"Unexpected error."}),v(null)}});function E(B){y(N=>N.includes(B)?N.filter(Q=>Q!==B):[...N,B])}async function j(B){try{await navigator.clipboard.writeText(B),n({title:"Copied to clipboard"})}catch{n({variant:"error",title:"Copy failed"})}}const z=((A=S.data)==null?void 0:A.keys)??[];return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Settings",description:"API keys for programmatic access to the Hogsend admin API.",action:f.jsxs(Ke,{onClick:()=>r(!0),children:[f.jsx(xE,{className:"h-4 w-4"}),"New API key"]})}),S.isPending?f.jsx(Ri,{}):S.isError?f.jsx(Ut,{error:S.error,onRetry:()=>S.refetch()}):z.length===0?f.jsx(ol,{icon:cE,title:"No API keys",description:"Create a key to authenticate API and webhook requests."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Name"}),f.jsx(Ce,{children:"Prefix"}),f.jsx(Ce,{children:"Scopes"}),f.jsx(Ce,{children:"Last used"}),f.jsx(Ce,{children:"Status"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:z.map(B=>{const N=B.revokedAt!==null;return f.jsxs(on,{children:[f.jsx(we,{className:"font-medium",children:B.name}),f.jsxs(we,{className:"font-mono text-xs text-muted-foreground",children:[B.keyPrefix,"…"]}),f.jsx(we,{children:f.jsx("div",{className:"flex flex-wrap gap-1",children:B.scopes.map(Q=>f.jsx(ft,{variant:"secondary",children:Q},Q))})}),f.jsx(we,{className:"text-muted-foreground",children:Yn(B.lastUsedAt)}),f.jsx(we,{children:N?f.jsx(ft,{variant:"outline",children:"Revoked"}):f.jsx(ft,{variant:"outline",className:"border-emerald-500/40 text-emerald-600 dark:text-emerald-400",children:"Active"})}),f.jsx(we,{className:"text-right",children:N?null:f.jsx(Ke,{variant:"outline",size:"sm",onClick:()=>v(B),children:"Revoke"})})]},B.id)})})]})}),f.jsxs(Hc,{open:i,onClose:()=>r(!1),title:"Create API key",description:"Choose a descriptive name and the scopes this key needs.",footer:f.jsxs(f.Fragment,{children:[f.jsx(Ke,{variant:"outline",onClick:()=>r(!1),children:"Cancel"}),f.jsx(Ke,{onClick:()=>R.mutate(),disabled:!c.trim()||h.length===0||R.isPending,children:R.isPending?"Creating…":"Create key"})]}),children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"key-name",children:"Name"}),f.jsx(zt,{id:"key-name",placeholder:"CI ingest key",value:c,onChange:B=>d(B.target.value)})]}),f.jsxs("div",{className:"space-y-2",children:[f.jsx(Tt,{children:"Scopes"}),f.jsx("div",{className:"flex flex-col gap-2",children:qw.map(B=>f.jsxs("label",{className:"flex items-center gap-2 text-sm",htmlFor:`scope-${B}`,children:[f.jsx("input",{id:`scope-${B}`,type:"checkbox",className:"h-4 w-4 rounded border-input",checked:h.includes(B),onChange:()=>E(B)}),B]},B))})]})]}),f.jsx(Hc,{open:p!==null,onClose:()=>m(null),title:"API key created",description:"Copy this key now — it will not be shown again.",footer:f.jsx(Ke,{onClick:()=>m(null),children:"Done"}),children:p?f.jsxs("div",{className:"space-y-2",children:[f.jsx(Tt,{children:"Secret key"}),f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx("code",{className:"flex-1 break-all rounded-md border bg-muted px-3 py-2 font-mono text-xs",children:p.key}),f.jsx(Ke,{variant:"outline",size:"icon",onClick:()=>j(p.key),"aria-label":"Copy key",children:f.jsx(eE,{className:"h-4 w-4"})})]})]}):null}),f.jsx(_i,{open:b!==null,onClose:()=>v(null),onConfirm:()=>b&&_.mutate(b.id),title:"Revoke this API key?",description:b?`"${b.name}" will stop working immediately. This cannot be undone.`:void 0,confirmLabel:"Revoke",destructive:!0,loading:_.isPending})]})}const Qw=[{value:"",label:"All"},{value:"bounced",label:"Bounced"},{value:"unsubscribed",label:"Unsubscribed"},{value:"complained",label:"Complained"}];function Gw({row:n}){return f.jsxs("div",{className:"flex flex-wrap gap-1",children:[n.unsubscribedAll?f.jsx(ft,{variant:"destructive",children:"Unsubscribed"}):null,n.bounceCount>0?f.jsxs(ft,{variant:"secondary",children:[ut(n.bounceCount)," bounce"]}):null,n.suppressed&&n.bounceCount===0&&!n.unsubscribedAll?f.jsx(ft,{variant:"destructive",children:"Complained"}):null,n.suppressed&&!n.unsubscribedAll&&n.bounceCount>0?f.jsx(ft,{variant:"destructive",children:"Suppressed"}):null]})}function Vw(){var m;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(""),[c,d]=Y.useState(null),h=lt({queryKey:Pe.suppressions(i),queryFn:()=>ow(i||void 0),placeholderData:ch}),y=Sa({mutationFn:b=>yb(b.userId,{suppressed:!1,unsubscribedAll:!1}),onSuccess:()=>{n({title:"Recipient restored",description:"They can receive emails again."}),d(null),s.invalidateQueries({queryKey:["suppressions"]})},onError:b=>{n({variant:"error",title:"Un-suppress failed",description:b instanceof Xt?b.message:"Unexpected error."}),d(null)}}),p=((m=h.data)==null?void 0:m.suppressions)??[];return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Suppressions",description:"Bounced, unsubscribed, and complained recipients."}),f.jsxs("div",{className:"flex max-w-xs flex-col gap-1.5",children:[f.jsx(Tt,{htmlFor:"supp-type",children:"Type"}),f.jsx(Qc,{id:"supp-type",value:i,onChange:b=>r(b.target.value),children:Qw.map(b=>f.jsx("option",{value:b.value,children:b.label},b.value))})]}),h.isPending?f.jsx(Ri,{}):h.isError?f.jsx(Ut,{error:h.error,onRetry:()=>h.refetch()}):p.length===0?f.jsx(ol,{title:"No suppressions",description:"Recipients who bounce, unsubscribe, or complain appear here."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Email"}),f.jsx(Ce,{children:"User ID"}),f.jsx(Ce,{children:"Reason"}),f.jsx(Ce,{className:"text-right",children:"Suppressed"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:p.map(b=>f.jsxs(on,{children:[f.jsx(we,{className:"font-medium",children:b.email}),f.jsx(we,{className:"text-muted-foreground",children:b.userId}),f.jsx(we,{children:f.jsx(Gw,{row:b})}),f.jsx(we,{className:"text-right text-muted-foreground",children:Yn(b.suppressedAt)}),f.jsx(we,{className:"text-right",children:f.jsx(Ke,{variant:"outline",size:"sm",onClick:()=>d(b),children:"Un-suppress"})})]},b.id))})]})}),f.jsx(_i,{open:c!==null,onClose:()=>d(null),onConfirm:()=>c&&y.mutate(c),title:"Un-suppress this recipient?",description:c?`${c.email} will be eligible to receive emails again.`:void 0,confirmLabel:"Un-suppress",loading:y.isPending})]})}function Sc({label:n,value:s}){return f.jsxs("div",{className:"rounded-md border p-3",children:[f.jsx("p",{className:"text-xs text-muted-foreground",children:n}),f.jsx("p",{className:"text-lg font-semibold",children:s})]})}function Yw({template:n}){const{toast:s}=vs(),[i,r]=Y.useState(!1),[c,d]=Y.useState(""),h=lt({queryKey:Pe.templatePreview(n.key),queryFn:()=>XE(n.key)}),y=lt({queryKey:Pe.templateReport(n.key),queryFn:()=>JE(n.key),retry:(b,v)=>!(v instanceof Xt&&v.status===404)&&b<2}),p=Sa({mutationFn:()=>ZE(n.key,c),onSuccess:b=>{s({title:"Test email sent",description:`Status: ${b.status}.`}),r(!1),d("")},onError:b=>{s({variant:"error",title:"Send-test failed",description:b instanceof Xt?b.message:"Unexpected error."})}}),m=y.isError&&y.error instanceof Xt&&y.error.status===404;return f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex items-start justify-between gap-4",children:[f.jsxs("div",{className:"space-y-1",children:[f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx("h2",{className:"text-xl font-semibold tracking-tight",children:n.key}),n.category?f.jsx(ft,{variant:"secondary",children:n.category}):null]}),f.jsx("p",{className:"text-sm text-muted-foreground",children:n.defaultSubject})]}),f.jsxs(Ke,{onClick:()=>r(!0),children:[f.jsx(Yr,{className:"h-4 w-4"}),"Send test"]})]}),f.jsxs("section",{className:"space-y-3",children:[f.jsx("h3",{className:"text-sm font-semibold",children:"Performance"}),y.isPending?f.jsx(Dt,{className:"h-28 w-full"}):m?f.jsx("p",{className:"rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground",children:"This template has never been sent."}):y.isError?f.jsx(Ut,{error:y.error,onRetry:()=>y.refetch()}):y.data?f.jsxs(f.Fragment,{children:[f.jsxs("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-4",children:[f.jsx(Sc,{label:"Sent",value:ut(y.data.totals.sent)}),f.jsx(Sc,{label:"Delivered",value:ut(y.data.totals.delivered)}),f.jsx(Sc,{label:"Open rate",value:Si(y.data.totals.openRate)}),f.jsx(Sc,{label:"Click rate",value:Si(y.data.totals.clickRate)})]}),f.jsxs(Cn,{children:[f.jsx(Mn,{className:"pb-2",children:f.jsx(An,{className:"text-sm font-medium text-muted-foreground",children:"Sends over time"})}),f.jsx(On,{children:f.jsx(lh,{label:"sent",data:y.data.series.map(b=>({date:b.date,value:b.sent}))})})]})]}):null]}),f.jsxs("section",{className:"space-y-3",children:[f.jsx("h3",{className:"text-sm font-semibold",children:"Preview"}),h.isPending?f.jsx(Dt,{className:"h-96 w-full"}):h.isError?f.jsx(Ut,{error:h.error,onRetry:()=>h.refetch()}):h.data?f.jsx("div",{className:"overflow-hidden rounded-lg border bg-white",children:f.jsx("iframe",{title:`${n.key} preview`,srcDoc:h.data.html,sandbox:"",className:"h-[600px] w-full"})}):null]}),f.jsx(Hc,{open:i,onClose:()=>r(!1),title:"Send a test email",description:`Sends "${n.key}" with example props.`,footer:f.jsxs(f.Fragment,{children:[f.jsx(Ke,{variant:"outline",onClick:()=>r(!1),children:"Cancel"}),f.jsx(Ke,{onClick:()=>p.mutate(),disabled:!c||p.isPending,children:p.isPending?"Sending…":"Send test"})]}),children:f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"test-to",children:"Recipient email"}),f.jsx(zt,{id:"test-to",type:"email",placeholder:"you@example.com",value:c,onChange:b=>d(b.target.value)})]})})]})}function Kw(){var d;const n=lt({queryKey:Pe.templates,queryFn:PE}),[s,i]=Y.useState(null),r=((d=n.data)==null?void 0:d.templates)??[];Y.useEffect(()=>{const h=r[0];s===null&&h&&i(h.key)},[s,r]);const c=r.find(h=>h.key===s)??null;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Templates",description:"Catalog, per-template stats, live previews, and send-test."}),n.isPending?f.jsx(Dt,{className:"h-96 w-full"}):n.isError?f.jsx(Ut,{error:n.error,onRetry:()=>n.refetch()}):r.length===0?f.jsx(ol,{title:"No templates registered",description:"Templates appear here once they're added to your email registry."}):f.jsxs("div",{className:"grid gap-6 lg:grid-cols-[260px_1fr]",children:[f.jsx("nav",{className:"space-y-1",children:r.map(h=>f.jsxs("button",{type:"button",onClick:()=>i(h.key),className:Je("w-full rounded-md px-3 py-2 text-left text-sm transition-colors",h.key===s?"bg-accent text-accent-foreground":"text-muted-foreground hover:bg-accent hover:text-accent-foreground"),children:[f.jsx("span",{className:"block font-medium",children:h.key}),h.category?f.jsx("span",{className:"block text-xs opacity-70",children:h.category}):null]},h.key))}),c?f.jsx(Yw,{template:c},c.key):null]})]})}const Kn=s2({component:BE}),Pw=ja({getParentRoute:()=>Kn,path:"/",component:Ow}),Xw=ja({getParentRoute:()=>Kn,path:"/sends",component:Bw}),Zw=ja({getParentRoute:()=>Kn,path:"/templates",component:Kw}),Jw=ja({getParentRoute:()=>Kn,path:"/journeys",component:Cw}),Fw=ja({getParentRoute:()=>Kn,path:"/buckets",component:gw}),Iw=ja({getParentRoute:()=>Kn,path:"/contacts",component:_w}),$w=ja({getParentRoute:()=>Kn,path:"/suppressions",component:Vw}),Ww=ja({getParentRoute:()=>Kn,path:"/settings",component:Hw}),eT=ja({getParentRoute:()=>Kn,path:"/debug",component:ww}),tT=Kn.addChildren([Pw,Xw,Zw,Jw,Fw,Iw,$w,eT,Ww]),nT=y2({routeTree:tT,basepath:"/studio",defaultPreload:"intent"}),gb=document.getElementById("root");if(!gb)throw new Error("Root element #root not found");_2.createRoot(gb).render(f.jsx(Y.StrictMode,{children:f.jsx(yS,{client:UE,children:f.jsx(DE,{children:f.jsx(A_,{children:f.jsx(v2,{router:nT})})})})}));
|
|
250
|
+
*/const zE=[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]],Rh=ze("Zap",zE),hb=Y.createContext(null);function DE({children:n}){const[s,i]=Y.useState([]),r=Y.useRef(0),c=Y.useCallback(y=>{i(p=>p.filter(m=>m.id!==y))},[]),d=Y.useCallback(y=>{const p=++r.current,m={id:p,title:y.title,description:y.description,variant:y.variant??"success"};i(b=>[...b,m]),window.setTimeout(()=>c(p),5e3)},[c]),h=Y.useMemo(()=>({toast:d}),[d]);return f.jsxs(hb.Provider,{value:h,children:[n,f.jsx("div",{className:"pointer-events-none fixed bottom-4 right-4 z-[60] flex w-full max-w-sm flex-col gap-2",children:s.map(y=>f.jsxs("div",{className:Je("pointer-events-auto flex items-start gap-3 rounded-md border bg-card p-4 shadow-lg",y.variant==="error"&&"border-destructive/40"),children:[y.variant==="success"?f.jsx(rb,{className:"mt-0.5 h-5 w-5 shrink-0 text-emerald-500"}):f.jsx(ob,{className:"mt-0.5 h-5 w-5 shrink-0 text-destructive"}),f.jsxs("div",{className:"flex-1 space-y-0.5",children:[f.jsx("p",{className:"text-sm font-medium",children:y.title}),y.description?f.jsx("p",{className:"text-sm text-muted-foreground",children:y.description}):null]}),f.jsx("button",{type:"button","aria-label":"Dismiss",className:"text-muted-foreground transition-colors hover:text-foreground",onClick:()=>c(y.id),children:f.jsx(jh,{className:"h-4 w-4"})})]},y.id))})]})}function vs(){const n=Y.useContext(hb);if(!n)throw new Error("useToast must be used within a ToastProvider");return n}const UE=new hS({defaultOptions:{queries:{staleTime:3e4,retry:(n,s)=>s instanceof Xt&&[401,403].includes(s.status)?!1:n<2,refetchOnWindowFocus:!1}}}),wr="https://docs.hogsend.com",ll={docs:wr,quickstart:`${wr}/docs/getting-started`,recipes:`${wr}/docs/recipes`,journeys:`${wr}/docs/guides/journeys`,buckets:`${wr}/docs/guides/buckets`},kE=[{label:"Overview",path:"/",icon:fE},{label:"Sends",path:"/sends",icon:Yr},{label:"Templates",path:"/templates",icon:ub},{label:"Journeys",path:"/journeys",icon:Sh},{label:"Buckets",path:"/buckets",icon:Y_},{label:"Contacts",path:"/contacts",icon:db},{label:"Suppressions",path:"/suppressions",icon:ib},{label:"Debug",path:"/debug",icon:sE},{label:"Settings",path:"/settings",icon:EE}];function LE(){const n=b2({select:s=>s.location.pathname});return f.jsxs("aside",{className:"flex h-full w-60 flex-col border-r bg-card",children:[f.jsx("div",{className:"flex h-14 items-center border-b px-5",children:f.jsx("span",{className:"text-sm font-semibold tracking-tight",children:"Hogsend Studio"})}),f.jsx("nav",{className:"flex-1 space-y-1 overflow-y-auto p-2",children:kE.map(s=>{const i=s.path==="/"?n==="/":n.startsWith(s.path),r=s.icon;return f.jsxs(bi,{to:s.path,className:Je("flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",i?"bg-accent text-accent-foreground":"text-muted-foreground hover:bg-accent hover:text-accent-foreground"),children:[f.jsx(r,{className:"h-4 w-4"}),s.label]},s.path)})}),f.jsx("div",{className:"border-t p-2",children:f.jsxs("a",{href:ll.docs,target:"_blank",rel:"noreferrer",className:"flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",children:[f.jsx(G_,{className:"h-4 w-4"}),"Docs",f.jsx(lb,{className:"ml-auto h-3.5 w-3.5 opacity-60"})]})})]})}function BE(){const{data:n}=H0();return f.jsxs("div",{className:"flex h-full",children:[f.jsx(LE,{}),f.jsxs("div",{className:"flex flex-1 flex-col overflow-hidden",children:[f.jsxs("header",{className:"flex h-14 items-center justify-end gap-3 border-b bg-card px-6",children:[n!=null&&n.user?f.jsx("span",{className:"text-sm text-muted-foreground",children:n.user.email}):null,f.jsxs(Ke,{variant:"ghost",size:"sm",onClick:()=>{LR()},children:[f.jsx(hE,{className:"h-4 w-4"}),"Sign out"]})]}),f.jsx("main",{className:"flex-1 overflow-y-auto p-6",children:f.jsx(D0,{})})]})]})}function Dt({className:n,...s}){return f.jsx("div",{className:Je("animate-pulse rounded-md bg-muted",n),...s})}function Ri({rows:n=6}){return f.jsxs("div",{className:"space-y-2",children:[f.jsx(Dt,{className:"h-9 w-full"}),Array.from({length:n}).map((s,i)=>f.jsx(Dt,{className:"h-12 w-full"},i))]})}function qE({count:n=4}){return f.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-4",children:Array.from({length:n}).map((s,i)=>f.jsx(Dt,{className:"h-28 w-full"},i))})}function ol({title:n,description:s,icon:i=rE,action:r}){return f.jsxs("div",{className:"flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed p-12 text-center",children:[f.jsx(i,{className:"h-8 w-8 text-muted-foreground"}),f.jsx("p",{className:"text-sm font-medium",children:n}),s?f.jsx("p",{className:"max-w-sm text-sm text-muted-foreground",children:s}):null,r?f.jsx("div",{className:"mt-2 flex flex-wrap items-center justify-center gap-2",children:r}):null]})}function Ut({error:n,onRetry:s}){const i=n instanceof Xt||n instanceof Error?n.message:"Something went wrong.";return f.jsxs("div",{className:"flex flex-col items-center justify-center gap-3 rounded-lg border border-destructive/40 bg-destructive/5 p-12 text-center",children:[f.jsx(F_,{className:"h-8 w-8 text-destructive"}),f.jsxs("div",{className:"space-y-1",children:[f.jsx("p",{className:"text-sm font-medium",children:"Failed to load"}),f.jsx("p",{className:"max-w-sm text-sm text-muted-foreground",children:i})]}),s?f.jsx(Ke,{variant:"outline",size:"sm",onClick:s,children:"Retry"}):null]})}function Ra({title:n,description:s,action:i}){return f.jsxs("div",{className:"flex items-start justify-between gap-4",children:[f.jsxs("div",{children:[f.jsx("h1",{className:"text-2xl font-semibold tracking-tight",children:n}),s?f.jsx("p",{className:"text-sm text-muted-foreground",children:s}):null]}),i?f.jsx("div",{className:"shrink-0",children:i}):null]})}const HE=V0("inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground",secondary:"border-transparent bg-secondary text-secondary-foreground",destructive:"border-transparent bg-destructive text-destructive-foreground",outline:"text-foreground"}},defaultVariants:{variant:"default"}});function ft({className:n,variant:s,...i}){return f.jsx("div",{className:Je(HE({variant:s}),n),...i})}function Hc({open:n,onClose:s,title:i,description:r,children:c,footer:d,className:h}){return Y.useEffect(()=>{if(!n)return;const y=p=>{p.key==="Escape"&&s()};return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[n,s]),n?f.jsxs("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4",children:[f.jsx("button",{type:"button","aria-label":"Close dialog",className:"absolute inset-0 bg-black/50",onClick:s}),f.jsxs("div",{role:"dialog","aria-modal":"true",className:Je("relative z-10 w-full max-w-lg rounded-lg border bg-card p-6 shadow-lg",h),children:[f.jsxs("div",{className:"mb-4 flex items-start justify-between gap-4",children:[f.jsxs("div",{className:"space-y-1",children:[f.jsx("h2",{className:"text-lg font-semibold leading-none tracking-tight",children:i}),r?f.jsx("p",{className:"text-sm text-muted-foreground",children:r}):null]}),f.jsx(Ke,{variant:"ghost",size:"icon",className:"-mr-2 -mt-2 h-8 w-8",onClick:s,"aria-label":"Close",children:f.jsx(jh,{className:"h-4 w-4"})})]}),c?f.jsx("div",{className:"space-y-4",children:c}):null,d?f.jsx("div",{className:"mt-6 flex justify-end gap-2",children:d}):null]})]}):null}function _i({open:n,onClose:s,onConfirm:i,title:r,description:c,confirmLabel:d="Confirm",cancelLabel:h="Cancel",destructive:y,loading:p}){return f.jsx(Hc,{open:n,onClose:s,title:r,description:c,footer:f.jsxs(f.Fragment,{children:[f.jsx(Ke,{variant:"outline",onClick:s,disabled:p,children:h}),f.jsx(Ke,{variant:y?"destructive":"default",onClick:i,disabled:p,children:p?"Working…":d})]})})}function xi({href:n,children:s,variant:i="outline",className:r}){return f.jsxs("a",{href:n,target:"_blank",rel:"noreferrer",className:Je(xh({variant:i,size:"sm"}),r),children:[s,f.jsx(lb,{className:"h-3.5 w-3.5 opacity-70"})]})}const cl=Y.forwardRef(({className:n,...s},i)=>f.jsx("div",{className:"relative w-full overflow-auto",children:f.jsx("table",{ref:i,className:Je("w-full caption-bottom text-sm",n),...s})}));cl.displayName="Table";const ul=Y.forwardRef(({className:n,...s},i)=>f.jsx("thead",{ref:i,className:Je("[&_tr]:border-b",n),...s}));ul.displayName="TableHeader";const fl=Y.forwardRef(({className:n,...s},i)=>f.jsx("tbody",{ref:i,className:Je("[&_tr:last-child]:border-0",n),...s}));fl.displayName="TableBody";const on=Y.forwardRef(({className:n,...s},i)=>f.jsx("tr",{ref:i,className:Je("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",n),...s}));on.displayName="TableRow";const Ce=Y.forwardRef(({className:n,...s},i)=>f.jsx("th",{ref:i,className:Je("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",n),...s}));Ce.displayName="TableHead";const we=Y.forwardRef(({className:n,...s},i)=>f.jsx("td",{ref:i,className:Je("p-2 align-middle [&:has([role=checkbox])]:pr-0",n),...s}));we.displayName="TableCell";const QE=Y.forwardRef(({className:n,...s},i)=>f.jsx("caption",{ref:i,className:Je("mt-4 text-sm text-muted-foreground",n),...s}));QE.displayName="TableCaption";function GE(){return Xe.get("/v1/admin/metrics/overview")}function VE(n){return Xe.get("/v1/admin/emails",{query:n})}function YE(n){return Xe.get(`/v1/admin/emails/${n}`)}function KE(n){return Xe.post(`/v1/admin/emails/${n}/resend`)}function PE(){return Xe.get("/v1/admin/templates")}function XE(n){return Xe.get(`/v1/admin/templates/${encodeURIComponent(n)}/preview`)}function ZE(n,s){return Xe.post(`/v1/admin/templates/${encodeURIComponent(n)}/send-test`,{json:{to:s}})}function JE(n){return Xe.get(`/v1/admin/reporting/templates/${encodeURIComponent(n)}`)}function FE(){return Xe.get("/v1/admin/metrics/journeys")}function IE(n){return Xe.get(`/v1/admin/metrics/journeys/${encodeURIComponent(n)}`)}function mb(){return Xe.get("/v1/admin/journeys",{query:{limit:100}})}function $E(n,s){return Xe.put(`/v1/admin/journeys/${encodeURIComponent(n)}`,{json:{enabled:s}})}function WE(){return Xe.get("/v1/admin/buckets",{query:{limit:100}})}function ew(n){return Xe.get(`/v1/admin/buckets/${encodeURIComponent(n)}`)}function tw(){return Xe.get("/v1/admin/metrics/buckets")}function nw(n,s){return Xe.get(`/v1/admin/metrics/buckets/${encodeURIComponent(n)}`,{query:{period:s}})}function aw(n,s){return Xe.patch(`/v1/admin/buckets/${encodeURIComponent(n)}`,{json:{enabled:s}})}function sw(n){return Xe.get("/v1/admin/contacts",{query:{search:n,limit:50}})}function lw(n){return Xe.get(`/v1/admin/contacts/${encodeURIComponent(n)}`)}function iw(n){return Xe.get(`/v1/admin/reporting/contacts/${encodeURIComponent(n)}/activity`)}function rw(n){return Xe.get(`/v1/admin/contacts/${encodeURIComponent(n)}/timeline`,{query:{limit:100}})}function yb(n,s){return Xe.put(`/v1/admin/contacts/${encodeURIComponent(n)}/preferences`,{json:s})}function ow(n){return Xe.get("/v1/admin/suppressions",{query:{type:n,limit:200}})}function cw(){return Xe.get("/v1/admin/api-keys",{query:{limit:100,includeRevoked:"true"}})}function uw(n){return Xe.post("/v1/admin/api-keys",{json:n})}function fw(n){return Xe.delete(`/v1/admin/api-keys/${encodeURIComponent(n)}`)}function dw(n){return Xe.post("/v1/admin/events",{json:{event:n.event,userId:n.userId,userEmail:n.userEmail,properties:n.properties??{}}})}const Pe={overview:["overview"],emails:n=>["emails",n],email:n=>["email",n],templates:["templates"],templatePreview:n=>["template-preview",n],templateReport:n=>["template-report",n],journeyMetrics:["journey-metrics"],journeys:["journeys"],journeyFunnel:n=>["journey-funnel",n],buckets:["buckets"],bucketMetrics:["bucket-metrics"],bucket:n=>["bucket",n],bucketTrend:n=>["bucket-trend",n],contacts:n=>["contacts",n],contact:n=>["contact",n],contactActivity:n=>["contact-activity",n],contactTimeline:n=>["contact-timeline",n],suppressions:n=>["suppressions",n],apiKeys:["api-keys"]};function Yn(n){if(!n)return"—";const s=new Date(n);return Number.isNaN(s.getTime())?"—":s.toLocaleString(void 0,{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function Mc(n){if(!n)return"—";const s=new Date(n);return Number.isNaN(s.getTime())?"—":s.toLocaleDateString(void 0,{month:"short",day:"numeric"})}function hw(n){if(!n)return"—";const s=new Date(n);if(Number.isNaN(s.getTime()))return"—";const i=Date.now()-s.getTime(),r=Math.round(i/1e3);if(r<60)return"just now";const c=Math.round(r/60);if(c<60)return`${c}m ago`;const d=Math.round(c/60);if(d<24)return`${d}h ago`;const h=Math.round(d/24);return h<30?`${h}d ago`:Mc(n)}function Si(n){return n==null||Number.isNaN(n)?"—":`${(n*100).toFixed(1)}%`}function ut(n){return n==null||Number.isNaN(n)?"—":n.toLocaleString()}function _h(n){if(n==null||Number.isNaN(n))return"—";if(n<60)return`${Math.round(n)}s`;const s=Math.floor(n/60);if(s<60)return`${s}m`;const i=Math.floor(s/60),r=s%60;if(i<24)return r?`${i}h ${r}m`:`${i}h`;const c=Math.floor(i/24),d=i%24;return d?`${c}d ${d}h`:`${c}d`}function mw(n){if(!n)return null;const s=(n.hours??0)*3600+(n.minutes??0)*60+(n.seconds??0);return s>0?_h(s):null}function yw(n,s=48){return n.length>s?`${n.slice(0,s-1)}…`:n}function lh({data:n,height:s=160,label:i="value"}){var c,d;if(n.length===0)return f.jsx("div",{className:"flex items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground",style:{height:s},children:"No data in range"});const r=Math.max(...n.map(h=>h.value),1);return f.jsxs("div",{className:"space-y-2",children:[f.jsx("div",{className:"flex items-end gap-1 rounded-md border bg-muted/20 p-3",style:{height:s},children:n.map(h=>{const y=h.value/r*100;return f.jsx("div",{className:"group flex flex-1 flex-col items-center justify-end",title:`${Mc(h.date)}: ${h.value} ${i}`,children:f.jsx("div",{className:"w-full min-w-[2px] rounded-t bg-primary/70 transition-colors group-hover:bg-primary",style:{height:`${Math.max(y,h.value>0?4:0)}%`}})},h.date)})}),f.jsxs("div",{className:"flex justify-between text-xs text-muted-foreground",children:[f.jsx("span",{children:Mc((c=n[0])==null?void 0:c.date)}),f.jsx("span",{children:Mc((d=n[n.length-1])==null?void 0:d.date)})]})]})}function pw({bucketId:n}){const s=lt({queryKey:Pe.bucketTrend(n),queryFn:()=>nw(n)});if(s.isPending)return f.jsx(Dt,{className:"h-48 w-full"});if(s.isError)return f.jsx(Ut,{error:s.error,onRetry:()=>s.refetch()});const i=s.data,r=i.points.map(y=>({date:y.date,value:y.entered})),c=i.points.map(y=>({date:y.date,value:y.left})),d=i.points.reduce((y,p)=>y+p.entered,0),h=i.points.reduce((y,p)=>y+p.left,0);return f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex flex-wrap gap-6 text-sm",children:[f.jsxs("div",{children:[f.jsx("span",{className:"block text-xs text-muted-foreground",children:"Current size"}),f.jsx("span",{className:"text-lg font-semibold",children:ut(i.size)})]}),f.jsxs("div",{children:[f.jsx("span",{className:"block text-xs text-muted-foreground",children:"Entered (range)"}),f.jsx("span",{className:"text-lg font-semibold",children:ut(d)})]}),f.jsxs("div",{children:[f.jsx("span",{className:"block text-xs text-muted-foreground",children:"Left (range)"}),f.jsx("span",{className:"text-lg font-semibold",children:ut(h)})]})]}),f.jsxs("div",{className:"grid gap-6 lg:grid-cols-2",children:[f.jsxs("div",{className:"space-y-2",children:[f.jsx("span",{className:"text-sm font-medium",children:"Entered over time"}),f.jsx(lh,{data:r,label:"joined"})]}),f.jsxs("div",{className:"space-y-2",children:[f.jsx("span",{className:"text-sm font-medium",children:"Left over time"}),f.jsx(lh,{data:c,label:"left"})]})]})]})}function gw(){var _,E;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(null),[c,d]=Y.useState(null),h=lt({queryKey:Pe.bucketMetrics,queryFn:tw}),y=lt({queryKey:Pe.buckets,queryFn:WE}),p=Sa({mutationFn:j=>aw(j.id,j.enabled),onSuccess:(j,z)=>{n({title:z.enabled?"Bucket enabled":"Bucket disabled"}),d(null),s.invalidateQueries({queryKey:Pe.buckets}),s.invalidateQueries({queryKey:Pe.bucketMetrics})},onError:j=>{n({variant:"error",title:"Update failed",description:j instanceof Xt?j.message:"Unexpected error."}),d(null)}}),m=h.isPending||y.isPending,b=h.isError||y.isError,v=new Map((((_=y.data)==null?void 0:_.buckets)??[]).map(j=>[j.id,j])),S=(((E=h.data)==null?void 0:E.buckets)??[]).map(j=>{const z=v.get(j.bucketId);return{...j,enabled:(z==null?void 0:z.enabled)??!1,kind:(z==null?void 0:z.kind)??"dynamic",timeBased:(z==null?void 0:z.timeBased)??!1}}),R=S.find(j=>j.bucketId===i)??null;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Buckets",description:"Real-time, code-defined membership. Sizes, enter/leave trends, and which journeys each bucket feeds — observe only, authoring stays in code."}),m?f.jsx(Ri,{}):b?f.jsx(Ut,{error:h.error??y.error,onRetry:()=>{h.refetch(),y.refetch()}}):S.length===0?f.jsx(ol,{title:"No buckets registered",description:"Buckets are real-time, code-defined audiences (defineBucket()). Add one to your app and members appear here as events arrive.",action:f.jsxs(f.Fragment,{children:[f.jsx(xi,{href:ll.buckets,children:"How to create a bucket"}),f.jsx(xi,{href:ll.recipes,variant:"ghost",children:"Recipes"})]})}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Bucket"}),f.jsx(Ce,{className:"text-right",children:"Current size"}),f.jsx(Ce,{className:"text-right",children:"Entered"}),f.jsx(Ce,{className:"text-right",children:"Left"}),f.jsx(Ce,{className:"text-right",children:"Avg dwell"}),f.jsx(Ce,{children:"Freshness"}),f.jsx(Ce,{children:"State"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:S.map(j=>f.jsxs(on,{className:"cursor-pointer","data-state":j.bucketId===i?"selected":void 0,onClick:()=>r(z=>z===j.bucketId?null:j.bucketId),children:[f.jsxs(we,{children:[f.jsx("span",{className:"font-medium",children:j.name}),f.jsx("span",{className:"block text-xs text-muted-foreground",children:j.bucketId})]}),f.jsx(we,{className:"text-right",children:ut(j.size)}),f.jsx(we,{className:"text-right",children:ut(j.entered)}),f.jsx(we,{className:"text-right",children:ut(j.left)}),f.jsx(we,{className:"text-right text-muted-foreground",children:_h(j.avgDwellSecs)}),f.jsx(we,{children:f.jsx(vw,{timeBased:j.timeBased})}),f.jsx(we,{children:f.jsx(ft,{variant:j.enabled?"default":"secondary",children:j.enabled?"Enabled":"Disabled"})}),f.jsx(we,{className:"text-right",children:f.jsx(Ke,{variant:"outline",size:"sm",onClick:z=>{z.stopPropagation(),d(j)},children:j.enabled?"Disable":"Enable"})})]},j.bucketId))})]})}),R?f.jsxs("div",{className:"space-y-6",children:[f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"text-base",children:[R.name," — feeds journeys"]})}),f.jsx(On,{children:f.jsx(bw,{bucketId:R.bucketId})})]}),f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"text-base",children:[R.name," — enter / leave over time"]})}),f.jsx(On,{children:f.jsx(pw,{bucketId:R.bucketId})})]})]}):null,f.jsx(_i,{open:c!==null,onClose:()=>d(null),onConfirm:()=>c&&p.mutate({id:c.bucketId,enabled:!c.enabled}),title:c!=null&&c.enabled?"Disable this bucket?":"Enable this bucket?",description:c!=null&&c.enabled?"Membership will stop being recomputed; in-flight members stay until they leave.":"Matching users will start being added to this bucket again.",confirmLabel:c!=null&&c.enabled?"Disable":"Enable",destructive:c==null?void 0:c.enabled,loading:p.isPending})]})}function vw({timeBased:n}){return n?f.jsx(ft,{variant:"outline",title:"Time-based — leaves lag the reconcile cron",children:"Building"}):f.jsx(ft,{variant:"outline",title:"Event-driven — transitions in real time",children:"Live"})}function bw({bucketId:n}){const s=lt({queryKey:Pe.bucket(n),queryFn:()=>ew(n)});if(s.isPending)return f.jsx(Dt,{className:"h-10 w-full"});if(s.isError)return f.jsx(Ut,{error:s.error,onRetry:()=>s.refetch()});const i=s.data.bucket.feedsJourneys,r=mw(s.data.bucket.maxDwell);return f.jsxs("div",{className:"space-y-3",children:[r?f.jsxs(ft,{variant:"outline",title:"Members are force-removed maxDwell after joining, regardless of criteria.",children:["Time-boxed · ",r]}):null,i.length===0?f.jsxs("div",{className:"space-y-1 text-sm text-muted-foreground",children:[f.jsxs("p",{children:["No journeys are bound to this bucket's transitions yet. Bind one to THIS bucket with the typed refs"," ",f.jsx("code",{className:"text-xs",children:"bucket.entered"})," /"," ",f.jsx("code",{className:"text-xs",children:"bucket.left"})," (e.g."," ",f.jsx("code",{className:"text-xs",children:"{ trigger: { event: bucket.entered } }"}),"), or colocate a reaction with"," ",f.jsx("code",{className:"text-xs",children:'bucket.on("enter", ...)'}),"."]}),f.jsxs("p",{children:["To react to ANY bucket, use the generic"," ",f.jsx("code",{className:"text-xs",children:"Events.BUCKET_ENTERED"})," /"," ",f.jsx("code",{className:"text-xs",children:"Events.BUCKET_LEFT"})," constants."]})]}):f.jsx("div",{className:"flex flex-wrap gap-2",children:i.map(c=>f.jsxs(ft,{variant:c.owned?"default":"secondary",title:c.trigger,children:[c.name,c.owned?f.jsx("span",{className:"ml-1.5 text-[10px] uppercase tracking-wide opacity-70",children:"owned"}):null]},`${c.id}-${c.trigger}`))})]})}const xw={delivered:{variant:"outline",className:"border-emerald-500/40 text-emerald-600 dark:text-emerald-400"},opened:{variant:"outline",className:"border-sky-500/40 text-sky-600 dark:text-sky-400"},clicked:{variant:"outline",className:"border-violet-500/40 text-violet-600 dark:text-violet-400"},sent:{variant:"secondary"},rendered:{variant:"secondary"},queued:{variant:"secondary"},bounced:{variant:"destructive"},complained:{variant:"destructive"},failed:{variant:"destructive"}};function Kc({status:n}){const s=xw[n]??{variant:"outline"};return f.jsx(ft,{variant:s.variant,className:Je("capitalize",s.className),children:n})}function pb({open:n,onClose:s,title:i,description:r,children:c,className:d}){return Y.useEffect(()=>{if(!n)return;const h=y=>{y.key==="Escape"&&s()};return document.addEventListener("keydown",h),()=>document.removeEventListener("keydown",h)},[n,s]),n?f.jsxs("div",{className:"fixed inset-0 z-50",children:[f.jsx("button",{type:"button","aria-label":"Close panel",className:"absolute inset-0 bg-black/50",onClick:s}),f.jsxs("div",{role:"dialog","aria-modal":"true",className:Je("absolute inset-y-0 right-0 flex w-full max-w-2xl flex-col border-l bg-card shadow-xl",d),children:[f.jsxs("div",{className:"flex items-start justify-between gap-4 border-b p-6",children:[f.jsxs("div",{className:"space-y-1",children:[f.jsx("h2",{className:"text-lg font-semibold leading-none tracking-tight",children:i}),r?f.jsx("p",{className:"break-all text-sm text-muted-foreground",children:r}):null]}),f.jsx(Ke,{variant:"ghost",size:"icon",className:"-mr-2 -mt-2 h-8 w-8 shrink-0",onClick:s,"aria-label":"Close",children:f.jsx(jh,{className:"h-4 w-4"})})]}),f.jsx("div",{className:"flex-1 overflow-y-auto p-6",children:c})]})]}):null}const Sw={event:Rh,journey:Sh,email:ub};function jw({entry:n}){const s=Sw[n.type],i=n.data,r=n.type==="event"?String(i.event??"event"):n.type==="journey"?`${String(i.journeyId??"journey")} · ${String(i.status??"")}`:String(i.subject??i.templateKey??"email");return f.jsxs("li",{className:"flex gap-3",children:[f.jsx("span",{className:"mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border bg-card",children:f.jsx(s,{className:"h-3.5 w-3.5 text-muted-foreground"})}),f.jsxs("div",{className:"min-w-0 flex-1 pb-3",children:[f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx("p",{className:"truncate text-sm font-medium",children:r}),n.type==="email"&&i.status?f.jsx(Kc,{status:String(i.status)}):null]}),f.jsx("p",{className:"text-xs text-muted-foreground",children:Yn(n.timestamp)})]})]})}function Rw({contactId:n,onClose:s}){var _,E;const i=n!==null,{toast:r}=vs(),c=ps(),[d,h]=Y.useState(!1),y=lt({queryKey:n?Pe.contact(n):["contact","none"],queryFn:()=>lw(n),enabled:i}),p=lt({queryKey:n?Pe.contactActivity(n):["activity","none"],queryFn:()=>iw(n),enabled:i}),m=lt({queryKey:n?Pe.contactTimeline(n):["timeline","none"],queryFn:()=>rw(n),enabled:i}),b=Sa({mutationFn:()=>yb(n,{suppressed:!1}),onSuccess:()=>{r({title:"Contact un-suppressed",description:"They can receive emails again."}),h(!1),n&&c.invalidateQueries({queryKey:Pe.contact(n)}),c.invalidateQueries({queryKey:["suppressions"]})},onError:j=>{r({variant:"error",title:"Un-suppress failed",description:j instanceof Xt?j.message:"Unexpected error."}),h(!1)}}),v=(_=y.data)==null?void 0:_.contact,S=(E=y.data)==null?void 0:E.preferences,R=(S==null?void 0:S.suppressed)||(S==null?void 0:S.unsubscribedAll);return f.jsxs(f.Fragment,{children:[f.jsx(pb,{open:i,onClose:s,title:(v==null?void 0:v.email)??(v==null?void 0:v.externalId)??"Contact",description:v?v.externalId:void 0,children:y.isPending?f.jsxs("div",{className:"space-y-4",children:[f.jsx(Dt,{className:"h-24 w-full"}),f.jsx(Dt,{className:"h-40 w-full"})]}):y.isError?f.jsx(Ut,{error:y.error,onRetry:()=>y.refetch()}):v?f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[S!=null&&S.suppressed?f.jsx(ft,{variant:"destructive",children:"Suppressed"}):null,S!=null&&S.unsubscribedAll?f.jsx(ft,{variant:"destructive",children:"Unsubscribed"}):null,S&&S.bounceCount>0?f.jsxs(ft,{variant:"secondary",children:[S.bounceCount," bounce",S.bounceCount===1?"":"s"]}):null,R?null:f.jsx(ft,{variant:"outline",className:"border-emerald-500/40 text-emerald-600 dark:text-emerald-400",children:"Subscribed"}),R?f.jsx(Ke,{size:"sm",variant:"outline",className:"ml-auto",onClick:()=>h(!0),children:"Un-suppress"}):null]}),f.jsxs("dl",{className:"grid grid-cols-2 gap-4",children:[f.jsxs("div",{children:[f.jsx("dt",{className:"text-xs text-muted-foreground",children:"First seen"}),f.jsx("dd",{className:"text-sm",children:Yn(v.firstSeenAt)})]}),f.jsxs("div",{children:[f.jsx("dt",{className:"text-xs text-muted-foreground",children:"Last seen"}),f.jsx("dd",{className:"text-sm",children:Yn(v.lastSeenAt)})]})]}),f.jsxs("section",{children:[f.jsxs("h3",{className:"mb-3 text-sm font-semibold",children:["Email activity",p.data?` (${p.data.total})`:""]}),p.isPending?f.jsx(Dt,{className:"h-20 w-full"}):p.isError?f.jsx("p",{className:"text-sm text-muted-foreground",children:"Could not load email activity."}):p.data&&p.data.sends.length>0?f.jsx("ul",{className:"space-y-2",children:p.data.sends.slice(0,10).map(j=>f.jsxs("li",{className:"flex items-center justify-between gap-3 rounded-md border p-3",children:[f.jsxs("div",{className:"min-w-0",children:[f.jsx("p",{className:"truncate text-sm font-medium",children:j.subject}),f.jsx("p",{className:"text-xs text-muted-foreground",children:Yn(j.createdAt)})]}),f.jsx(Kc,{status:j.status})]},j.id))}):f.jsx("p",{className:"text-sm text-muted-foreground",children:"No emails sent."})]}),f.jsxs("section",{children:[f.jsx("h3",{className:"mb-3 text-sm font-semibold",children:"Timeline"}),m.isPending?f.jsx(Dt,{className:"h-20 w-full"}):m.isError?f.jsx("p",{className:"text-sm text-muted-foreground",children:"Could not load timeline."}):m.data&&m.data.timeline.length>0?f.jsx("ul",{children:m.data.timeline.map((j,z)=>f.jsx(jw,{entry:j},`${j.type}-${j.timestamp}-${z}`))}):f.jsx("p",{className:"text-sm text-muted-foreground",children:"No activity yet."})]})]}):null}),f.jsx(_i,{open:d,onClose:()=>h(!1),onConfirm:()=>b.mutate(),title:"Un-suppress this contact?",description:"They will be eligible to receive emails again.",confirmLabel:"Un-suppress",loading:b.isPending})]})}function _w(){var p;const[n,s]=Y.useState(""),[i,r]=Y.useState(""),[c,d]=Y.useState(null);Y.useEffect(()=>{const m=window.setTimeout(()=>r(n.trim()),300);return()=>window.clearTimeout(m)},[n]);const h=lt({queryKey:Pe.contacts(i),queryFn:()=>sw(i||void 0),placeholderData:ch}),y=((p=h.data)==null?void 0:p.contacts)??[];return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Contacts",description:"Search contacts and review their full activity timeline."}),f.jsxs("div",{className:"relative max-w-sm",children:[f.jsx(jE,{className:"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"}),f.jsx(zt,{placeholder:"Search by email or external ID…",className:"pl-9",value:n,onChange:m=>s(m.target.value)})]}),h.isPending?f.jsx(Ri,{}):h.isError?f.jsx(Ut,{error:h.error,onRetry:()=>h.refetch()}):y.length===0?f.jsx(ol,{title:"No contacts found",description:i?"No contacts match your search.":"Contacts appear here as events are ingested."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Email"}),f.jsx(Ce,{children:"External ID"}),f.jsx(Ce,{className:"text-right",children:"Last seen"})]})}),f.jsx(fl,{children:y.map(m=>f.jsxs(on,{className:"cursor-pointer",onClick:()=>d(m.id),children:[f.jsx(we,{className:"font-medium",children:m.email??"—"}),f.jsx(we,{className:"text-muted-foreground",children:m.externalId}),f.jsx(we,{className:"text-right text-muted-foreground",children:hw(m.lastSeenAt)})]},m.id))})]})}),f.jsx(Rw,{contactId:c,onClose:()=>d(null)})]})}function Tt({className:n,...s}){return f.jsx("label",{className:Je("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",n),...s})}function Dv(){return`test_${Math.random().toString(36).slice(2,10)}`}function Ew(n){const s=n.trim();if(!s)return{ok:!0,value:{}};try{const i=JSON.parse(s);return i===null||typeof i!="object"||Array.isArray(i)?{ok:!1,error:"Properties must be a JSON object."}:{ok:!0,value:i}}catch(i){return{ok:!1,error:i instanceof Error?i.message:"Invalid JSON."}}}function ww(){const{toast:n}=vs(),[s,i]=Y.useState(""),[r,c]=Y.useState(Dv),[d,h]=Y.useState(""),[y,p]=Y.useState("{}"),[m,b]=Y.useState(null),[v,S]=Y.useState(null),R=lt({queryKey:Pe.journeys,queryFn:mb}),_=Y.useMemo(()=>{var B;const A=new Map;for(const N of((B=R.data)==null?void 0:B.journeys)??[]){const Q=A.get(N.trigger.event)??[];Q.push(N.name),A.set(N.trigger.event,Q)}return Array.from(A,([N,Q])=>({event:N,journeys:Q}))},[R.data]),E=Sa({mutationFn:A=>dw(A),onSuccess:A=>{S(A),n({title:"Event ingested",description:`Stored: ${A.stored} · ${A.exits.length} journey exit(s).`})},onError:A=>{n({variant:"error",title:"Ingest failed",description:A instanceof Xt?A.message:"Unexpected error."})}}),j=()=>{const A=Ew(y);if(!A.ok){b(A.error);return}b(null),E.mutate({event:s.trim(),userId:r.trim(),userEmail:d.trim()||void 0,properties:A.value})},z=!!s.trim()&&!!r.trim()&&!E.isPending;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Debug",description:"Fire events into the ingest pipeline — exactly what real events do. Trigger journeys locally without a PostHog tunnel."}),f.jsxs("div",{className:"grid gap-6 lg:grid-cols-3",children:[f.jsxs(Cn,{className:"lg:col-span-2",children:[f.jsxs(Mn,{children:[f.jsx(An,{className:"text-base",children:"Send a test event"}),f.jsxs(Lr,{children:["POSTs to ",f.jsx("code",{className:"text-xs",children:"/v1/ingest"}),". Journeys whose trigger matches will enrol this user."]})]}),f.jsxs(On,{className:"space-y-4",children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"event",children:"Event name"}),f.jsx(zt,{id:"event",list:"event-presets",placeholder:"e.g. user.created",value:s,onChange:A=>i(A.target.value)}),f.jsx("datalist",{id:"event-presets",children:_.map(A=>f.jsx("option",{value:A.event},A.event))})]}),f.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"userId",children:"User ID"}),f.jsxs("div",{className:"flex gap-2",children:[f.jsx(zt,{id:"userId",placeholder:"test_user",value:r,onChange:A=>c(A.target.value)}),f.jsx(Ke,{variant:"outline",size:"icon",title:"Generate a random test user ID",onClick:()=>c(Dv()),children:f.jsx(TE,{className:"h-4 w-4"})})]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"userEmail",children:"User email (optional)"}),f.jsx(zt,{id:"userEmail",type:"email",placeholder:"you@example.com",value:d,onChange:A=>h(A.target.value)})]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"props",children:"Properties (JSON)"}),f.jsx("textarea",{id:"props",spellCheck:!1,className:"flex min-h-[140px] w-full rounded-md border border-input bg-transparent px-3 py-2 font-mono text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",value:y,onChange:A=>{p(A.target.value),m&&b(null)}}),m?f.jsx("p",{className:"text-xs text-destructive",children:m}):null]}),f.jsx("div",{className:"flex justify-end",children:f.jsxs(Ke,{onClick:j,disabled:!z,children:[f.jsx(Rh,{className:"h-4 w-4"}),E.isPending?"Sending…":"Send event"]})})]})]}),f.jsxs("div",{className:"space-y-6",children:[f.jsxs(Cn,{children:[f.jsxs(Mn,{children:[f.jsx(An,{className:"text-base",children:"Journey triggers"}),f.jsx(Lr,{children:"Events that enrol a registered journey. Click to fill the form."})]}),f.jsx(On,{children:_.length===0?f.jsx("p",{className:"text-sm text-muted-foreground",children:"No journeys registered yet. Any event name still works — define a journey to see triggers here."}):f.jsx("div",{className:"flex flex-wrap gap-2",children:_.map(A=>f.jsx(Ke,{variant:"outline",size:"sm",title:`Triggers: ${A.journeys.join(", ")}`,onClick:()=>i(A.event),children:A.event},A.event))})})]}),v?f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"flex items-center gap-2 text-base",children:[f.jsx(rb,{className:"h-4 w-4 text-green-500"}),"Result"]})}),f.jsxs(On,{className:"space-y-3",children:[f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx(ft,{variant:v.stored?"default":"secondary",children:v.stored?"Stored":"Not stored"}),f.jsxs("span",{className:"text-sm text-muted-foreground",children:[v.exits.length," journey exit(s)"]})]}),f.jsx("pre",{className:"max-h-48 overflow-auto rounded-md border bg-muted/30 p-3 text-xs",children:JSON.stringify(v,null,2)}),f.jsxs("p",{className:"text-sm text-muted-foreground",children:["See the effect in"," ",f.jsx(bi,{to:"/journeys",className:"underline underline-offset-4",children:"Journeys"})," ","or the"," ",f.jsx(bi,{to:"/contacts",className:"underline underline-offset-4",children:"contact's timeline"}),". Make sure the worker is running."]})]})]}):null]})]})]})}function Tw({step:n,base:s}){const i=s>0?n.value/s:0,r=i*100;return f.jsxs("div",{className:"space-y-1",children:[f.jsxs("div",{className:"flex items-center justify-between text-sm",children:[f.jsx("span",{className:"font-medium",children:n.label}),f.jsxs("span",{className:"text-muted-foreground",children:[ut(n.value),s>0?f.jsxs("span",{className:"ml-1 text-xs",children:["(",Si(i),")"]}):null]})]}),f.jsx("div",{className:"h-2 overflow-hidden rounded-full bg-muted",children:f.jsx("div",{className:"h-full rounded-full bg-primary",style:{width:`${Math.min(r,100)}%`}})})]})}function Nw({journeyId:n}){const s=lt({queryKey:Pe.journeyFunnel(n),queryFn:()=>IE(n)});if(s.isPending)return f.jsx(Dt,{className:"h-48 w-full"});if(s.isError)return f.jsx(Ut,{error:s.error,onRetry:()=>s.refetch()});const i=s.data,r=[{label:"Enrolled",value:i.enrolled},{label:"Email sent",value:i.emailSent},{label:"Opened",value:i.emailOpened},{label:"Clicked",value:i.emailClicked},{label:"Completed",value:i.completed}];return f.jsxs("div",{className:"space-y-4",children:[f.jsx("div",{className:"space-y-3",children:r.map(c=>f.jsx(Tw,{step:c,base:i.enrolled},c.label))}),f.jsxs("div",{className:"flex gap-4 border-t pt-3 text-sm text-muted-foreground",children:[f.jsxs("span",{children:["Failed: ",ut(i.failed)]}),f.jsxs("span",{children:["Exited: ",ut(i.exited)]})]})]})}function Cw(){var _,E;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(null),[c,d]=Y.useState(null),h=lt({queryKey:Pe.journeyMetrics,queryFn:FE}),y=lt({queryKey:Pe.journeys,queryFn:mb}),p=Sa({mutationFn:j=>$E(j.id,j.enabled),onSuccess:(j,z)=>{n({title:z.enabled?"Journey enabled":"Journey disabled"}),d(null),s.invalidateQueries({queryKey:Pe.journeys}),s.invalidateQueries({queryKey:Pe.journeyMetrics})},onError:j=>{n({variant:"error",title:"Update failed",description:j instanceof Xt?j.message:"Unexpected error."}),d(null)}}),m=h.isPending||y.isPending,b=h.isError||y.isError,v=new Map((((_=y.data)==null?void 0:_.journeys)??[]).map(j=>[j.id,j.enabled])),S=(((E=h.data)==null?void 0:E.journeys)??[]).map(j=>({...j,enabled:v.get(j.journeyId)??!1})),R=S.find(j=>j.journeyId===i)??null;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Journeys",description:"Lifecycle journeys, completion rates, and per-journey funnels."}),m?f.jsx(Ri,{}):b?f.jsx(Ut,{error:h.error??y.error,onRetry:()=>{h.refetch(),y.refetch()}}):S.length===0?f.jsx(ol,{title:"No journeys registered",description:"Journeys are defined in code with defineJourney(). Add one to your app, then fire its trigger event from Debug to see it here.",action:f.jsxs(f.Fragment,{children:[f.jsx(xi,{href:ll.journeys,children:"How to create a journey"}),f.jsx(xi,{href:ll.recipes,variant:"ghost",children:"Recipes"})]})}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Journey"}),f.jsx(Ce,{className:"text-right",children:"Enrolled"}),f.jsx(Ce,{className:"text-right",children:"Active"}),f.jsx(Ce,{className:"text-right",children:"Completed"}),f.jsx(Ce,{className:"text-right",children:"Completion"}),f.jsx(Ce,{className:"text-right",children:"Avg duration"}),f.jsx(Ce,{children:"State"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:S.map(j=>f.jsxs(on,{className:"cursor-pointer","data-state":j.journeyId===i?"selected":void 0,onClick:()=>r(z=>z===j.journeyId?null:j.journeyId),children:[f.jsxs(we,{children:[f.jsx("span",{className:"font-medium",children:j.name}),f.jsx("span",{className:"block text-xs text-muted-foreground",children:j.journeyId})]}),f.jsx(we,{className:"text-right",children:ut(j.enrolled)}),f.jsx(we,{className:"text-right",children:ut(j.active)}),f.jsx(we,{className:"text-right",children:ut(j.completed)}),f.jsx(we,{className:"text-right",children:Si(j.completionRate)}),f.jsx(we,{className:"text-right text-muted-foreground",children:_h(j.avgDurationSecs)}),f.jsx(we,{children:f.jsx(ft,{variant:j.enabled?"default":"secondary",children:j.enabled?"Enabled":"Disabled"})}),f.jsx(we,{className:"text-right",children:f.jsx(Ke,{variant:"outline",size:"sm",onClick:z=>{z.stopPropagation(),d(j)},children:j.enabled?"Disable":"Enable"})})]},j.journeyId))})]})}),R?f.jsxs(Cn,{children:[f.jsx(Mn,{children:f.jsxs(An,{className:"text-base",children:[R.name," — funnel"]})}),f.jsx(On,{children:f.jsx(Nw,{journeyId:R.journeyId})})]}):null,f.jsx(_i,{open:c!==null,onClose:()=>d(null),onConfirm:()=>c&&p.mutate({id:c.journeyId,enabled:!c.enabled}),title:c!=null&&c.enabled?"Disable this journey?":"Enable this journey?",description:c!=null&&c.enabled?"New events will stop enrolling users into this journey.":"New matching events will start enrolling users into this journey.",confirmLabel:c!=null&&c.enabled?"Disable":"Enable",destructive:c==null?void 0:c.enabled,loading:p.isPending})]})}function Gs({label:n,value:s,hint:i,icon:r}){return f.jsxs(Cn,{children:[f.jsxs(Mn,{className:"flex flex-row items-center justify-between space-y-0 pb-2",children:[f.jsx(An,{className:"text-sm font-medium text-muted-foreground",children:n}),r?f.jsx(r,{className:"h-4 w-4 text-muted-foreground"}):null]}),f.jsxs(On,{children:[f.jsx("div",{className:"text-2xl font-semibold tracking-tight",children:s}),i?f.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:i}):null]})]})}function Mw(n){return n.totalContacts===0&&n.activeJourneys===0&&n.emailsSent30d===0}function Aw(){return f.jsxs(Cn,{className:"border-primary/30 bg-primary/5",children:[f.jsxs(Mn,{children:[f.jsx(An,{className:"text-base",children:"Welcome to Hogsend Studio"}),f.jsx(Lr,{children:"Studio observes your code-first lifecycle engine. Here's the path to your first running journey."})]}),f.jsxs(On,{className:"space-y-4",children:[f.jsxs("ol",{className:"space-y-1.5 text-sm text-muted-foreground",children:[f.jsxs("li",{children:[f.jsx("span",{className:"font-medium text-foreground",children:"1."})," Define a journey in code and start the worker."]}),f.jsxs("li",{children:[f.jsx("span",{className:"font-medium text-foreground",children:"2."})," Fire its trigger event to enrol a test user."]}),f.jsxs("li",{children:[f.jsx("span",{className:"font-medium text-foreground",children:"3."})," Watch enrolments, sends, and exits land here."]})]}),f.jsxs("div",{className:"flex flex-wrap gap-2",children:[f.jsxs(bi,{to:"/debug",className:Je(xh({size:"sm"})),children:[f.jsx(Rh,{className:"h-4 w-4"}),"Send a test event"]}),f.jsx(xi,{href:ll.quickstart,children:"Quickstart"}),f.jsx(xi,{href:ll.journeys,variant:"ghost",children:"Create a journey"})]})]})]})}function Ow(){const n=lt({queryKey:Pe.overview,queryFn:GE});return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Overview",description:"Key delivery and engagement metrics at a glance."}),n.isPending?f.jsx(qE,{count:6}):n.isError?f.jsx(Ut,{error:n.error,onRetry:()=>n.refetch()}):f.jsxs(f.Fragment,{children:[Mw(n.data)?f.jsx(Aw,{}):null,f.jsxs("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:[f.jsx(Gs,{label:"Total contacts",value:ut(n.data.totalContacts),icon:db}),f.jsx(Gs,{label:"Active journeys",value:ut(n.data.activeJourneys),hint:"Instances active or waiting",icon:Sh}),f.jsx(Gs,{label:"Sent (24h)",value:ut(n.data.emailsSent24h),icon:Yr}),f.jsx(Gs,{label:"Sent (7d)",value:ut(n.data.emailsSent7d),icon:zv}),f.jsx(Gs,{label:"Sent (30d)",value:ut(n.data.emailsSent30d),icon:zv}),f.jsx(Gs,{label:"Bounce rate (30d)",value:Si(n.data.bounceRate30d),hint:"Bounced / sent in last 30 days",icon:cb})]}),f.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:f.jsx(Gs,{label:"Unsubscribe rate",value:Si(n.data.unsubscribeRate),hint:"Unsubscribed / total preferences",icon:ME})})]})]})}const Qc=Y.forwardRef(({className:n,children:s,...i},r)=>f.jsxs("div",{className:"relative",children:[f.jsx("select",{ref:r,className:Je("flex h-9 w-full appearance-none rounded-md border border-input bg-transparent px-3 py-1 pr-8 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",n),...i,children:s}),f.jsx(Z_,{className:"pointer-events-none absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"})]}));Qc.displayName="Select";const zw={queued:fb,sent:Yr,delivered:P_,opened:nE,clicked:vE,bounced:cb,complained:ib,failed:ob};function Dw({event:n}){const s=zw[n.type]??fb;return f.jsxs("li",{className:"flex gap-3",children:[f.jsxs("div",{className:"flex flex-col items-center",children:[f.jsx("span",{className:"flex h-7 w-7 items-center justify-center rounded-full border bg-card",children:f.jsx(s,{className:"h-3.5 w-3.5 text-muted-foreground"})}),f.jsx("span",{className:"w-px flex-1 bg-border"})]}),f.jsxs("div",{className:"pb-4",children:[f.jsx("p",{className:"text-sm font-medium capitalize",children:n.type}),f.jsx("p",{className:"text-xs text-muted-foreground",children:Yn(n.timestamp)}),n.url?f.jsx("p",{className:"mt-0.5 break-all text-xs text-muted-foreground",children:n.url}):null]})]})}function $a({label:n,value:s}){return f.jsxs("div",{className:"space-y-0.5",children:[f.jsx("dt",{className:"text-xs text-muted-foreground",children:n}),f.jsx("dd",{className:"break-all text-sm",children:s})]})}function Uw({emailId:n,onClose:s}){const i=n!==null,{toast:r}=vs(),c=ps(),[d,h]=Y.useState(!1),y=lt({queryKey:n?Pe.email(n):["email","none"],queryFn:()=>YE(n),enabled:i}),p=Sa({mutationFn:()=>KE(n),onSuccess:()=>{r({title:"Resend queued",description:"A fresh send was queued from this template."}),h(!1),c.invalidateQueries({queryKey:["emails"]}),n&&c.invalidateQueries({queryKey:Pe.email(n)})},onError:v=>{r({variant:"error",title:"Resend failed",description:v instanceof Xt?v.message:"Unexpected error."}),h(!1)}}),m=y.data,b=m&&["failed","bounced"].includes(m.email.status);return f.jsxs(f.Fragment,{children:[f.jsx(pb,{open:i,onClose:s,title:"Email send",description:m==null?void 0:m.email.subject,children:y.isPending?f.jsxs("div",{className:"space-y-4",children:[f.jsx(Dt,{className:"h-24 w-full"}),f.jsx(Dt,{className:"h-40 w-full"})]}):y.isError?f.jsx(Ut,{error:y.error,onRetry:()=>y.refetch()}):m?f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex items-center justify-between gap-2",children:[f.jsx(Kc,{status:m.email.status}),b?f.jsxs(Ke,{size:"sm",variant:"outline",onClick:()=>h(!0),children:[f.jsx(Yr,{className:"h-4 w-4"}),"Resend"]}):null]}),f.jsxs("dl",{className:"grid grid-cols-2 gap-4",children:[f.jsx($a,{label:"To",value:m.email.toEmail}),f.jsx($a,{label:"From",value:m.email.fromEmail}),f.jsx($a,{label:"Template",value:m.email.templateKey??"—"}),f.jsx($a,{label:"Category",value:m.email.category??"—"}),f.jsx($a,{label:"Journey",value:m.email.journeyId??"—"}),f.jsx($a,{label:"User",value:m.email.userId??"—"}),f.jsx($a,{label:"Message ID",value:m.email.messageId??"—"}),f.jsx($a,{label:"Created",value:Yn(m.email.createdAt)})]}),f.jsxs("section",{children:[f.jsx("h3",{className:"mb-3 text-sm font-semibold",children:"Timeline"}),m.events.length===0?f.jsx("p",{className:"text-sm text-muted-foreground",children:"No delivery events recorded yet."}):f.jsx("ul",{className:"[&>li:last-child>div:first-child>span:last-child]:hidden",children:m.events.map((v,S)=>f.jsx(Dw,{event:v},`${v.type}-${v.timestamp}-${S}`))})]}),f.jsxs("section",{children:[f.jsxs("h3",{className:"mb-3 text-sm font-semibold",children:["Tracked links (",m.trackedLinks.length,")"]}),m.trackedLinks.length===0?f.jsx("p",{className:"text-sm text-muted-foreground",children:"No tracked links in this email."}):f.jsx("ul",{className:"space-y-2",children:m.trackedLinks.map(v=>f.jsxs("li",{className:"flex items-center justify-between gap-3 rounded-md border p-3",children:[f.jsx("span",{className:"break-all text-sm",children:v.originalUrl}),f.jsxs("span",{className:"shrink-0 text-xs text-muted-foreground",children:[v.clickCount," click",v.clickCount===1?"":"s"]})]},v.id))})]})]}):null}),f.jsx(_i,{open:d,onClose:()=>h(!1),onConfirm:()=>p.mutate(),title:"Resend this email?",description:"A new send will be queued from the original template and recipient.",confirmLabel:"Resend",loading:p.isPending})]})}const kw=["queued","rendered","sent","delivered","opened","clicked","bounced","complained","failed"],Lw=["opened","clicked","bounced","complained"],Tr=25,Uv={templateKey:"",status:"",engagement:"",journeyId:"",userId:"",from:"",to:""};function Bw(){var N,Q;const[n,s]=Y.useState(Uv),[i,r]=Y.useState("createdAt"),[c,d]=Y.useState("desc"),[h,y]=Y.useState(0),[p,m]=Y.useState(null),b=n.from?new Date(`${n.from}T00:00:00`).toISOString():void 0,v=n.to?new Date(`${n.to}T23:59:59`).toISOString():void 0,S={limit:Tr,offset:h,templateKey:n.templateKey||void 0,status:n.status||void 0,engagement:n.engagement||void 0,journeyId:n.journeyId||void 0,userId:n.userId||void 0,from:b,to:v,sort:i,order:c},R=lt({queryKey:Pe.emails(S),queryFn:()=>VE(S),placeholderData:ch});function _(k){s(D=>({...D,...k})),y(0)}function E(k){i===k?d(D=>D==="asc"?"desc":"asc"):(r(k),d("desc")),y(0)}const j=Object.values(n).some(Boolean),z=((N=R.data)==null?void 0:N.total)??0,A=((Q=R.data)==null?void 0:Q.emails)??[];function B({column:k,label:D}){const O=i===k;return f.jsx(Ce,{children:f.jsxs("button",{type:"button",className:"inline-flex items-center gap-1 font-medium hover:text-foreground",onClick:()=>E(k),children:[D,O?c==="asc"?f.jsx(q_,{className:"h-3 w-3"}):f.jsx(k_,{className:"h-3 w-3"}):null]})})}return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Sends",description:"Every email sent — filter, sort, and drill into the timeline."}),f.jsxs("div",{className:"grid gap-3 rounded-lg border p-4 md:grid-cols-3 lg:grid-cols-4",children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-template",children:"Template"}),f.jsx(zt,{id:"f-template",placeholder:"template key",value:n.templateKey,onChange:k=>_({templateKey:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-status",children:"Status"}),f.jsxs(Qc,{id:"f-status",value:n.status,onChange:k=>_({status:k.target.value}),children:[f.jsx("option",{value:"",children:"All"}),kw.map(k=>f.jsx("option",{value:k,children:k},k))]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-engagement",children:"Engagement"}),f.jsxs(Qc,{id:"f-engagement",value:n.engagement,onChange:k=>_({engagement:k.target.value}),children:[f.jsx("option",{value:"",children:"Any"}),Lw.map(k=>f.jsx("option",{value:k,children:k},k))]})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-journey",children:"Journey ID"}),f.jsx(zt,{id:"f-journey",placeholder:"journey id",value:n.journeyId,onChange:k=>_({journeyId:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-user",children:"User ID"}),f.jsx(zt,{id:"f-user",placeholder:"user id",value:n.userId,onChange:k=>_({userId:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-from",children:"From"}),f.jsx(zt,{id:"f-from",type:"date",value:n.from,onChange:k=>_({from:k.target.value})})]}),f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"f-to",children:"To"}),f.jsx(zt,{id:"f-to",type:"date",value:n.to,onChange:k=>_({to:k.target.value})})]}),j?f.jsx("div",{className:"flex items-end",children:f.jsx(Ke,{variant:"ghost",size:"sm",onClick:()=>{s(Uv),y(0)},children:"Clear filters"})}):null]}),R.isPending?f.jsx(Ri,{}):R.isError?f.jsx(Ut,{error:R.error,onRetry:()=>R.refetch()}):A.length===0?f.jsx(ol,{title:"No sends found",description:j?"Try widening or clearing your filters.":"Emails will appear here once your journeys start sending."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Recipient"}),f.jsx(Ce,{children:"Subject"}),f.jsx(Ce,{children:"Template"}),f.jsx(Ce,{children:"Status"}),f.jsx(B,{column:"sentAt",label:"Sent"}),f.jsx(B,{column:"createdAt",label:"Created"})]})}),f.jsx(fl,{children:A.map(k=>f.jsxs(on,{className:"cursor-pointer",onClick:()=>m(k.id),children:[f.jsx(we,{className:"font-medium",children:k.toEmail}),f.jsx(we,{children:yw(k.subject,40)}),f.jsx(we,{className:"text-muted-foreground",children:k.templateKey??"—"}),f.jsx(we,{children:f.jsx(Kc,{status:k.status})}),f.jsx(we,{className:"text-muted-foreground",children:Yn(k.sentAt)}),f.jsx(we,{className:"text-muted-foreground",children:Yn(k.createdAt)})]},k.id))})]})}),z>0?f.jsxs("div",{className:"flex items-center justify-between text-sm text-muted-foreground",children:[f.jsxs("span",{children:[h+1,"–",Math.min(h+Tr,z)," of ",z]}),f.jsxs("div",{className:"flex gap-2",children:[f.jsx(Ke,{variant:"outline",size:"sm",disabled:h===0,onClick:()=>y(k=>Math.max(0,k-Tr)),children:"Previous"}),f.jsx(Ke,{variant:"outline",size:"sm",disabled:h+Tr>=z,onClick:()=>y(k=>k+Tr),children:"Next"})]})]}):null,f.jsx(Uw,{emailId:p,onClose:()=>m(null)})]})}const qw=["read","journey-admin","full-admin"];function Hw(){var A;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(!1),[c,d]=Y.useState(""),[h,y]=Y.useState(["read"]),[p,m]=Y.useState(null),[b,v]=Y.useState(null),S=lt({queryKey:Pe.apiKeys,queryFn:cw}),R=Sa({mutationFn:()=>uw({name:c.trim(),scopes:h}),onSuccess:B=>{r(!1),d(""),y(["read"]),m(B),s.invalidateQueries({queryKey:Pe.apiKeys})},onError:B=>{n({variant:"error",title:"Could not create key",description:B instanceof Xt?B.message:"Unexpected error."})}}),_=Sa({mutationFn:B=>fw(B),onSuccess:()=>{n({title:"API key revoked"}),v(null),s.invalidateQueries({queryKey:Pe.apiKeys})},onError:B=>{n({variant:"error",title:"Revoke failed",description:B instanceof Xt?B.message:"Unexpected error."}),v(null)}});function E(B){y(N=>N.includes(B)?N.filter(Q=>Q!==B):[...N,B])}async function j(B){try{await navigator.clipboard.writeText(B),n({title:"Copied to clipboard"})}catch{n({variant:"error",title:"Copy failed"})}}const z=((A=S.data)==null?void 0:A.keys)??[];return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Settings",description:"API keys for programmatic access to the Hogsend admin API.",action:f.jsxs(Ke,{onClick:()=>r(!0),children:[f.jsx(xE,{className:"h-4 w-4"}),"New API key"]})}),S.isPending?f.jsx(Ri,{}):S.isError?f.jsx(Ut,{error:S.error,onRetry:()=>S.refetch()}):z.length===0?f.jsx(ol,{icon:cE,title:"No API keys",description:"Create a key to authenticate API and webhook requests."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Name"}),f.jsx(Ce,{children:"Prefix"}),f.jsx(Ce,{children:"Scopes"}),f.jsx(Ce,{children:"Last used"}),f.jsx(Ce,{children:"Status"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:z.map(B=>{const N=B.revokedAt!==null;return f.jsxs(on,{children:[f.jsx(we,{className:"font-medium",children:B.name}),f.jsxs(we,{className:"font-mono text-xs text-muted-foreground",children:[B.keyPrefix,"…"]}),f.jsx(we,{children:f.jsx("div",{className:"flex flex-wrap gap-1",children:B.scopes.map(Q=>f.jsx(ft,{variant:"secondary",children:Q},Q))})}),f.jsx(we,{className:"text-muted-foreground",children:Yn(B.lastUsedAt)}),f.jsx(we,{children:N?f.jsx(ft,{variant:"outline",children:"Revoked"}):f.jsx(ft,{variant:"outline",className:"border-emerald-500/40 text-emerald-600 dark:text-emerald-400",children:"Active"})}),f.jsx(we,{className:"text-right",children:N?null:f.jsx(Ke,{variant:"outline",size:"sm",onClick:()=>v(B),children:"Revoke"})})]},B.id)})})]})}),f.jsxs(Hc,{open:i,onClose:()=>r(!1),title:"Create API key",description:"Choose a descriptive name and the scopes this key needs.",footer:f.jsxs(f.Fragment,{children:[f.jsx(Ke,{variant:"outline",onClick:()=>r(!1),children:"Cancel"}),f.jsx(Ke,{onClick:()=>R.mutate(),disabled:!c.trim()||h.length===0||R.isPending,children:R.isPending?"Creating…":"Create key"})]}),children:[f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"key-name",children:"Name"}),f.jsx(zt,{id:"key-name",placeholder:"CI ingest key",value:c,onChange:B=>d(B.target.value)})]}),f.jsxs("div",{className:"space-y-2",children:[f.jsx(Tt,{children:"Scopes"}),f.jsx("div",{className:"flex flex-col gap-2",children:qw.map(B=>f.jsxs("label",{className:"flex items-center gap-2 text-sm",htmlFor:`scope-${B}`,children:[f.jsx("input",{id:`scope-${B}`,type:"checkbox",className:"h-4 w-4 rounded border-input",checked:h.includes(B),onChange:()=>E(B)}),B]},B))})]})]}),f.jsx(Hc,{open:p!==null,onClose:()=>m(null),title:"API key created",description:"Copy this key now — it will not be shown again.",footer:f.jsx(Ke,{onClick:()=>m(null),children:"Done"}),children:p?f.jsxs("div",{className:"space-y-2",children:[f.jsx(Tt,{children:"Secret key"}),f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx("code",{className:"flex-1 break-all rounded-md border bg-muted px-3 py-2 font-mono text-xs",children:p.key}),f.jsx(Ke,{variant:"outline",size:"icon",onClick:()=>j(p.key),"aria-label":"Copy key",children:f.jsx(eE,{className:"h-4 w-4"})})]})]}):null}),f.jsx(_i,{open:b!==null,onClose:()=>v(null),onConfirm:()=>b&&_.mutate(b.id),title:"Revoke this API key?",description:b?`"${b.name}" will stop working immediately. This cannot be undone.`:void 0,confirmLabel:"Revoke",destructive:!0,loading:_.isPending})]})}const Qw=[{value:"",label:"All"},{value:"bounced",label:"Bounced"},{value:"unsubscribed",label:"Unsubscribed"},{value:"complained",label:"Complained"}];function Gw({row:n}){return f.jsxs("div",{className:"flex flex-wrap gap-1",children:[n.unsubscribedAll?f.jsx(ft,{variant:"destructive",children:"Unsubscribed"}):null,n.bounceCount>0?f.jsxs(ft,{variant:"secondary",children:[ut(n.bounceCount)," bounce"]}):null,n.suppressed&&n.bounceCount===0&&!n.unsubscribedAll?f.jsx(ft,{variant:"destructive",children:"Complained"}):null,n.suppressed&&!n.unsubscribedAll&&n.bounceCount>0?f.jsx(ft,{variant:"destructive",children:"Suppressed"}):null]})}function Vw(){var m;const{toast:n}=vs(),s=ps(),[i,r]=Y.useState(""),[c,d]=Y.useState(null),h=lt({queryKey:Pe.suppressions(i),queryFn:()=>ow(i||void 0),placeholderData:ch}),y=Sa({mutationFn:b=>yb(b.userId,{suppressed:!1,unsubscribedAll:!1}),onSuccess:()=>{n({title:"Recipient restored",description:"They can receive emails again."}),d(null),s.invalidateQueries({queryKey:["suppressions"]})},onError:b=>{n({variant:"error",title:"Un-suppress failed",description:b instanceof Xt?b.message:"Unexpected error."}),d(null)}}),p=((m=h.data)==null?void 0:m.suppressions)??[];return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Suppressions",description:"Bounced, unsubscribed, and complained recipients."}),f.jsxs("div",{className:"flex max-w-xs flex-col gap-1.5",children:[f.jsx(Tt,{htmlFor:"supp-type",children:"Type"}),f.jsx(Qc,{id:"supp-type",value:i,onChange:b=>r(b.target.value),children:Qw.map(b=>f.jsx("option",{value:b.value,children:b.label},b.value))})]}),h.isPending?f.jsx(Ri,{}):h.isError?f.jsx(Ut,{error:h.error,onRetry:()=>h.refetch()}):p.length===0?f.jsx(ol,{title:"No suppressions",description:"Recipients who bounce, unsubscribe, or complain appear here."}):f.jsx("div",{className:"rounded-lg border",children:f.jsxs(cl,{children:[f.jsx(ul,{children:f.jsxs(on,{children:[f.jsx(Ce,{children:"Email"}),f.jsx(Ce,{children:"User ID"}),f.jsx(Ce,{children:"Reason"}),f.jsx(Ce,{className:"text-right",children:"Suppressed"}),f.jsx(Ce,{className:"text-right",children:"Action"})]})}),f.jsx(fl,{children:p.map(b=>f.jsxs(on,{children:[f.jsx(we,{className:"font-medium",children:b.email}),f.jsx(we,{className:"text-muted-foreground",children:b.userId}),f.jsx(we,{children:f.jsx(Gw,{row:b})}),f.jsx(we,{className:"text-right text-muted-foreground",children:Yn(b.suppressedAt)}),f.jsx(we,{className:"text-right",children:f.jsx(Ke,{variant:"outline",size:"sm",onClick:()=>d(b),children:"Un-suppress"})})]},b.id))})]})}),f.jsx(_i,{open:c!==null,onClose:()=>d(null),onConfirm:()=>c&&y.mutate(c),title:"Un-suppress this recipient?",description:c?`${c.email} will be eligible to receive emails again.`:void 0,confirmLabel:"Un-suppress",loading:y.isPending})]})}function Sc({label:n,value:s}){return f.jsxs("div",{className:"rounded-md border p-3",children:[f.jsx("p",{className:"text-xs text-muted-foreground",children:n}),f.jsx("p",{className:"text-lg font-semibold",children:s})]})}function Yw({template:n}){const{toast:s}=vs(),[i,r]=Y.useState(!1),[c,d]=Y.useState(""),h=lt({queryKey:Pe.templatePreview(n.key),queryFn:()=>XE(n.key)}),y=lt({queryKey:Pe.templateReport(n.key),queryFn:()=>JE(n.key),retry:(b,v)=>!(v instanceof Xt&&v.status===404)&&b<2}),p=Sa({mutationFn:()=>ZE(n.key,c),onSuccess:b=>{s({title:"Test email sent",description:`Status: ${b.status}.`}),r(!1),d("")},onError:b=>{s({variant:"error",title:"Send-test failed",description:b instanceof Xt?b.message:"Unexpected error."})}}),m=y.isError&&y.error instanceof Xt&&y.error.status===404;return f.jsxs("div",{className:"space-y-6",children:[f.jsxs("div",{className:"flex items-start justify-between gap-4",children:[f.jsxs("div",{className:"space-y-1",children:[f.jsxs("div",{className:"flex items-center gap-2",children:[f.jsx("h2",{className:"text-xl font-semibold tracking-tight",children:n.key}),n.category?f.jsx(ft,{variant:"secondary",children:n.category}):null]}),f.jsx("p",{className:"text-sm text-muted-foreground",children:n.defaultSubject})]}),f.jsxs(Ke,{onClick:()=>r(!0),children:[f.jsx(Yr,{className:"h-4 w-4"}),"Send test"]})]}),f.jsxs("section",{className:"space-y-3",children:[f.jsx("h3",{className:"text-sm font-semibold",children:"Performance"}),y.isPending?f.jsx(Dt,{className:"h-28 w-full"}):m?f.jsx("p",{className:"rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground",children:"This template has never been sent."}):y.isError?f.jsx(Ut,{error:y.error,onRetry:()=>y.refetch()}):y.data?f.jsxs(f.Fragment,{children:[f.jsxs("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-4",children:[f.jsx(Sc,{label:"Sent",value:ut(y.data.totals.sent)}),f.jsx(Sc,{label:"Delivered",value:ut(y.data.totals.delivered)}),f.jsx(Sc,{label:"Open rate",value:Si(y.data.totals.openRate)}),f.jsx(Sc,{label:"Click rate",value:Si(y.data.totals.clickRate)})]}),f.jsxs(Cn,{children:[f.jsx(Mn,{className:"pb-2",children:f.jsx(An,{className:"text-sm font-medium text-muted-foreground",children:"Sends over time"})}),f.jsx(On,{children:f.jsx(lh,{label:"sent",data:y.data.series.map(b=>({date:b.date,value:b.sent}))})})]})]}):null]}),f.jsxs("section",{className:"space-y-3",children:[f.jsx("h3",{className:"text-sm font-semibold",children:"Preview"}),h.isPending?f.jsx(Dt,{className:"h-96 w-full"}):h.isError?f.jsx(Ut,{error:h.error,onRetry:()=>h.refetch()}):h.data?f.jsx("div",{className:"overflow-hidden rounded-lg border bg-white",children:f.jsx("iframe",{title:`${n.key} preview`,srcDoc:h.data.html,sandbox:"",className:"h-[600px] w-full"})}):null]}),f.jsx(Hc,{open:i,onClose:()=>r(!1),title:"Send a test email",description:`Sends "${n.key}" with example props.`,footer:f.jsxs(f.Fragment,{children:[f.jsx(Ke,{variant:"outline",onClick:()=>r(!1),children:"Cancel"}),f.jsx(Ke,{onClick:()=>p.mutate(),disabled:!c||p.isPending,children:p.isPending?"Sending…":"Send test"})]}),children:f.jsxs("div",{className:"space-y-1.5",children:[f.jsx(Tt,{htmlFor:"test-to",children:"Recipient email"}),f.jsx(zt,{id:"test-to",type:"email",placeholder:"you@example.com",value:c,onChange:b=>d(b.target.value)})]})})]})}function Kw(){var d;const n=lt({queryKey:Pe.templates,queryFn:PE}),[s,i]=Y.useState(null),r=((d=n.data)==null?void 0:d.templates)??[];Y.useEffect(()=>{const h=r[0];s===null&&h&&i(h.key)},[s,r]);const c=r.find(h=>h.key===s)??null;return f.jsxs("div",{className:"space-y-6",children:[f.jsx(Ra,{title:"Templates",description:"Catalog, per-template stats, live previews, and send-test."}),n.isPending?f.jsx(Dt,{className:"h-96 w-full"}):n.isError?f.jsx(Ut,{error:n.error,onRetry:()=>n.refetch()}):r.length===0?f.jsx(ol,{title:"No templates registered",description:"Templates appear here once they're added to your email registry."}):f.jsxs("div",{className:"grid gap-6 lg:grid-cols-[260px_1fr]",children:[f.jsx("nav",{className:"space-y-1",children:r.map(h=>f.jsxs("button",{type:"button",onClick:()=>i(h.key),className:Je("w-full rounded-md px-3 py-2 text-left text-sm transition-colors",h.key===s?"bg-accent text-accent-foreground":"text-muted-foreground hover:bg-accent hover:text-accent-foreground"),children:[f.jsx("span",{className:"block font-medium",children:h.key}),h.category?f.jsx("span",{className:"block text-xs opacity-70",children:h.category}):null]},h.key))}),c?f.jsx(Yw,{template:c},c.key):null]})]})}const Kn=s2({component:BE}),Pw=ja({getParentRoute:()=>Kn,path:"/",component:Ow}),Xw=ja({getParentRoute:()=>Kn,path:"/sends",component:Bw}),Zw=ja({getParentRoute:()=>Kn,path:"/templates",component:Kw}),Jw=ja({getParentRoute:()=>Kn,path:"/journeys",component:Cw}),Fw=ja({getParentRoute:()=>Kn,path:"/buckets",component:gw}),Iw=ja({getParentRoute:()=>Kn,path:"/contacts",component:_w}),$w=ja({getParentRoute:()=>Kn,path:"/suppressions",component:Vw}),Ww=ja({getParentRoute:()=>Kn,path:"/settings",component:Hw}),eT=ja({getParentRoute:()=>Kn,path:"/debug",component:ww}),tT=Kn.addChildren([Pw,Xw,Zw,Jw,Fw,Iw,$w,eT,Ww]),nT=y2({routeTree:tT,basepath:"/studio",defaultPreload:"intent"}),gb=document.getElementById("root");if(!gb)throw new Error("Root element #root not found");_2.createRoot(gb).render(f.jsx(Y.StrictMode,{children:f.jsx(yS,{client:UE,children:f.jsx(DE,{children:f.jsx(A_,{children:f.jsx(v2,{router:nT})})})})}));
|
package/studio/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Hogsend Studio</title>
|
|
7
|
-
<script type="module" crossorigin src="/studio/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/studio/assets/index-CgJBk-Ft.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/studio/assets/index-BNDE5JtQ.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body class="h-full">
|