1import{j as e}from"./ui-CMTTTAxa.js";import{a as d,L as X}from"./vendor-BDindXV3.js";import{u as Y,$ as ee,H as g,a3 as F,L as te,s as b,t as A}from"./index-Bqrg_C6x.js";import{f as L,p as se,i as ne,a as re}from"./auditView-CovMU-jm.js";import{R as K}from"./refresh-cw-BT4SDfzU.js";import{K as ae}from"./key-round-BGKg4QG6.js";import{R as oe}from"./rotate-ccw-B9sRx1dD.js";import{S as de}from"./shield-alert-H84ek3l-.js";import{C as ie}from"./chevron-left-BuLYUPp0.js";import{C as le}from"./chevron-right-CzU0GhkP.js";import"./supabase-Czj9ajwo.js";const x=25,T=[{key:"all",label:"All",actions:["ssn.reveal","document.reveal","document.delete","pin.failed_attempt","pin.reset"],hint:"Shows every kind of sensitive access."},{key:"ssn",label:"SSN reveals",actions:["ssn.reveal"],hint:"Shows only SSN reveals, from the CRM and the trustee portal."},{key:"docview",label:"Protected document views",actions:["document.reveal"],hint:"Shows only openings of PIN-protected documents (IDs, tax returns)."},{key:"docdelete",label:"Document deletes",actions:["document.delete"],hint:"Shows only deleted client documents."},{key:"pin",label:"Failed PINs & lockouts",actions:["pin.failed_attempt"],hint:"Shows only wrong PIN entries; the fifth in a row locks the PIN."},{key:"reset",label:"PIN resets",actions:["pin.reset"],hint:"Shows only PINs reset from this page."}],ce="needs the database update 20260917101600_settings_security_pin_status.sql, which has not been applied yet.";function ue(s){return(s==null?void 0:s.code)==="PGRST202"||/could not find the function/i.test((s==null?void 0:s.message)||"")}function ke(){return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-xl font-bold text-foreground",children:"Security"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-0.5",children:"Security PINs and a log of every sensitive access: SSN reveals, protected documents, deletes and PIN failures."})]}),e.jsx(me,{}),e.jsx(xe,{})]})}function me(){const{user:s}=Y(),i=ee.includes(s==null?void 0:s.role),[a,o]=d.useState([]),[N,S]=d.useState(!0),[c,y]=d.useState(!1),[p,j]=d.useState(null),[u,f]=d.useState(null);async function h(){y(!0);const{data:t,error:l}=await b.rpc("admin_security_pin_status");l?(console.error("[security] pin status failed:",l),j(ue(l)?`PIN status ${ce}`:`Could not load PIN status: ${l.message}`)):(j(null),o(t||[])),S(!1),y(!1)}d.useEffect(()=>{h()},[]);async function I(t){const l=t.full_name||t.email;if(!window.confirm(`Reset ${l}'s security PIN? Their PIN is deleted and any lockout cleared; they'll set a new PIN the next time one is asked for.`))return;f(t.profile_id);const{error:v}=await b.rpc("reset_security_pin",{p_target:t.profile_id});if(f(null),v){A.error(`Could not reset the PIN: ${v.message}`);return}A.success(`${l}'s PIN was reset`),h()}const k=Date.now(),_={set:a.filter(t=>t.has_pin).length,notSet:a.filter(t=>!t.has_pin).length,locked:a.filter(t=>t.locked_until&&new Date(t.locked_until).getTime()>k).length};return e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"text-base font-semibold text-foreground",children:"Security PINs"}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-0.5",children:[N||p?"The PIN guards SSN reveals, protected documents and document deletes.":`${_.set} set · ${_.notSet} not set · ${_.locked} locked. Five wrong PINs in a row lock it for 15 minutes.`,!i&&" Only the Owner or a Master Admin can reset a PIN."]})]}),e.jsx(g,{text:"Reloads everyone's PIN status.",children:e.jsx("button",{onClick:h,disabled:c,className:"p-2 rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-secondary transition-colors",children:e.jsx(K,{className:`w-4 h-4 ${c?"animate-spin":""}`})})})]}),p&&e.jsx("div",{className:"p-3 rounded-lg bg-amber-500/10 border border-amber-500/20 text-sm text-amber-600 dark:text-amber-400",children:p}),e.jsx("div",{className:"bg-card border border-border/50 rounded-xl overflow-hidden",children:N?e.jsx("div",{className:"flex items-center justify-center py-14",children:e.jsx("div",{className:"w-7 h-7 border-4 border-muted border-t-[#5BC8E8] rounded-full animate-spin"})}):a.length===0?e.jsxs("div",{className:"text-center py-12",children:[e.jsx(ae,{className:"w-9 h-9 text-muted-foreground mx-auto mb-2"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:p?"PIN status unavailable":"No team members found"})]}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full min-w-[680px]",children:[e.jsx("thead",{className:"bg-background/50 border-b border-border/50",children:e.jsx("tr",{children:["Member","Role","PIN","Failed attempts","PIN changed","Last failed attempt",""].map(t=>e.jsx("th",{className:"px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wide",children:t},t))})}),e.jsx("tbody",{className:"divide-y divide-border/30",children:a.map(t=>{const l=t.locked_until&&new Date(t.locked_until).getTime()>k;return e.jsxs("tr",{className:"hover:bg-secondary/30 transition-colors",children:[e.jsxs("td",{className:"px-4 py-3",children:[e.jsxs("p",{className:"text-sm text-foreground",children:[t.full_name||t.email||"â",t.is_active===!1&&e.jsx("span",{className:"ml-2 text-[10px] px-1.5 py-0.5 rounded bg-secondary text-muted-foreground",children:"Deactivated"})]}),t.full_name&&e.jsx("p",{className:"text-[11px] text-muted-foreground",children:t.email})]}),e.jsx("td",{className:"px-4 py-3 text-sm text-muted-foreground whitespace-nowrap",children:F(t.role).label}),e.jsx("td",{className:"px-4 py-3 whitespace-nowrap",children:t.has_pin?l?e.jsxs("span",{className:"text-xs px-2 py-0.5 rounded-full font-semibold bg-red-500/10 text-red-500",children:["Locked until ",new Date(t.locked_until).toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})]}):e.jsx("span",{className:"text-xs px-2 py-0.5 rounded-full font-semibold bg-[#d1fae5] text-[#059669]",children:"Set"}):e.jsx("span",{className:"text-xs px-2 py-0.5 rounded-full font-semibold bg-secondary text-muted-foreground",children:"Not set"})}),e.jsx("td",{className:`px-4 py-3 text-sm ${t.failed_attempts>0?"text-amber-500 font-medium":"text-muted-foreground"}`,children:t.has_pin?t.failed_attempts:"â"}),e.jsx("td",{className:"px-4 py-3 text-xs text-muted-foreground whitespace-nowrap",children:t.has_pin?L(t.pin_updated_at):"â"}),e.jsx("td",{className:"px-4 py-3 text-xs text-muted-foreground whitespace-nowrap",children:L(t.last_failed_at)}),e.jsx("td",{className:"px-4 py-3 text-right",children:t.has_pin&&e.jsx(g,{text:i?"Deletes their PIN and clears any lockout; they set a new PIN the next time one is asked for.":"Only the Owner or a Master Admin can reset a PIN.",side:"left",children:e.jsxs("button",{onClick:()=>I(t),disabled:!i||u!==null,className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-secondary transition-colors disabled:opacity-50",children:[u===t.profile_id?e.jsx(te,{className:"w-3.5 h-3.5 animate-spin"}):e.jsx(oe,{className:"w-3.5 h-3.5"}),"Reset PIN"]})})})]},t.profile_id)})})]})})})]})}
1function xe(){const[s,i]=d.useState("all"),[a,o]=d.useState(0),[N,S]=d.useState([]),[c,y]=d.useState(0),[p,j]=d.useState(!0),[u,f]=d.useState(!1),[h,I]=d.useState(null),[k,_]=d.useState(0),[t,l]=d.useState({}),[v,G]=d.useState({}),[U,q]=d.useState({}),P=d.useRef(0);d.useEffect(()=>{const n=++P.current;async function E(){f(!0);const w=T.find(r=>r.key===s).actions,{data:m,error:$,count:Z}=await b.from("audit_log").select("id, action_type, description, performed_by, performed_by_name, performed_by_role, entity_type, entity_id, old_value, created_at",{count:"exact"}).in("action_type",w).order("created_at",{ascending:!1}).order("id",{ascending:!1}).range(a*x,a*x+x-1);if(n!==P.current)return;if($){console.error("[security] access log failed:",$),I($.message),S([]),y(0),j(!1),f(!1);return}const R=m||[],M=[...new Set(R.flatMap(r=>[r.performed_by_name?null:r.performed_by,r.action_type.startsWith("pin.")?r.entity_id:null]).filter(Boolean))],B=[...new Set(R.filter(r=>r.action_type==="document.reveal"&&r.entity_id).map(r=>r.entity_id))],[z,J]=await Promise.all([M.length?b.from("profiles").select("id, full_name, email").in("id",M):Promise.resolve({data:[]}),B.length?b.from("documents").select("id, client_id, document_type, document_name").in("id",B):Promise.resolve({data:[]})]);if(n!==P.current)return;const D=Object.fromEntries((J.data||[]).map(r=>[r.id,r])),O=[...new Set(R.map(r=>W(r,D)).filter(Boolean))],Q=O.length?await b.from("clients").select("id, first_name, last_name, client_number").in("id",O):{data:[]};n===P.current&&(I(null),S(R),y(Z||0),l(Object.fromEntries((z.data||[]).map(r=>[r.id,r.full_name||r.email]))),G(D),q(Object.fromEntries((Q.data||[]).map(r=>[r.id,r]))),j(!1),f(!1))}E()},[s,a,k]);const C=Math.max(0,Math.ceil(c/x)-1),H=c===0?0:a*x+1,V=Math.min(c,(a+1)*x);return e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h3",{className:"text-base font-semibold text-foreground",children:"Sensitive-access log"}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-0.5",children:[c.toLocaleString()," entries · newest first"]})]}),e.jsx(g,{text:"Reloads this page of the sensitive-access log.",children:e.jsx("button",{onClick:()=>_(n=>n+1),disabled:u,className:"p-2 rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-secondary transition-colors",children:e.jsx(K,{className:`w-4 h-4 ${u?"animate-spin":""}`})})})]}),e.jsx("div",{className:"flex gap-2 flex-wrap",children:T.map(n=>e.jsx(g,{text:n.hint,children:e.jsx("button",{onClick:()=>{i(n.key),o(0)},className:`px-3 py-1.5 rounded-lg text-xs font-medium border transition-all ${s===n.key?"bg-[#5BC8E8] text-black border-[#5BC8E8]":"border-border text-muted-foreground hover:text-foreground"}`,children:n.label})},n.key))}),h&&e.jsxs("div",{className:"p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-sm text-red-500",children:["Could not load the log: ",h]}),e.jsx("div",{className:"bg-card border border-border/50 rounded-xl overflow-hidden",children:p?e.jsx("div",{className:"flex items-center justify-center py-14",children:e.jsx("div",{className:"w-7 h-7 border-4 border-muted border-t-[#5BC8E8] rounded-full animate-spin"})}):N.length===0?e.jsxs("div",{className:"text-center py-12",children:[e.jsx(de,{className:"w-9 h-9 text-muted-foreground mx-auto mb-2"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Nothing logged here yet"})]}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full min-w-[720px]",children:[e.jsx("thead",{className:"bg-background/50 border-b border-border/50",children:e.jsx("tr",{children:["When","Who","What","Client","Details"].map(n=>e.jsx("th",{className:"px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wide",children:n},n))})}),e.jsx("tbody",{className:"divide-y divide-border/30",children:N.map(n=>{const E=n.performed_by_name||t[n.performed_by]||(n.performed_by?"Unknown user":"System"),w=W(n,v),m=w?U[w]:null;return e.jsxs("tr",{className:"hover:bg-secondary/30 transition-colors",children:[e.jsx("td",{className:"px-4 py-3 align-top whitespace-nowrap text-xs text-muted-foreground",children:L(n.created_at)}),e.jsxs("td",{className:"px-4 py-3 align-top",children:[e.jsx("p",{className:"text-sm text-foreground",children:E}),n.performed_by_role&&e.jsx("p",{className:"text-[11px] text-muted-foreground",children:F(n.performed_by_role).label})]}),e.jsx("td",{className:"px-4 py-3 align-top whitespace-nowrap",children:e.jsx(pe,{row:n})}),e.jsx("td",{className:"px-4 py-3 align-top text-sm",children:m?e.jsx(X,{to:`/client?clientId=${m.id}`,className:"text-[#5BC8E8] hover:underline",children:[m.first_name,m.last_name].filter(
1Boolean).join(" ")||m.client_number||"Client"}):w?e.jsx("span",{className:"text-xs text-muted-foreground",children:"Client no longer exists"}):n.action_type==="document.delete"?e.jsx("span",{className:"text-xs text-muted-foreground",title:"Deletes logged before 2026-09-17 did not record the client.",children:"Not recorded"}):e.jsx("span",{className:"text-muted-foreground",children:"â"})}),e.jsx("td",{className:"px-4 py-3 align-top text-sm text-foreground break-words",children:fe(n,t,v)})]},n.id)})})]})})}),c>x&&e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("p",{className:"text-xs text-muted-foreground",children:[H,"â",V," of ",c.toLocaleString()]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(g,{text:"Shows the previous, newer page of entries.",children:e.jsxs("button",{onClick:()=>o(n=>Math.max(0,n-1)),disabled:a===0||u,className:"flex items-center gap-1 px-3 py-1.5 rounded-lg border border-border text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-secondary transition-colors disabled:opacity-50",children:[e.jsx(ie,{className:"w-3.5 h-3.5"}),"Newer"]})}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:["Page ",a+1," of ",C+1]}),e.jsx(g,{text:"Shows the next, older page of entries.",children:e.jsxs("button",{onClick:()=>o(n=>Math.min(C,n+1)),disabled:a>=C||u,className:"flex items-center gap-1 px-3 py-1.5 rounded-lg border border-border text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-secondary transition-colors disabled:opacity-50",children:["Older",e.jsx(le,{className:"w-3.5 h-3.5"})]})})]})]})]})}function W(s,i){var a;if(s.action_type==="ssn.reveal")return s.entity_id||null;if(s.action_type==="document.reveal")return((a=i[s.entity_id])==null?void 0:a.client_id)||null;if(s.action_type==="document.delete"){const o=se(s.old_value);return o.kind==="object"&&o.value.client_id||null}return null}function pe({row:s}){const i="text-xs px-2 py-0.5 rounded-full font-semibold";switch(s.action_type){case"ssn.reveal":return e.jsx("span",{className:`${i} bg-[#5BC8E8]/15 text-[#5BC8E8]`,children:"SSN revealed"});case"document.reveal":return e.jsx("span",{className:`${i} bg-[#5BC8E8]/15 text-[#5BC8E8]`,children:"Protected document viewed"});case"document.delete":return e.jsx("span",{className:`${i} bg-red-500/10 text-red-500`,children:"Document deleted"});case"pin.reset":return e.jsx("span",{className:`${i} bg-secondary text-foreground`,children:"PIN reset"});case"pin.failed_attempt":return ne(s.description)?e.jsx("span",{className:`${i} bg-red-500/10 text-red-500`,children:"Locked out"}):e.jsx("span",{className:`${i} bg-amber-500/10 text-amber-500`,children:"Failed PIN"});default:return e.jsx("span",{className:`${i} bg-secondary text-muted-foreground`,children:s.action_type})}}function fe(s,i,a){switch(s.action_type){case"ssn.reveal":return s.performed_by_role==="TRUSTEE"?"Revealed in the trustee portal":"Revealed in the CRM";case"document.reveal":{const o=a[s.entity_id];return o?o.document_name||o.document_type:s.description||"â"}case"pin.failed_attempt":{const o=re(s.description);return o?o.n>=o.of?`Wrong PIN ${o.n} times in a row â locked for 15 minutes`:`Wrong PIN (attempt ${o.n} of ${o.of})`:s.description||"â"}case"pin.reset":return`Reset ${i[s.entity_id]||"a team member"}'s PIN`;default:return s.description||"â"}}export{ke 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.