PageSourceSearch

https://www.namespace.world/assets/AdminCommunicationPreferences-attC0imp.js

js namespace.world collected 2026-10-09 23:12:21 UTC 6,067 bytes, 1 lines download raw bytes

1import{j as e}from"./ui-C8sRQjV2.js";import{e as re,r as a}from"./vendor-D2r5Dgbz.js";import{ab as ie,K as u,ag as ne,U as oe,S as ce,C as O,L as M,O as R,W as H,P as T,a1 as le,B as D}from"./index-Bn-QXQxN.js";import{B as q}from"./badge-Bj90dfG-.js";import{T as z,a as W,b as p,c as i,d as $,e as n}from"./table-WqNLmNmG.js";import{L as C}from"./loader-circle-B9Nxe3Zm.js";import{S as de}from"./shield-off-Ba9LA-L4.js";import"./supabase-DplbTa1P.js";const f=25,ye=()=>{const j=re(),{isAdminAuthenticated:o,isLoading:l}=ie(),[x,L]=a.useState(!0),[g,G]=a.useState([]),[y,K]=a.useState([]),[_,Z]=a.useState(0),[d,F]=a.useState(""),[N,J]=a.useState([]),[v,Q]=a.useState({}),[m,k]=a.useState(1),[b,V]=a.useState(0),[X,E]=a.useState(!1);a.useEffect(()=>{if(!l){if(!o){j("/admin/login");return}Y()}},[o,l]),a.useEffect(()=>{o&&ee()},[m,d,o]);const Y=async()=>{L(!0);const[{data:s},t,{count:r}]=await Promise.all([u.from("communication_categories").select("key,label,description,is_system,sort_order").order("sort_order"),u.rpc("get_communication_preference_stats"),u.from("profiles").select("id",{count:"exact",head:!0}).not("email","is",null)]);G(s||[]),K(t.data||[]),Z(r||0),L(!1)},ee=async()=>{E(!0);const s=(m-1)*f,t=s+f-1,{data:r,count:w}=await u.from("user_communication_preferences").select("user_id,category_key,opted_in,updated_at",{count:"exact"}).eq("opted_in",!1).order("updated_at",{ascending:!1}).range(s,t),h=r||[];J(h),V(w||0);const A=Array.from(new Set(h.map(c=>c.user_id))),B={};for(let c=0;c<A.length;c+=50){const U=A.slice(c,c+50);if(U.length===0)continue;const{data:ae}=await u.from("profiles").select("id,full_name,email").in("id",U);for(const I of ae||[])B[I.id]=I}Q(B),E(!1)},se=a.useMemo(()=>{const s={};for(const t of y)s[t.category_key]=t;return s},[y]),P=a.useMemo(()=>{if(!d.trim())return N;const s=d.trim().toLowerCase();return N.filter(t=>{const r=v[t.user_id];return r?(r.full_name||"").toLowerCase().includes(s)||(r.email||"").toLowerCase().includes(s):!1})},[N,v,d]),te=s=>{var t;return((t=g.find(r=>r.key===s))==null?void 0:t.label)||s};return l||!o?e.jsx("div",{className:"flex items-center justify-center py-24",children:e.jsx(C,{className:"h-8 w-8 animate-spin text-primary"})}):e.jsxs("div",{children:[e.jsx(ne,{title:"Communication preferences",description:"Opt-in rates per category and the users who have opted out."}),e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[e.jsx(S,{icon:e.jsx(oe,{className:"h-4 w-4"}),label:"Total users with email",value:x?"…":_}),e.jsx(S,{icon:e.jsx(ce,{className:"h-4 w-4"}),label:"Categories",value:x?"…":g.length}),e.jsx(S,{icon:e.jsx(de,{className:"h-4 w-4"}),label:"Total opt-outs",value:x?"…":y.reduce((s,t)=>s+t.opted_out_count,0)})]}),e.jsxs(O,{children:[e.jsxs(M,{children:[e.jsx(R,{children:"Opt-in rate by category"}),e.jsx(H,{children:"Users default to opted-in for every non-system category. Counts shown for users who have explicitly set a preference."})]}),e.jsx(T,{children:x?e.jsx("div",{className:"flex justify-center py-12",children:e.jsx(C,{className:"h-6 w-6 animate-spin text-primary"})}):e.jsxs(z,{children:[e.jsx(W,{children:e.jsxs(p,{children:[e.jsx(i,{children:"Category"}),e.jsx(i,{children:"Type"}),e.jsx(i,{className:"text-right",children:"Explicit opt-ins"}),e.jsx(i,{className:"text-right",children:"Opt-outs"}),e.jsx(i,{className:"text-right",children:"Effective recipients"})]})}),e.jsx($,{children:g.map(s=>{const t=se[s.key],r=(t==null?void 0:t.opted_out_count)??0,w=(t==null?void 0:t.opted_in_count)??0,h=s.is_system?_:Math.max(0,_-r);return e.jsxs(p,{children:[e.jsxs(n,{children:[e.jsx("div",{className:"font-medium",children:s.label}),s.description&&e.jsx("div",{className:"text-xs text-muted-foreground",children:s.description})]}),e.jsx(n,{children:e.jsx(q,{variant:s.is_system?"secondary":"outline",children:s.is_system?"System (always sent)":"Opt-in"})}),e.jsx(n,{className:"text-right tabular-nums",children:w}),e.jsx(n,{className:"text-right tabular-nums",children:s.is_system?"—":r}),e.jsx(n,{className:"text-right tabular-nums font-semibold",children:h})]},s.key)})})]})})]}),e.jsxs(O,{children:[e.jsxs(M,{children:[e.jsx(R,{children:"Users who opted out"}),e.jsx(H,{children:"Most recent opt-outs first. Search by name or email."})]}),e.jsxs(T,{className:"space-y-4",children:[e.jsx(le,{placeholder:"Search by name or email…",value:d,onChange:s=>F(s.target.value),className:"max-w-md"}),X?e.jsx("div",{className:"flex justify-center py-12",children:e.jsx(C,{className:"h-6 w-6 animate-spin text-primary"})}):P.length===0?e.jsx("div",{className:"text-center py-8 text-muted-foreground",children:"No opt-out
1s to show."}):e.jsxs(z,{children:[e.jsx(W,{children:e.jsxs(p,{children:[e.jsx(i,{children:"User"}),e.jsx(i,{children:"Email"}),e.jsx(i,{children:"Category opted out of"}),e.jsx(i,{children:"When"})]})}),e.jsx($,{children:P.map(s=>{const t=v[s.user_id];return e.jsxs(p,{children:[e.jsx(n,{className:"text-sm",children:(t==null?void 0:t.full_name)||"—"}),e.jsx(n,{className:"text-sm",children:(t==null?void 0:t.email)||"—"}),e.jsx(n,{children:e.jsx(q,{variant:"outline",children:te(s.category_key)})}),e.jsx(n,{className:"text-sm text-muted-foreground",children:new Date(s.updated_at).toLocaleDateString()})]},`${s.user_id}-${s.category_key}`)})})]}),b>f&&e.jsxs("div",{className:"flex justify-between items-center text-sm text-muted-foreground",children:[e.jsxs("span",{children:["Page ",m," · ",b," total opt-out records"]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(D,{variant:"outline",size:"sm",disabled:m<=1,onClick:()=>k(s=>s-1),children:"Prev"}),e.jsx(D,{variant:"outline",size:"sm",disabled:m*f>=b,onClick:()=>k(s=>s+1),children:"Next"})]})]})]})]})]})]})};function S({icon:j,label:o,value:l}){return e.jsx(O,{children:e.jsxs(T,{className:"pt-6",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:[j,o]}),e.jsx("div",{className:"text-3xl font-bold mt-2",children:l})]})})}export{ye as default};

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