PageSourceSearch

https://goa-smart-corpsec.lovable.app/assets/AdminWebForms-BdFIuUMI.js

js goa-smart-corpsec.lovable.app collected 2026-10-03 03:27:50 UTC 39,310 bytes, 1 lines download raw bytes

1import{u as w,j as e,a as Q}from"./vendor-query-CML5FxPR.js";import{r as x,L as ce}from"./vendor-react-oQp4urKO.js";import{s as S,d as de,e as me,f as ue,g as he,B as M,u as _,S as C,c as j,Q as R,I as z,Z as J,r as pe}from"./index-D63u7RRD.js";import{P as K}from"./PageHeader-CfxguHdq.js";import{B as q}from"./badge-DJn1BKj4.js";import{C as b,c as y,a as Y,b as V,d as Z}from"./card-DrvIgE54.js";import{T as xe,a as fe,b as k,c as v}from"./tabs-CPSWf_qg.js";import{u as ge}from"./usePathTab-DIXg6DtQ.js";import{u as X,a as ee}from"./untypedTable-CH1HLLK8.js";import{T as be}from"./textarea-CSX8hOKa.js";import{E as A}from"./external-link-CimBVVuD.js";import{R as ye}from"./rotate-ccw-uQ5FwdTK.js";import{f as D}from"./format-CzNdI1F_.js";import{S as je}from"./switch-DjSfhUOb.js";import{E as Ne}from"./EmailListEditor-Cu0lhoYu.js";import{F as _e,S as we}from"./useOnboarding-KgC3OgPY.js";import{u as ke}from"./useTaskCategories-Dbo9V4Cg.js";import{G as ve}from"./globe-C2AvaiFu.js";import{S as Te}from"./settings-2-BcHlHiOI.js";import{S as Se}from"./send-DMTe9QcR.js";import{L as Ce}from"./list-checks-DXj4k4Ge.js";import{F as Ee}from"./file-warning-Dn0-ROKc.js";import{T as te}from"./triangle-alert-qIrO8F1R.js";import{C as qe}from"./circle-alert-D_3ogsMq.js";import{R as Ae}from"./refresh-cw-BSpBOaJN.js";import"./vendor-ui-qQFM1ZHb.js";import"./vendor-supabase-BgQJfCCH.js";import"./vendor-charts-Du44YKL_.js";import"./plus-BsSG3PRq.js";import"./types-Bbu0IFp_.js";import"./useIncorporationEmail-DZ9suB-w.js";import"./incorporationSignal-BeJJiRvo.js";const se=()=>X("web_form_submissions"),L=()=>X("web_form_sources"),F={accepted:{label:"Accepted",tone:"ok",meaning:""},duplicate:{label:"Duplicate",tone:"info",meaning:"The same submission arrived twice. The original task was returned, nothing was created."},rejected_auth:{label:"Wrong key",tone:"error",meaning:"The site sent no intake key, or one this platform does not recognise. Nothing reached the firm — the sender's key needs fixing."},rejected_validation:{label:"Missing fields",tone:"error",meaning:"The form did not send everything required. The client saw an error; the enquiry was lost."},rejected_rate_limit:{label:"Rate limited",tone:"warn",meaning:"Too many submissions from one address in a minute. Usually a bot, occasionally a real retry loop."},rejected_method:{label:"Wrong method",tone:"warn",meaning:"Something called the endpoint with the wrong HTTP method. Usually a scanner, not a form."},rejected_honeypot:{label:"Honeypot",tone:"warn",meaning:"The decoy field was filled, so this was treated as a bot. If a real form starts appearing here, that form is sending a field named `website` and every submission from it is being silently dropped."},failed_insert:{label:"Failed",tone:"error",meaning:"The submission was valid but could not be written. This one needs replaying."},failed_attachment:{label:"Files missing",tone:"error",meaning:"The task was created but a file the client attached did not copy across. The task looks complete and is not."}},I=t=>{var s;return((s=F[t])==null?void 0:s.label)??String(t).replace(/_/g," ").replace(/\b\w/g,r=>r.toUpperCase())},ae=t=>{var s;return((s=F[t])==null?void 0:s.tone)??"warn"},Fe=t=>{var s;return((s=F[t])==null?void 0:s.meaning)??""},$e=t=>t==="rejected_validation"||t==="failed_insert"||t==="rejected_auth",U=t=>t==="accepted"||t==="duplicate"||t==="failed_attachment",Oe=Object.keys(F),re={site:null,form:null,outcomes:[],days:30},Me=t=>new Date(Date.now()-t*864e5).toISOString();function ne(t,s=200){return w({queryKey:["web-form-submissions",t,s],queryFn:async()=>{let r=se().select("*").gte("received_at",Me(t.days)).order("received_at",{ascending:!1}).limit(s);t.site&&(r=r.eq("source_site",t.site)),t.form&&(r=r.eq("source_form",t.form)),t.outcomes.length>0&&(r=r.in("outcome",t.outcomes));const{data:o,error:a}=await r.returns();if(a)throw a;return o??[]},staleTime:3e4})}function P(){return w({queryKey:["web-form-sources"],queryFn:async()=>{const{data:t,error:s}=await L().select("*").order("site_key").order("form_key").returns();if(s)throw s;return t??[]},staleTime:5*6e4})}function Re(){return w({queryKey:["web-form-paths"],queryFn:async()=>{var o;const{data:t,error:s}=await se().select("source_site, source_form, payload, received_at").order("received_at",{ascending:!1}).limit(500).returns();if(s)throw s;const r={};for(const a of t??[]){if(!a.source_site||!a.source_form)continue;const c=`${a.source_site}/${a.source_form}`;if(r[c])continue;const m=(o=a.payload)==null?void 0:o.path;typeof m=="string"&&m.trim()&&(r[c]=m.trim())}return r},staleTime:5*6e4})}function H(){return w({queryKey:["web-form-activity"],queryFn:async()=>{const{data:t,error:s}=await S.rpc("web_form_activity").returns();if(s)throw s;return t??[]},staleTime:6e4})}function De(t){const s=Array.from(new Set(t.filter(Boolean))).sort();return w({queryKey:["web-form-task-titles",s],enabled:s.length>0,staleTime:5*6e4,queryFn:async()=>{const{data:r,error:o}=await S.from("task_items").select("id, title, reference_no").in("id",s);if(o)throw o;return Object.fromEntries((r??[]).map(a=>[a.id,a.title??a.reference_no??"Task"]))}})}const Le=168,Ie=3;function ie(t){const{source:s,lastReceivedAt:r,now:o}=t;if(!s.is_active||!s.expected_per_week||s.expected_per_week<=0)return null;const a=Le/s.expected_per_week,c=s.silence_alert_hours??Math.round(a*Ie);if(!r)return{silent:!0,hoursSince:null,expectedHours:a,thresholdHours:c,neverReceived:!0}
1;const m=Date.parse(r);if(Number.isNaN(m))return null;const d=Math.max(0,(o-m)/36e5);return{silent:d>c,hoursSince:d,expectedHours:a,thresholdHours:c,neverReceived:!1}}function oe(t){if(t.neverReceived)return"has never received a submission";const s=t.hoursSince??0,r=s>=48?`${Math.floor(s/24)} days`:`${Math.round(s)}h`,o=t.expectedHours>=48?`${Math.round(t.expectedHours/24)} days`:`${Math.round(t.expectedHours)}h`;return`quiet for ${r} — one every ${o} is normal here`}function Pe({row:t,onClose:s,onReplayed:r}){const[o,a]=x.useState(""),[c,m]=x.useState(!1),[d,i]=x.useState(!1);if(x.useEffect(()=>{a(JSON.stringify((t==null?void 0:t.payload)??{},null,2)),i(!1)},[t]),!t)return null;const n=Fe(t.outcome),u=$e(t.outcome)&&!t.task_id,l=async()=>{var E,N,W;let h;if(d)try{h=JSON.parse(o)}catch{_.error("That is not valid JSON",{description:"Fix the highlighted braces or quotes before sending."});return}m(!0);const{data:g,error:p}=await S.functions.invoke("replay-web-form",{body:{submissionId:t.id,payload:h}});if(m(!1),p){_.error("Replay failed",{description:p.message});return}const f=g;if(!(f!=null&&f.ok)){const B=(E=f==null?void 0:f.result)==null?void 0:E.fields;_.error(((N=f==null?void 0:f.result)==null?void 0:N.error)??"The submission was rejected again",{description:B?`Still wrong: ${Object.keys(B).join(", ")}`:void 0}),r();return}_.success(`Task ${((W=f.result)==null?void 0:W.reference_no)??""} created`,{description:"The original record is unchanged — the replay was added as a new entry."}),r(),s()};return e.jsx(de,{open:!!t,onOpenChange:h=>!h&&s(),children:e.jsxs(me,{className:"w-full sm:max-w-2xl overflow-y-auto",children:[e.jsx(ue,{children:e.jsxs(he,{className:"text-base",children:[I(t.outcome)," · ",D(new Date(t.received_at),"d MMM yyyy HH:mm")]})}),e.jsxs("div",{className:"mt-4 space-y-4",children:[n&&e.jsx("p",{className:"rounded-md border bg-muted/40 px-3 py-2 text-[13px] text-muted-foreground",children:n}),e.jsxs("dl",{className:"grid grid-cols-[8rem_1fr] gap-x-3 gap-y-1.5 text-[13px]",children:[e.jsx(T,{label:"Site",value:t.source_site}),e.jsx(T,{label:"Form",value:t.source_form_label||t.source_form}),e.jsx(T,{label:"Reference",value:t.reference_no}),e.jsx(T,{label:"External ref",value:t.external_ref}),e.jsx(T,{label:"IP",value:t.ip})]}),t.error&&e.jsxs("div",{className:"rounded-md border border-rose-500/30 bg-rose-500/5 px-3 py-2",children:[e.jsx("p",{className:"text-[11px] uppercase tracking-wider text-muted-foreground",children:"What went wrong"}),e.jsx("p",{className:"text-[13px] mt-0.5",children:t.error}),t.field_errors&&Object.keys(t.field_errors).length>0&&e.jsx("ul",{className:"mt-1.5 space-y-0.5",children:Object.entries(t.field_errors).map(([h,g])=>e.jsxs("li",{className:"text-[12px]",children:[e.jsx("code",{className:"rounded bg-muted px-1 py-0.5 text-[11px]",children:h})," ",e.jsx("span",{className:"text-muted-foreground",children:g})]},h))})]}),t.task_id&&e.jsxs("a",{href:`/tasks?task=${t.task_id}`,target:"_blank",rel:"noreferrer",className:"inline-flex items-center gap-1.5 text-[13px] text-primary hover:underline",children:["Open the task this created ",e.jsx(A,{className:"h-3 w-3"})]}),t.replayed_from&&e.jsx("p",{className:"text-[12px] text-muted-foreground",children:"This entry is a replay of an earlier submission."}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsx("p",{className:"text-[11px] uppercase tracking-wider text-muted-foreground",children:"What the website sent"}),u&&!d&&e.jsx(M,{size:"sm",variant:"ghost",className:"h-6 text-[11px]",onClick:()=>i(!0),children:"Edit before replaying"})]}),e.jsx(be,{value:o,onChange:h=>a(h.target.value),readOnly:!d,rows:14,className:"font-mono text-[11px] leading-relaxed"})]}),u&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(M,{size:"sm",onClick:l,disabled:c,className:"gap-1.5",children:[e.jsx(ye,{className:"h-3.5 w-3.5"}),c?"Sending…":"Replay this submission"]}),e.jsx("span",{className:"text-[11px] text-muted-foreground",children:"Runs the same path a live submission takes."})]})]})]})})}function T({label:t,value:s}){return s?e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:t}),e.jsx("dd",{className:"truncate",children:s})]}):null}function He({value:t,placeholder:s,onSave:r}){const[o,a]=x.useState(null),c=o??(t===null?"":String(t)),m=async()=>
1{if(o===null)return;const d=o.trim(),i=d===""?null:Number(d);if(i!==null&&(!Number.isFinite(i)||i<=0)){_.error("Enter a positive number, or leave it empty to stop watching"),a(null);return}a(null),i!==t&&await r(i)};return e.jsx(z,{value:c,placeholder:s,onChange:d=>a(d.target.value),onBlur:m,onKeyDown:d=>{d.key==="Enter"&&d.target.blur(),d.key==="Escape"&&a(null)},className:"h-7 w-20 text-[12px] tabular-nums"})}function We(){const t=Q(),{data:s,isLoading:r}=P(),{data:o}=H(),a=x.useMemo(()=>new Map((o??[]).map(i=>[`${i.site_key}/${i.form_key}`,i])),[o]),c=async(i,n)=>{const{error:u}=await L().update(ee({...n,updated_at:new Date().toISOString()})).eq("id",i);if(u){_.error("Could not save",{description:u.message});return}t.invalidateQueries({queryKey:["web-form-sources"]})},m=x.useMemo(()=>{const i=new Map;for(const n of s??[]){const u=i.get(n.site_key)??{label:n.site_label,rows:[]};u.rows.push(n),i.set(n.site_key,u)}return Array.from(i.entries())},[s]);if(r)return e.jsx(C,{className:"h-64 w-full"});if((s??[]).length===0)return e.jsx(b,{children:e.jsxs(y,{className:"p-6 text-sm text-muted-foreground",children:["No forms are registered. Migration"," ",e.jsx("code",{className:"rounded bg-muted px-1 text-[11px]",children:"20260820110000"})," seeds the ones the websites already send — if this is empty, it has not been applied yet."]})});const d=Date.now();return e.jsxs("div",{className:"space-y-4",children:[e.jsx("p",{className:"text-[13px] text-muted-foreground",children:"A form is only watched once you say how many submissions a week to expect. Leave it empty and nothing is flagged — an invented threshold gets switched off within a week and is worse than no check at all."}),e.jsxs("p",{className:"text-[13px] text-muted-foreground",children:["Who is emailed when a submission arrives is set under"," ",e.jsx(ce,{to:"/admin/web-forms/emails",className:"text-primary underline underline-offset-2",children:"the Emails tab"}),", not on the websites. An empty list there means a submission is recorded and nobody is told."]}),m.map(([i,n])=>e.jsx(b,{children:e.jsxs(y,{className:"p-0",children:[e.jsx("div",{className:"border-b px-3 py-2",children:e.jsx("p",{className:"text-sm font-medium",children:n.label})}),e.jsxs("div",{className:"hidden sm:grid grid-cols-[1fr_5rem_5rem_6rem_7rem_7rem_3rem] gap-2 border-b bg-muted/30 px-3 py-1.5 text-[10px] uppercase tracking-wider text-muted-foreground",children:[e.jsx("span",{children:"Form"}),e.jsx("span",{className:"text-right",children:"This week"}),e.jsx("span",{className:"text-right",children:"Last week"}),e.jsx("span",{className:"text-right",children:"Turned away"}),e.jsx("span",{children:"Last arrival"}),e.jsx("span",{children:"Expect /week"}),e.jsx("span",{className:"text-right",children:"On"})]}),n.rows.map(u=>{const l=a.get(`${u.site_key}/${u.form_key}`),h=ie({source:u,lastReceivedAt:(l==null?void 0:l.last_accepted_at)??null,now:d});return e.jsxs("div",{className:j("grid grid-cols-2 sm:grid-cols-[1fr_5rem_5rem_6rem_7rem_7rem_3rem] gap-2 items-center border-b px-3 py-2 last:border-b-0",!u.is_active&&"opacity-50"),children:[e.jsxs("div",{className:"col-span-2 sm:col-span-1 min-w-0",children:[e.jsx("p",{className:"text-[13px] font-medium truncate",children:u.form_tag}),e.jsxs("p",{className:"text-[11px] text-muted-foreground truncate",children:[e.jsx("code",{className:"text-[10px]",children:u.form_key}),(h==null?void 0:h.silent)&&e.jsxs("span",{className:"text-amber-600 dark:text-amber-400",children:[" · ",oe(h)]})]})]}),e.jsx("span",{className:"text-right text-[13px] tabular-nums",children:(l==null?void 0:l.this_week)??0}),e.jsx("span",{className:"text-right text-[13px] tabular-nums text-muted-foreground",children:(l==null?void 0:l.last_week)??0}),e.jsx("span",{className:j("text-right text-[13px] tabular-nums",((l==null?void 0:l.rejected_30d)??0)>0?"text-rose-500":"text-muted-foreground"),children:(l==null?void 0:l.rejected_30d)??0}),e.jsx("span",{className:"text-[11px] text-muted-foreground",children:l!=null&&l.last_accepted_at?`${R(new Date(l.last_accepted_at))} ago`:"never"}),e.jsx(He,{value:u.expected_per_week,placeholder:"—",onSave:g=>c(u.id,{expected_per_week:g})}),e.jsx("span",{className:"flex justify-end",children:e.jsx(je,{checked:u.is_active,onCheckedChange:g=>c(u.id,{is_active:g}),"aria-label":`${u.form_tag} watched`})})]},u.id)})]})},i))]})}function G({source:t,field:s,onSave:r}){const[o,a]=x.useState(!1),[c,m]=x.useState(""),d=s==="cc"?t.notify_cc_emails:t.notify_emails,i=Array.isArray(d)?d:[],n=t.notify_emails_source==="site",u=s==="cc"?"Site CC":"Site emails",l=async()=>{const h=c.split(/[,;\s]+/).map(p=>
1p.trim().toLowerCase()).filter(Boolean),g=h.find(p=>!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(p));if(g){_.error(`${g} is not an email address`);return}a(!1),h.join(",")!==i.join(",")&&await r(h)};return o?e.jsx(z,{autoFocus:!0,value:c,placeholder:"[email protected], [email protected]",onChange:h=>m(h.target.value),onBlur:l,onKeyDown:h=>{h.key==="Enter"&&h.target.blur(),h.key==="Escape"&&a(!1)},className:"mt-1 h-6 text-[11px]"}):e.jsx("button",{type:"button",onClick:()=>{m(i.join(", ")),a(!0)},className:"mt-0.5 block max-w-full truncate text-left text-[11px] text-muted-foreground hover:text-foreground",title:i.length?`${u}: ${i.join(", ")}${n?" (reported by the site)":" (entered here by hand)"}`:`Nothing recorded. Click to note the ${s==="cc"?"addresses this website copies":"addresses this website sends to"}.`,children:i.length===0?e.jsxs("span",{className:"text-muted-foreground/60",children:[u,": not recorded"]}):e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"text-muted-foreground/70",children:[u,":"]})," ",i.join(", "),e.jsx("span",{className:"text-muted-foreground/60",children:n?" · reported":" · noted by hand"})]})})}const Be=[{key:"web_intake_notify_emails",title:"Website enquiries",trigger:"A client submits one of the forms listed above, on any of the three websites.",body:"The task is created either way; this is who the platform emails about it. Skipped when the task already has an assignee or watcher — then it goes to them instead.",empty:"Nobody is emailed. The submission is still recorded and still appears on the Inbox tab."},{key:"doc_upload_notify_emails",title:"Client document uploads",trigger:"A document is uploaded under Company → Documents, or produced by the Create Resolution dialog.",body:"This is the internal side; who at the client is told is set per company.",empty:"No internal notice is sent for uploads."}],Ke=[{key:"compliance_notify_emails",title:"Compliance chases",trigger:"Nothing. The Send Compliance Reminder dialog exists but no button opens it.",unreachable:"Leave this empty. Nothing can reach the feature, and the dialog it belongs to lists a company's directors as recipients while sending to this list instead — so it would need fixing before it were wired up, not filling in.",body:"Would be the recipients of a compliance notice sent by hand from a company profile.",empty:"Nothing happens either way."},{key:"payment_notify_emails",title:"Payment reminders",trigger:"Company → Billing, sending a payment reminder with Admin ticked as one of the audiences.",body:"These addresses are added to the recipients. Client contacts are chosen separately in the same dialog.",empty:"Ticking Admin adds nobody; the reminder still goes to whatever client contacts were picked."},{key:"global_cc_emails",title:"Global CC",trigger:"Every outgoing email the platform sends: automated reminders, KYC requests, new member notices, company mail.",body:"Was written into seven edge functions, so it could not be seen or changed without a deploy.",empty:"Nothing is CC'd."},{key:"system_cc_email",title:"Documents sent from a company",trigger:"Company → Documents, emailing a document to a client.",body:"Overrides the Global CC for that one mail rather than adding to it, so nobody is copied twice.",empty:"The Global CC above is used instead."}],Ue=[{key:"todo_notify_emails",title:"To-Do task activity",trigger:"Nothing reads this list.",unreachable:"Nothing is sent from here. Task email goes to the people on the task — creator, assignees, watchers — and no function reads this setting, so an address added here is never emailed. Follow a task to hear about it.",body:"Was intended as a fixed list emailed on task activity.",empty:"No difference either way."},{key:"todo_cc_emails",title:"To-Do CC",trigger:"Nothing reads this list.",unreachable:"Same as above: no function reads it. The Global CC is what reaches task mail.",body:"Was intended as a CC on the list above.",empty:"No difference either way."}];function $({icon:t,title:s,description:r,audiences:o}){return e.jsxs(b,{children:[e.jsxs(Y,{className:"pb-3",children:[e.jsxs(V,{className:"text-base flex items-center gap-2",children:[t," ",s]}),e.jsx(Z,{children:r})]}),e.jsx(y,{className:"space-y-5 pt-0",children:o.map(a=>e.jsxs("div",{className:j("space-y-1.5",a.unreachable&&"opacity-70"),children:[e.jsxs("p",{className:"text-sm font-medium",children:[a.title,a.unreachable&&e.jsx("span",{className:"ml-2 roun
1ded bg-muted px-1.5 py-0.5 text-[10px] font-normal text-muted-foreground",children:"nothing triggers this"})]}),e.jsxs("p",{className:"text-[11px] text-foreground/70",children:[e.jsx("span",{className:"text-muted-foreground/70",children:"Sent when:"})," ",a.trigger]}),e.jsx("p",{className:"text-[11px] text-muted-foreground",children:a.body}),a.unreachable&&e.jsx("p",{className:"text-[11px] text-amber-700 dark:text-amber-400",children:a.unreachable}),e.jsx(Ne,{settingKey:a.key,canEdit:!0}),e.jsxs("p",{className:"text-[11px] text-muted-foreground/80",children:["Empty: ",a.empty]})]},a.key))})]})}function Ge({source:t}){const{visibleCategories:s}=ke(),r=s.find(c=>c.id===t.category),o=r==null?void 0:r.subTypes.find(c=>c.id===t.sub_type),a=_e[t.form_key];return e.jsxs("p",{className:"text-[11px] text-muted-foreground",children:[r?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-muted-foreground/70",children:"Creates:"})," ",r.label,o?` / ${o.label}`:""]}):e.jsx("span",{className:"text-muted-foreground/60",children:"Creates: an unfiled task — staff choose the category"}),a?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-muted-foreground/70",children:" · Outstanding:"})," ",we[a]]}):e.jsx("span",{className:"text-muted-foreground/60",children:" · not on Outstanding"})]})}function Qe(){const{data:t=[],isLoading:s}=P(),{data:r={}}=Re(),{data:o=[]}=H(),a=Q(),c=x.useMemo(()=>new Map(o.map(i=>[`${i.site_key}/${i.form_key}`,i.last_accepted_at])),[o]),m=x.useMemo(()=>{const i=new Map;for(const n of t){const u=i.get(n.site_key)??{label:n.site_label,rows:[]};u.rows.push(n),i.set(n.site_key,u)}return Array.from(i.entries())},[t]),d=async(i,n)=>{const{error:u}=await L().update(ee({...n,updated_at:new Date().toISOString()})).eq("id",i);if(u){_.error("Could not save",{description:u.message});return}a.invalidateQueries({queryKey:["web-form-sources"]})};return e.jsxs("div",{className:"space-y-6",children:[e.jsx("p",{className:"text-[13px] text-muted-foreground",children:"Two different things, and they are easy to confuse. The websites send their own confirmation when a form is submitted, configured in their own projects — that is the first section. This platform emails the firm when the submission arrives here — that is the second."}),e.jsxs(b,{children:[e.jsxs(Y,{className:"pb-3",children:[e.jsxs(V,{className:"text-base flex items-center gap-2",children:[e.jsx(ve,{className:"h-4 w-4"})," What each website sends"]}),e.jsxs(Z,{children:["Where each form sits, what it becomes once it arrives, and who the site emails. Every form links to the page a client starts on, and each site links to its own recipients console — the one that decides the confirmation the client receives, which this platform never sends. A site that posts ",e.jsx("code",{className:"mx-1 text-[11px]",children:"notify_emails"}),"with a submission keeps its row current and is marked ",e.jsx("em",{children:"reported"}),"; anything typed in here is a note, as current as the day it was written."]})]}),e.jsxs(y,{className:"space-y-4 pt-0",children:[s&&e.jsx(C,{className:"h-24 w-full"}),m.map(([i,n])=>{var h,g;const u=((h=n.rows.find(p=>p.site_url))==null?void 0:h.site_url)??null,l=((g=n.rows.find(p=>p.email_admin_path))==null?void 0:g.email_admin_path)??null;return e.jsxs("div",{className:"rounded-lg border",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2",children:[e.jsx("p",{className:"text-sm font-medium",children:n.label}),u&&l&&e.jsxs("a",{href:`${u}${l}`,target:"_blank",rel:"noreferrer",className:"inline-flex items-center gap-1 text-[11px] text-primary hover:underline",children:[e.jsx(Te,{className:"h-3 w-3"})," Manage this site's recipients",e.jsx(A,{className:"h-3 w-3"})]})]}),n.rows.map(p=>{const f=r[`${p.site_key}/${p.form_key}`],E=c.get(`${p.site_key}/${p.form_key}`);return e.jsxs("div",{className:"grid gap-1 border-b px-3 py-2.5 last:border-b-0 sm:grid-cols-[14rem_1fr]",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("p",{className:"text-[13px] font-medium truncate",children:p.form_tag}),e.jsxs("p",{className:"text-[11px] text-muted-foreground truncate",children:[e.jsx("code",{className:"text-[10px]",children:p.form_key}),!p.is_active&&e.jsx("span",{children:" · off"})]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsxs("p",{className:"text-[11px] text-muted-foreground truncate",children:[p.page_path&&p.site_url?e.jsxs("a",{href:`${p.site_url}${p.page_path}`,target:"_blank",rel:"noreferrer",className:"inline-flex items-center gap-1 text-primary hover:underline",children:[p.site_url.replace(/^https?:\/\//,""),p.page_path,e.jsx(A,{className:"h-3 w-3"})]}):f?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-muted-foreground
1/70",children:"Last submitted from:"})," ",e.jsx("code",{className:"text-[10px]",children:f})]}):e.jsx("span",{className:"text-muted-foreground/60",children:E?"Page not recorded":"No page recorded, and no submission yet"}),p.page_path&&f&&f!==p.page_path&&e.jsxs("span",{className:"text-muted-foreground/60",children:[" · last submitted from ",e.jsx("code",{className:"text-[10px]",children:f})]})]}),e.jsx(Ge,{source:p}),e.jsx(G,{source:p,field:"to",onSave:N=>d(p.id,{notify_emails:N,notify_emails_source:N.length?"manual":null,notify_emails_seen_at:N.length?new Date().toISOString():null})}),e.jsx(G,{source:p,field:"cc",onSave:N=>d(p.id,{notify_cc_emails:N,notify_emails_source:"manual",notify_emails_seen_at:new Date().toISOString()})})]})]},p.id)})]},i)}),!s&&m.length===0&&e.jsx("p",{className:"text-[13px] text-muted-foreground",children:"No forms registered yet."})]})]}),e.jsx($,{icon:e.jsx(J,{className:"h-4 w-4"}),title:"What this platform sends when something arrives",description:"Named addresses, not roles — these used to be resolved by expanding a role, so granting somebody admin quietly subscribed them to clients' mail. An empty list emails nobody, and that is a choice rather than an oversight.",audiences:Be}),e.jsx($,{icon:e.jsx(Se,{className:"h-4 w-4"}),title:"Copied on mail we send out",description:"These reach a client's inbox, so an address here is visible to them. The first two are recipient lists despite the heading — they decide who the notice is addressed to.",audiences:Ke}),e.jsx($,{icon:e.jsx(Ce,{className:"h-4 w-4"}),title:"Internal task activity",description:"Nothing here reaches a client — and, as it stands, nothing here is read at all. Task email follows the people on the task.",audiences:Ue})]})}const ze={director:"Director change",shareholder:"Shareholder change",officer:"Officer change",secretary:"Secretary change"},Je=t=>ze[t]??t.replace(/_/g," ").replace(/\b\w/g,s=>s.toUpperCase()),Ye={pending:"bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/30",approved:"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",rejected:"bg-rose-500/10 text-rose-600 dar
1k:text-rose-400 border-rose-500/30"},Ve=t=>t.replace(/_/g," ").replace(/\b\w/g,s=>s.toUpperCase());function Ze(t){return w({queryKey:["portal-person-requests",t],staleTime:6e4,queryFn:async()=>{const s=new Date(Date.now()-t*864e5).toISOString(),{data:r,error:o}=await S.from("company_person_requests").select("id, created_at, company_id, request_type, status, submitted_name, submitted_email, pdf_path, task_id, auto_approved, reviewed_at").gte("created_at",s).is("deleted_at",null).order("created_at",{ascending:!1}).limit(300);if(o)throw o;const a=r??[],c=Array.from(new Set(a.map(d=>d.company_id).filter(Boolean)));let m={};if(c.length){const{data:d}=await S.from("companies").select("id, name").in("id",c);m=Object.fromEntries((d??[]).map(i=>[i.id,i.name]))}return{rows:a,companies:m}}})}function Xe(){const[t,s]=x.useState(30),[r,o]=x.useState(!1),{data:a,isLoading:c,error:m}=Ze(t),d=x.useMemo(()=>{const n=(a==null?void 0:a.rows)??[];return r?n.filter(u=>!u.pdf_path):n},[a,r]),i=((a==null?void 0:a.rows)??[]).filter(n=>!n.pdf_path).length;return m?e.jsx(b,{className:"border-rose-500/40 bg-rose-500/5",children:e.jsxs(y,{className:"p-4 text-sm",children:["Could not read the portal requests: ",m.message]})}):e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-[13px] text-muted-foreground",children:"Officer and shareholder changes clients submit from the client portal. These arrive through their own path, not the public form endpoint, so they are read here rather than duplicated into the Inbox."}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[i>0&&e.jsxs("button",{type:"button",onClick:()=>o(n=>!n),className:j("rounded-full border px-2.5 py-1 text-[11px] transition-colors",r?"bg-rose-500/10 text-rose-600 dar
1k:text-rose-400 border-rose-500/40":"border-rose-500/30 text-rose-600 dark:text-rose-400"),children:[e.jsx(Ee,{className:"inline h-3 w-3 mr-1"}),"No signed copy ",e.jsx("span",{className:"tabular-nums",children:i})]}),e.jsx("span",{className:"ml-auto inline-flex rounded-md border p-0.5",children:[30,90,365].map(n=>e.jsx("button",{type:"button",onClick:()=>s(n),className:j("rounded px-2 py-0.5 text-[11px]",t===n?"bg-primary text-primary-foreground":"text-muted-foreground hover:text-foreground"),children:n===365?"1y":`${n}d`},n))})]}),i>0&&!r&&e.jsx(b,{className:"border-amber-500/40 bg-amber-500/5",children:e.jsxs(y,{className:"flex items-start gap-3 p-3",children:[e.jsx(te,{className:"mt-0.5 h-4 w-4 shrink-0 text-amber-500"}),e.jsxs("p",{className:"text-[13px]",children:[e.jsxs("span",{className:"font-medium",children:[i," ",i===1?"request has":"requests have"," no signed copy on file."]})," ",e.jsx("span",{className:"text-muted-foreground",children:"The client signed and was told the form was submitted, but the PDF never reached storage. Their answers are intact — only the signed document is missing."})]})]})}),e.jsx(b,{children:e.jsx(y,{className:"p-0",children:c?e.jsx("div",{className:"space-y-2 p-3",children:[0,1,2].map(n=>e.jsx(C,{className:"h-12 w-full"},n))}):d.length===0?e.jsx("p",{className:"p-6 text-center text-sm text-muted-foreground",children:r?"Every request in this period has its signed copy on file.":`No portal requests in the last ${t} days.`}):d.map(n=>e.jsxs("div",{className:"flex flex-col gap-1.5 border-b px-3 py-2.5 last:border-b-0 sm:flex-row sm:items-start sm:gap-3",children:[e.jsx("span",{className:"w-28 shrink-0 text-[11px] text-muted-foreground tabular-nums",children:D(new Date(n.created_at),"d MMM HH:mm")}),e.jsxs("span",{className:"min-w-0 flex-1",children:[e.jsxs("span",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"text-[13px] font-medium",children:Je(n.request_type)}),e.jsx(q,{variant:"outline",className:j("text-[10px]",Ye[n.status]),children:Ve(n.status)}),n.auto_approved&&e.jsx(q,{variant:"outline",className:"text-[10px]",children:"Auto-approved"}),!n.pdf_path&&e.jsx(q,{variant:"outline",className:"text-[10px] border-rose-500/40 text-rose-600 dar
1k:text-rose-400",children:"No signed copy"})]}),e.jsxs("span",{className:"block text-[11px] text-muted-foreground truncate mt-0.5",children:[(a==null?void 0:a.companies[n.company_id])??"Unknown company",n.submitted_name&&e.jsxs(e.Fragment,{children:[" · ",n.submitted_name]}),n.submitted_email&&e.jsxs(e.Fragment,{children:[" · ",n.submitted_email]})]})]}),e.jsxs("span",{className:"flex shrink-0 items-center gap-3 text-[11px]",children:[e.jsx("span",{className:"text-muted-foreground",children:n.reviewed_at?`reviewed ${R(new Date(n.reviewed_at))} ago`:"awaiting review"}),n.task_id&&e.jsxs("a",{href:`/tasks?task=${n.task_id}`,target:"_blank",rel:"noreferrer",className:"inline-flex items-center gap-1 text-primary hover:underline",children:["Task ",e.jsx(A,{className:"h-3 w-3"})]})]})]},n.id))})})]})}const le={ok:"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",info:"bg-sky-500/10 text-sky-600 dark:text-sky-400 border-sky-500/30",warn:"bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/30",error:"bg-rose-500/10 text-rose-600 dar
1k:text-rose-400 border-rose-500/30"};function et({outcome:t}){return e.jsx(q,{variant:"outline",className:j("text-[10px] font-medium",le[ae(t)]),children:I(t)})}function tt({row:t,taskTitle:s,onOpen:r}){var m,d;const o=String(((m=t.payload)==null?void 0:m.full_name)??"")||String(((d=t.payload)==null?void 0:d.email)??"")||"—",a=t.source_site??"unknown site",c=t.source_form_label||t.source_form||"unknown form";return e.jsx("button",{type:"button",onClick:r,className:"w-full text-left px-3 py-2.5 hover:bg-muted/50 transition-colors border-b last:border-b-0",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("span",{className:"w-28 shrink-0 text-[11px] text-muted-foreground tabular-nums pt-0.5",children:D(new Date(t.received_at),"d MMM HH:mm")}),e.jsxs("span",{className:"flex-1 min-w-0",children:[e.jsxs("span",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"text-sm font-medium truncate",children:o}),e.jsx(et,{outcome:t.outcome})]}),e.jsxs("span",{className:"block text-[11px] text-muted-foreground truncate mt-0.5",children:[a," · ",c,s&&e.jsxs("span",{className:"text-foreground/70",children:[" → ",s]}),t.error&&e.jsxs("span",{className:"text-rose-500",children:[" · ",t.error]})]})]})]})})}function st(){const[t,s]=x.useState(re),[r,o]=x.useState(null),{data:a,isLoading:c,error:m,refetch:d}=ne(t),{data:i}=De((a??[]).map(l=>l.task_id??"")),n=x.useMemo(()=>{const l={};for(const h of a??[])l[h.outcome]=(l[h.outcome]??0)+1;return l},[a]),u=l=>{s(h=>{const g=h.outcomes.includes(l);return{...h,outcomes:g?h.outcomes.filter(p=>p!==l):[...h.outcomes,l]}})};return m?e.jsx(b,{className:"border-rose-500/40 bg-rose-500/5",children:e.jsxs(y,{className:"flex items-start gap-3 p-4 text-sm",children:[e.jsx(qe,{className:"mt-0.5 h-4 w-4 shrink-0 text-rose-500"}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"font-medium",children:"The intake book could not be read."}),e.jsxs("p",{className:"text-muted-foreground",children:[m.message," — if the table is missing, migration"," ",e.jsx("code",{className:"rounded bg-muted px-1 text-[11px]",children:"20260820110000"})," has not been applied yet."]})]})]})}):e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[Oe.filter(l=>n[l]).map(l=>e.jsxs("button",{type:"button",onClick:()=>u(l),className:j("rounded-full border px-2.5 py-1 text-[11px] transition-colors",t.outcomes.includes(l)?le[ae(l)]:"border-border text-muted-foreground hover:text-foreground"),children:[I(l)," ",e.jsx("span",{className:"tabular-nums opacity-70",children:n[l]})]},l)),e.jsx("span",{className:"ml-auto inline-flex rounded-md border p-0.5",children:[7,30,90].map(l=>e.jsxs("button",{type:"button",onClick:()=>s(h=>({...h,days:l})),className:j("rounded px-2 py-0.5 text-[11px]",t.days===l?"bg-primary text-primary-foreground":"text-muted-foreground hover:text-foreground"),children:[l,"d"]},l))}),e.jsxs(M,{size:"sm",variant:"ghost",className:"h-7 gap-1.5 text-xs",onClick:()=>d(),children:[e.jsx(Ae,{className:"h-3 w-3"})," Refresh"]})]}),e.jsx(b,{children:e.jsx(y,{className:"p-0",children:c?e.jsx("div",{className:"space-y-2 p-3",children:[0,1,2,3].map(l=>e.jsx(C,{className:"h-10 w-full"},l))}):(a??[]).length===0?e.jsxs("p",{className:"p-6 text-center text-sm text-muted-foreground",children:["Nothing arrived in the last ",t.days," days.",t.outcomes.length>0&&" Try clearing the filters."]}):(a??[]).map(l=>e.jsx(tt,{row:l,taskTitle:l.task_id?i==null?void 0:i[l.task_id]:void 0,onOpen:()=>o(l)},l.id))})}),e.jsx(Pe,{row:r,onClose:()=>o(null),onReplayed:()=>d()})]})}function at(){const{data:t}=P(),{data:s}=H(),r=x.useMemo(()=>{if(!t||!s)return[];const o=Date.now(),a=new Map(s.map(c=>[`${c.site_key}/${c.form_key}`,c]));return t.map(c=>{const m=a.get(`${c.site_key}/${c.form_key}`),d=ie({source:c,lastReceivedAt:(m==null?void 0:m.last_accepted_at)??null,now:o});return d!=null&&d.silent?{source:c,verdict:d}:null}).filter(c=>c!==null)},[t,s]);return r.length===0?null:e.jsx(b,{className:"border-amber-500/40 bg-amber-500/5",children:e.jsxs(y,{className:"flex items-start gap-3 p-4",children:[e.jsx(te,{className:"mt-0.5 h-4 w-4 shrink-0 text-amber-500"}),e.jsxs("div",{className:"space-y-1 text-sm",children:[e.jsxs("p",{className:"font-medium",children:[r.length," ",r.length===1?"form has":"forms have"," gone quiet"]}),e.jsx("ul",{className:"space-y-0.5 text-muted-foreground text-[13px]",children:r.slice(0,5).map(({source:o,verdict:a})=>e.jsxs("li",{children:[e.jsxs("span",{className:"text-foreground",children:[o.brand_tag," · ",o.form_tag]})," — ",oe(a)]},o.id))}),r.length>5&&e.jsxs("p",{className:"text-muted-foreground text-[13px]",children:["and ",r.length-5," more, on the Forms tab."]})]})]})})}function rt(){const{data:t,isLoading:s}=ne({...re,days:30},1e3),r=x.useMemo(()=>{var d;const o=t??[],a=o.filter(i=>U(i.outcome)).length,c=o.filter(i=>!U(i.outcome)).length,m=new Map;for(const i of o)for(const n of Object.keys(i.field_errors??{}
1))m.set(n,(m.get(n)??0)+1);return{arrived:a,lost:c,total:o.length,topFields:Array.from(m.entries()).sort((i,n)=>n[1]-i[1]).slice(0,6),lastAt:((d=o[0])==null?void 0:d.received_at)??null}},[t]);return s?e.jsx(C,{className:"h-48 w-full"}):e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid gap-2 sm:grid-cols-3",children:[e.jsx(O,{label:"Real enquiries",value:String(r.arrived),hint:"last 30 days"}),e.jsx(O,{label:"Turned away",value:String(r.lost),hint:"rejected or failed"}),e.jsx(O,{label:"Last arrival",value:r.lastAt?`${R(new Date(r.lastAt))} ago`:"—",hint:"any site"})]}),e.jsx(b,{children:e.jsxs(y,{className:"p-4 space-y-2",children:[e.jsx("p",{className:"text-sm font-medium",children:"Fields the websites keep failing to send"}),r.topFields.length===0?e.jsx("p",{className:"text-[13px] text-muted-foreground",children:"No validation failures in 30 days. The forms are sending what this platform asks for."}):e.jsx("ul",{className:"space-y-1",children:r.topFields.map(([o,a])=>e.jsxs("li",{className:"flex items-center gap-2 text-[13px]",children:[e.jsx("code",{className:"rounded bg-muted px-1.5 py-0.5 text-[11px]",children:o}),e.jsx("span",{className:"text-muted-foreground",children:"missing or invalid"}),e.jsx("span",{className:"ml-auto tabular-nums font-medium",children:a})]},o))}),e.jsx("p",{className:"text-[11px] text-muted-foreground pt-1",children:"Each one is an enquiry the client tried to send and lost. Fix the website field, not this platform."})]})})]})}function O({label:t,value:s,hint:r}){return e.jsxs("div",{className:"rounded-md border px-3 py-2",children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wider text-muted-foreground",children:t}),e.jsx("p",{className:"text-xl font-semibold tabular-nums",children:s}),e.jsx("p",{className:"text-[11px] text-muted-foreground",children:r})]})}function Dt(){const{role:t,isPlatformAdmin:s}=pe(),[r,o]=ge("inbox","/admin/web-forms");return t==="super_admin"||s?e.jsxs("div",{className:"space-y-6",children:[e.jsx(K,{title:"Web Forms",subtitle:"Every request that arrives from outside — the public websites and the client portal, the ones that became tasks and the ones that did not"}),e.jsx(at,{}),e.jsxs(xe,{value:r,onValueChange:o,children:[e.jsxs(fe,{children:[e.jsxs(k,{value:"inbox",className:"gap-1.5",children:[e.jsx(J,{className:"h-3.5 w-3.5"})," Inbox"]}),e.jsx(k,{value:"portal",children:"Portal"}),e.jsx(k,{value:"forms",children:"Forms"}),e.jsx(k,{value:"emails",children:"Emails"}),e.jsx(k,{value:"health",children:"Health"})]}),e.jsx(v,{value:"inbox",className:"mt-4",children:e.jsx(st,{})}),e.jsx(v,{value:"portal",className:"mt-4",children:e.jsx(Xe,{})}),e.jsx(v,{value:"forms",className:"mt-4",children:e.jsx(We,{})}),e.jsx(v,{value:"emails",className:"mt-4",children:e.jsx(Qe,{})}),e.jsx(v,{value:"health",className:"mt-4",children:e.jsx(rt,{})})]})]}):e.jsxs("div",{className:"space-y-6",children:[e.jsx(K,{title:"Web Forms",subtitle:"What arrives from the public websites"}),e.jsx(b,{children:e.jsx(y,{className:"p-6 text-sm text-muted-foreground",children:"This page shows raw submissions from the public sites, including ones that were turned away, so it is restricted to super admins."})})]})}export{et as OutcomeBadge,Dt 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.