PageSourceSearch

https://specora.ai/assets/ModerationPolicyPage-CSOqOPPU.js

js specora.ai collected 2026-10-05 14:44:24 UTC 12,122 bytes, 1 lines download raw bytes

1import{f as p,Q as $,U as L,r as c,j as e,V}from"./index-BRXndiYS.js";async function I(t,a){const{data:r}=await p.get(`/admin/orgs/${t}/moderation-policies`,{params:a});return r}async function O(t){const{data:a}=await p.get(`/admin/moderation-policies/${t}`);return a}async function E(t,a){const{data:r}=await p.post(`/admin/orgs/${t}/moderation-policies`,a);return r}async function T(t,a){const{data:r}=await p.patch(`/admin/moderation-policies/${t}`,a);return r}async function F(t){const{data:a}=await p.delete(`/admin/moderation-policies/${t}`);return a}async function q(t,a){const{data:r}=await p.post(`/admin/moderation-policies/${t}/rules`,a);return r}async function U(t,a){const{data:r}=await p.patch(`/admin/moderation-rules/${t}`,a);return r}async function B(t){const{data:a}=await p.delete(`/admin/moderation-rules/${t}`);return a}async function J(t,a){const{data:r}=await p.get(`/admin/moderation-policies/${t}/audits`,{params:a});return r}function Q(t){switch(t){case"platform_admin":return{canList:!0,canMutatePolicy:!0,canMutateRules:!0,canDeletePolicy:!0,canViewAudit:!0,canManagePlatformDefaults:!0};case"org_admin":return{canList:!0,canMutatePolicy:!0,canMutateRules:!0,canDeletePolicy:!0,canViewAudit:!0,canManagePlatformDefaults:!1};case"org_moderator":return{canList:!0,canMutatePolicy:!1,canMutateRules:!1,canDeletePolicy:!1,canViewAudit:!0,canManagePlatformDefaults:!1};case"auditor":return{canList:!0,canMutatePolicy:!1,canMutateRules:!1,canDeletePolicy:!1,canViewAudit:!0,canManagePlatformDefaults:!1};case"denied":return{canList:!1,canMutatePolicy:!1,canMutateRules:!1,canDeletePolicy:!1,canViewAudit:!1,canManagePlatformDefaults:!1}}}function Y(t){if(t.isPlatformAdmin)return"platform_admin";const a=t.orgRoles.map(r=>r.toLowerCase());return a.includes("owner")||a.includes("admin")?"org_admin":a.includes("moderator")?"org_moderator":a.includes("auditor")?"auditor":"denied"}function z(){if(typeof window>"u")return[];const t=window.localStorage.getItem("prspec_access_token");if(!t)return[];try{const a=t.split(".")[1];if(!a)return[];const r=JSON.parse(atob(a)),m=r==null?void 0:r.roles;return Array.isArray(m)?m.map(x=>String(x)):[]}catch{return[]}}function G(){if(typeof window>"u")return null;const t=window.localStorage.getItem("prspec_access_token");if(!t)return null;try{const a=t.split(".")[1];if(!a)return null;const r=JSON.parse(atob(a)),m=r==null?void 0:r.org_id;return typeof m=="string"?m:null}catch{return null}}function X(){const t=$(),a=L(),r=z(),m=G(),x=Y({isPlatformAdmin:t,orgRoles:r}),s=Q(x),[u,d]=c.useState(m),[h,o]=c.useState("policies"),[j,w]=c.useState([]),[n,P]=c.useState(!1),[v,f]=c.useState(null),[y,b]=c.useState(null),[M,R]=c.useState([]),g=c.useCallback(async()=>{if(u){P(!0),f(null);try{const i=await I(u);w(i.items)}catch(i){f(i.message??"Failed to load policies")}finally{P(!1)}}},[u]);c.useEffect(()=>{s.canList&&u&&g()},[s.canList,u,g]);const k=c.useCallback(async i=>{try{const l=await J(i);R(l.items)}catch(l){f(l.message??"Failed to load audit log")}},[]);if(x==="denied")return e.jsx(V,{title:"Moderation Policy Access Denied",message:"Only platform admins, org admins, org moderators, and auditors may view moderation policies.",currentRole:a,requiredRoles:["admin","owner","moderator","auditor"]});const N=async i=>{if(s.canMutatePolicy)try{await T(i.id,{is_active:!i.is_active}),await g()}catch(l){f(l.message??"Update failed")}},C=async i=>{if(!(!s.canDeletePolicy||!window.confirm(`Soft-delete policy "${i.name}"? This cannot be reversed from the UI.`)))try{await F(i.id),await g()}catch(_){f(_.message??"Delete failed")}},D=async i=>{if(s.canMutateRules)try{await U(i.id,{is_enabled:!i.is_enabled}),await g()}catch(l){f(l.message??"Rule update failed")}},S=async i=>{if(!(!s.canMutateRules||!window.confirm(`Delete rule "${i.rule_id}"?`)))try{await B(i.id),await g()}catch(_){f(_.message??"Rule delete failed")}};return e.jsxs("div",{className:"moderation-policy-page","data-testid":"moderation-policy-page",children:[e.jsxs("header",{className:"page-header",children:[e.jsx("h1",{children:"Moderation Policies"}),e.jsxs("p",{className:"persona-badge","data-testid":"persona-badge",children:["Persona: ",e.jsx("strong",{children:x.replace("_"," ")})]})]}),s.canManagePlatformDefaults&&e.jsx("aside",{className:"platform-default-banner","data-testid":"platform-default-banner",children:"Platform-default policies (org_id=NULL) are visible to every internal org as a fallback. Edit them from the platform-admin console."}),e.jsxs("nav",{className:"tabs",role:"tablist",children:[e.jsx("button",{role:"tab","aria-selected":h==="policies",onClick:()=>o("policies"),"data-testid":"tab-policies",children:"Policies"}),s.canViewAudit&&e.jsx("button",{role:"tab","aria-selected":h==="audit",onClick:()=>o("audit"),"data-testid":"tab-audit",children:"Audit Log"})]}),v&&e.jsx("div",{role:"alert",className:"error","data-testid":"error-banner",children:v}),h==="policies"&&e.jsxs("section",{"data-testid":"policies-section",children:[s.canMutatePolicy&&e.jsx("button",{className:"btn-primary","data-testid":"btn-new-policy",onClick:()=>{u&&b({id:"",org_id:u,agent_name:"",name:"",description:null,is_active:!0,on_parse_failure:"pending_review",on_llm_error:"pending_review",ver
1sion:0,created_by_user_id:null,created_at:"",updated_at:"",deleted_at:null,rules:[]})},children:"New Policy"}),n&&e.jsx("p",{children:"Loading..."}),e.jsxs("table",{className:"policy-table","data-testid":"policy-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Agent"}),e.jsx("th",{children:"Name"}),e.jsx("th",{children:"Rules"}),e.jsx("th",{children:"Version"}),e.jsx("th",{children:"Active"}),e.jsx("th",{children:"On parse fail"}),e.jsx("th",{children:"On LLM error"}),(s.canMutatePolicy||s.canDeletePolicy||s.canViewAudit)&&e.jsx("th",{children:"Actions"})]})}),e.jsxs("tbody",{children:[j.map(i=>e.jsxs("tr",{"data-testid":`policy-row-${i.id}`,children:[e.jsx("td",{children:i.agent_name}),e.jsx("td",{children:i.name}),e.jsxs("td",{children:[i.rules.filter(l=>l.is_enabled&&!l.deleted_at).length," / ",i.rules.length]}),e.jsx("td",{children:i.version}),e.jsx("td",{children:s.canMutatePolicy?e.jsx("button",{"data-testid":`toggle-active-${i.id}`,onClick:()=>N(i),children:i.is_active?"Active":"Inactive"}):e.jsx("span",{children:i.is_active?"Active":"Inactive"})}),e.jsx("td",{children:e.jsx(A,{behavior:i.on_parse_failure})}),e.jsx("td",{children:e.jsx(A,{behavior:i.on_llm_error})}),(s.canMutatePolicy||s.canDeletePolicy||s.canViewAudit)&&e.jsxs("td",{children:[s.canViewAudit&&e.jsx("button",{"data-testid":`view-audit-${i.id}`,onClick:()=>{o("audit"),k(i.id)},children:"Audit"}),s.canDeletePolicy&&e.jsx("button",{"data-testid":`delete-${i.id}`,onClick:()=>C(i),children:"Delete"})]})]},i.id)),j.length===0&&!n&&e.jsx("tr",{children:e.jsxs("td",{colSpan:8,className:"empty-state","data-testid":"empty-state",children:["No moderation policies yet.",s.canMutatePolicy&&' Click "New Policy" to create one.']})})]})]}),y&&e.jsx(H,{policy:y,canMutateRules:s.canMutateRules,onClose:()=>b(null),onToggleRule:D,onDeleteRule:S,onAddRule:async i=>{if(y.id)try{await q(y.id,i);const l=await O(y.id);b(l),await g()}catch(l){f(l.message??"Add rule failed")}},onCreateNewPolicy:async i=>{if(u)try{const l=await E(u,i);b(l),await g()}catch(l){f(l.message??"Create policy failed")}}})]}),h==="audit"&&s.canViewAudit&&e.jsx(K,{audits:M})]})}function A({behavior:t}){const a=t==="approve"?"badge-danger":t==="reject"?"badge-warn":"badge-safe";return e.jsx("span",{className:a,"data-testid":`behavior-badge-${t}`,children:t.replace("_"," ")})}function H({policy:t,canMutateRules:a,onClose:r,onToggleRule:m,onDeleteRule:x,onAddRule:s,onCreateNewPolicy:u}){const[d,h]=c.useState({rule_id:"",description:"",pattern:"",severity:"block",locale:""}),[o,j]=c.useState({agent_name:t.agent_name,name:t.name}),w=t.id==="";return e.jsxs("div",{className:"rule-editor-panel","data-testid":"rule-editor-panel",role:"dialog","aria-label":"Policy rule editor",children:[e.jsxs("header",{children:[e.jsx("h2",{children:w?"Create Policy":`Edit Rules — ${t.name}`}),e.jsx("button",{onClick:r,"data-testid":"close-editor",children:"Close"})]}),w?e.jsxs("form",{"data-testid":"new-policy-form",onSubmit:async n=>{n.preventDefault(),!(!o.agent_name||!o.name)&&await u({agent_name:o.agent_name,name:o.name})},children:[e.jsxs("label",{children:["Agent name:",e.jsx("input",{"data-testid":"input-agent-name",value:o.agent_name,onChange:n=>j({...o,agent_name:n.target.value}),required:!0})]}),e.jsxs("label",{children:["Policy name:",e.jsx("input",{"data-testid":"input-policy-name",value:o.name,onChange:n=>j({...o,name:n.target.value}),required:!0})]}),e.jsx("button",{type:"submit","data-testid":"btn-create-policy",children:"Create Policy"})]}):e.jsxs(e.Fragment,{children:[e.jsxs("table",{className:"rule-table","data-testid":"rule-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Rule ID"}),e.jsx("th",{children:"Severity"}),e.jsx("th",{children:"Pattern"}),e.jsx("th",{children:"Locale"}),e.jsx("th",{children:"Enabled"}),a&&e.jsx("th",{children:"Actions"})]})}),e.jsx("tbody",{children:t.rules.filter(n=>!n.deleted_at).map(n=>e.jsxs("tr",{"data-testid":`rule-row-${n.id}`,children:[e.jsxs("td",{children:[e.jsx("strong",{children:n.rule_id}),e.jsx("br",{}),e.jsx("span",{className:"muted",children:n.description})]}),e.jsx("td",{children:n.severity}),e.jsx("td",{children:e.jsx("code",{children:n.pattern})}),e.jsx("td",{children:n.locale??"—"}),e.jsx("td",{children:n.is_enabled?"Yes":"No"}),a&&e.jsxs("td",{children:[e.jsx("button",{"data-testid":`toggle-rule-${n.id}`,onClick:()=>m(n),children:n.is_enabled?"Disable":"Enable"}),e.jsx("button",{"data-testid":`delete-rule-${n.id}`,onClick:()=>x(n),children:"Delete"})]})]},n.id))})]}),a&&e.jsxs("form",{className:"new-rule-form","data-testid":"new-rule-form",onSubmit:async n=>{n.preventDefault(),!(!d.rule_id||!d.pattern)&&(await s({rule_id:d.rule_id,description:d.description,pattern:d.pattern,severity:d.severity,locale:d.locale||null}),h({rule_id:"",description:"",pattern:"",severity:"block",locale:""}))},children:[e.jsx("input",{placeholder:"rule_id",value:d.rule_id,onChange:n=>h({...d,rule_id:n.target.value}),"data-testid":"input-rule-id",required:!0}),e.jsx("input",{placeholder:"description",value:d.description,onChange:n=>h({...d,description:n.target.value}),"data-testid":"input-rule-description"}),e.jsx("input",{placeholder:"regex pattern",value:d.pattern,onChange:n=>h({...d,pattern:n.target.value}),"data-testid":"input-rule-pattern",required:!0}),e.jsxs("select",{value:d.severity,onChange:n=>h({...d,severity:n.target.value}),"data-testid":"select-rule-severity",children:[e.jsx("option",{value:"block",children:"block"}),e.jsx("option",{value:"review",children:"review"}),e.jsx("option",{value:"warn",children:"warn"})]}),e.jsx("input",{placeholder:"locale (optional)",value:d.locale,onChange:n=>h({...d,locale:n.target.value}),"data-testid":"input-rule-locale"}),e.jsx("button",{type:"submit","data-testid":"btn-add-rule",children:"Add Rule"})]})]})]})}function K({audits:t}){return e.jsxs("section",{"data-testid":"audit-section",children:[e.jsx("h2",{children:"Audit Log"}),t.length===0?e.jsx("p",{"data-testid":"audit-empty",children:"No audit entries to display."}):e.jsxs("table",{className:"audit-table","data-testid":"audit-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Time"}),e.jsx("th",{children:"Actor role"}),e.jsx("th",{children:"Action"}),e.jsx("th",{children:"Before → After"})]})}),e.jsx("tbody",{children:t.map(a=>e.jsxs("tr",{"data-testid":`audit-row-${a.id}`,children:[e.jsx("td",{children:new Date(a.created_at).toLocaleStr
1ing()}),e.jsx("td",{children:a.actor_role??"unknown"}),e.jsx("td",{children:a.action}),e.jsx("td",{children:e.jsxs("details",{children:[e.jsx("summary",{children:"diff"}),e.jsx("pre",{children:JSON.stringify({before:a.before_snapshot,after:a.after_snapshot},null,2)})]})})]},a.id))})]})]})}export{X as default,Q as personaCapabilities,Y as resolveModerationPersona};

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.