1import{f as Ve,r as l,j as e,u as Ze,L as _e,k as Qe}from"./react-vendor-xjz46aeH.js";import{V as U,z as O,J as Ae,bU as Ye,v as X,k as Ne,B as A,C as D,A as H,D as ae,F as I,N as L,aW as J,S as Y,E as oe,j as ne,aX as Se,$ as Xe,P as re,cx as et,cy as tt}from"./index-DB-GErVi.js";import{T as st}from"./Toggle-DarifWY4.js";import{C as ie}from"./ConfirmDialog-DH3ybaP-.js";import{K as Fe}from"./KebabMenu-DEUsrnY7.js";import{u as qe}from"./ReadOnlyContext-BF6yHbx8.js";import{i as nt}from"./formDirty-BcOfVH4k.js";import{c as ce,d as Ue,b as le}from"./tanstack-vendor-CK-H18W-.js";import{r as rt,D as Ie,w as Te}from"./disconnectNextStep-Diz36gck.js";import{A as pe,i as ot}from"./notConnected-DTiCVQG8.js";import{u as at,a as it,b as ct,S as Be,c as Re}from"./useServiceNowIntegration-BsrBoaZI.js";import{a as lt,f as dt,g as ut,h as mt,d as xt,i as pt,b as ht}from"./useJiraIntegration-CbZz77_N.js";import{j as ft,k as gt,u as bt,e as jt,b as vt,f as yt}from"./usePolicyData-DQ0YydBj.js";import{c as wt,d as Nt,b as Ge,e as Ct}from"./hooks-BUOm1otM.js";import{E as kt}from"./ErrorState-DlvwccLP.js";import{D as At}from"./DataTable-DHou1xit.js";import{T as St}from"./TableSkeleton-C9Pu8LvF.js";import{T as $e}from"./TierBadge-fBbGch68.js";import{a as It}from"./tierStyle-JMtr5g9q.js";import"./sentry-vendor-B7Zxq--p.js";import"./TypeToConfirmInput-PzKwsIrP.js";import"./useCopyToClipboard-D3JgiyXp.js";(function(){try{var t=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{};t.SENTRY_RELEASE={id:"53c0c37b9d0458a05694e8ed0430da9c4ec6eaca"};var s=new t.Error().stack;s&&(t._sentryDebugIds=t._sentryDebugIds||{},t._sentryDebugIds[s]="d140df88-7431-42d0-a829-14d19103f0b7",t._sentryDebugIdIdentifier="sentry-dbid-d140df88-7431-42d0-a829-14d19103f0b7")}catch{}})();function Tt(t,s){return s.length<2?"Disconnect":s.filter(a=>a.name===t.name).length>1?`Disconnect ${t.name} (${t.cloudAccountId})`:`Disconnect ${t.name}`}function Rt({canManage:t,canDisconnect:s=!1}){const[n,a]=Ve(),[d,u]=l.useState(!1),c=ce(),x=U(),r=l.useRef(!1);l.useEffect(()=>{const v=n.get("state"),y=n.get("admin_consent"),S=n.get("code");if(!v||r.current||!y&&!S&&!n.get("error"))return;r.current=!0,(async()=>{try{if(!y){const{url:E}=await O.post("/connect/microsoft365/signin",{code:S??"",state:v,error:n.get("error")??"",errorDescription:n.get("error_description")??""});window.location.href=E;return}await O.post("/connect/microsoft365/consent",{tenant:n.get("tenant")??"",adminConsent:y,state:v,error:n.get("error")??"",errorDescription:n.get("error_description")??""}),D.success("Microsoft 365 connected. Seat data appears after the next sync."),c.invalidateQueries({queryKey:H.cloudAccounts.all(x)})}catch(E){E?.status===409?D.error("This Microsoft 365 tenant is already connected to a different ZopNight organisation, so it was not connected here. Contact support to move it."):D.error(`Could not finish connecting: ${E?.message||"Unknown error"}`)}finally{for(const E of["tenant","admin_consent","code","state","session_state","error","error_description"])n.delete(E);a(n,{replace:!0})}})()},[]);const[o,h]=l.useState("asking");l.useEffect(()=>{if(!t)return;let v=!1;return O.get("/connect/microsoft365/consent-url").then(y=>!v&&h(y?.url?"ready":"unconfigured")).catch(y=>{v||h(y?.status===400?"unconfigured":"unknown")}),()=>{v=!0}},[t]);const g=async()=>{u(!0);try{const v=await O.get("/connect/microsoft365/consent-url");if(!v?.url){D.error("Microsoft 365 connect is not configured on this environment."),u(!1);return}window.location.href=v.url}catch(v){D.error(`Could not start Microsoft connect: ${v?.message||"Unknown error"}`),u(!1)}},{data:f}=Ae(),p=(f??[]).filter(v=>v.provider==="microsoft365"),m=p.length>0,w=t&&o==="unconfigured",[b,k]=l.useState(null),i=Ye(),[C,T]=l.useState(rt),P=()=>{if(!b)return;const v=b.disconnectNextStep;i.mutate(b.id,{onSuccess:()=>{k(null),v&&(Te(v),T(v))}})};return e.jsxs("section",{id:"ai-seat-licences",className:"mt-8 scroll-mt-24",children:[e.jsx(Ie,{step:C,onDismiss:()=>{Te(null),T(null)}}),e.jsx("h2",{className:"text-base font-semibold text-text-primary",children:"AI Seat Licences"}),e.jsx("p",{className:"mt-1 text-sm text-text-secondary",children:"Products billed per person rather than per request. We read the vendor's own usage report to show which seats are earning their keep."}),e.jsx("div",{className:"mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:e.jsxs("div",{className:"border rounded-xl bg-surface shadow-card border-border-strong p-4 flex flex-col",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(pe,{provider:"microsoft365",label:"Microsoft 365"}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h3",{className:"font-semibold text-text-primary truncate",children:"Microsoft 365 Copilot"}),e.jsx("p",{className:"text-xs text-text-muted truncate",children:"Seat licences · read-only"})]}),m&&e.jsx(Fe,{ariaLabel:"Microsoft 365 actions",items:p.map(v=>({label:Tt(v,p),danger:!0,onClick:()=>k(v),disabled:!s,disabledTitle:"You do not have permission to remove a cloud account. Contact your admin."}))})]}),e.jsx("div",{className:"mt-3 flex items-center gap-2",children:e.jsx(X,{variant:m?"success":"neutral",children:m?"Connected":w?"Not configured":"Not connected"})}),w&&e.jsx("p",{className:"mt-2 text-xs text-text-muted",children:"Microsoft 365 connect is not configured on this environment."}),p.length>0&&e.jsx("ul",{className:"mt-3 space-y-1.5",children:p.map(v=>e.jsxs("li",{className:"min-w-0",children:[e.jsx("p",{className:"truncate text-xs font-medium text-text-secondary",children:v.name}),e.jsx("p",{className:"truncate font-mono text-xs text-text-muted",children:v.cloudAccountId})]},v.id))}),(o==="ready"||o==="unknown"||!t)&&e.jsx("div",{className:"mt-4 flex items-center justify-end gap-2",children:e.jsx(Ne,{content:t?"":"You do not have permission to connect a cloud account. Contact your admin.",wrapDisabled:!0,placement:"top-end",children:e.jsx(A,{size:"sm",disabled:!t||d,onClick:g,children:d?"Opening Microsoftâ¦":m?"Connect another tenant":"Connect"})})})]})}),e.jsx(ie,{open:!!b,onClose:()=>k(null),onConfirm:P,title:"Disconnect Microsoft 365 tenant",message:`Disconnect "${b?.name||b?.cloudAccountId}"? Seat discovery and usage reporting stop for this directory, and the seats it contributed leave the AI estate.`,confirmLabel:"Disconnect",isLoading:i.isPending,children:e.jsx(Ie,{step:b?.disconnectNextStep})})]})}function Pt({className:t="h-5 w-5"}){return e.jsxs("svg",{className:t,viewBox:"0 0 24 24",fill:"none","aria-hidden":"true",children:[e.jsx("rect",{x:"2",y:"2",width:"20",height:"20",rx:"6",fill:"#62D84E"}),e.jsx("circle",{cx:"12",cy:"12",r:"5.2",fill:"none",stroke:"#0B3D2E",strokeWidth:"2.2"})]})}function je({label:t,value:s}){return e.jsxs("div",{className:"flex items-center justify-between gap-3 text-sm",children:[e.jsx("span",{className:"text-text-muted",children:t}),e.jsx("span",{className:"text-text-primary truncate max-w-[60%] text-right",children:s||"â"})]})}const ve={instanceUrl:"",authType:"basic",username:"",password:"",clientId:"",clientSecret:"",defaultTable:"incident",defaultAssignmentGroup:""};function Ke(t){return Be.find(s=>
1s.name===t)?.label??t}function Dt({open:t,mode:s,integration:n,onClose:a,onConnect:d,onDisconnect:u,isPending:c}){const x=s==="edit",[r,o]=l.useState(ve),[h,g]=l.useState(!1);l.useEffect(()=>{if(!t)return;const i=x&&n?.config?n.config:null;o(i?{...ve,instanceUrl:i.instanceUrl??"",authType:i.authType??"basic",defaultTable:i.defaultTable??"incident",defaultAssignmentGroup:i.defaultAssignmentGroup??""}:ve),g(!!i)},[t,x,n]);const f=i=>C=>o(T=>({...T,[i]:C.target.value})),p=!!r.instanceUrl&&(r.authType==="basic"?!!(r.username&&r.password):!!(r.clientId&&r.clientSecret)),m=()=>{o(i=>({...i,defaultAssignmentGroup:i.defaultAssignmentGroup||Re[0].name})),g(!0)},w=h&&!!(r.defaultTable&&r.defaultAssignmentGroup),b=i=>{i.preventDefault(),d({instanceUrl:r.instanceUrl.trim().replace(/\/+$/,""),authType:r.authType,defaultTable:r.defaultTable,defaultAssignmentGroup:r.defaultAssignmentGroup})},k=(i,C)=>e.jsx("button",{type:"button",onClick:()=>o(T=>({...T,authType:i})),className:`flex-1 px-3 py-1.5 text-sm rounded-md transition-colors duration-fast ${r.authType===i?"bg-surface text-text-primary shadow-sm border border-border-strong":"text-text-muted hover:text-text-primary"}`,children:C});return e.jsx(ae,{open:t,onClose:a,title:x?"Edit ServiceNow connection":"Connect ServiceNow",footer:e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[x?e.jsx(A,{type:"button",variant:"ghost",className:"text-danger-500",onClick:u,children:"Disconnect"}):e.jsx("span",{}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(A,{type:"button",variant:"secondary",onClick:a,children:"Cancel"}),e.jsx(A,{type:"submit",form:"servicenow-form",disabled:!w||c,className:"min-w-[132px]",children:c?"Savingâ¦":x?"Save changes":"Connect"})]})]}),children:e.jsxs("form",{id:"servicenow-form",onSubmit:b,className:"space-y-4",children:[e.jsx(I,{label:"Instance URL",required:!0,children:e.jsx(L,{placeholder:"https://acme.service-now.com",value:r.instanceUrl,onChange:f("instanceUrl")})}),e.jsx(I,{label:"Auth method",children:e.jsxs("div",{className:"flex gap-1 p-1 rounded-lg bg-surface-tertiary/50 border border-border",children:[k("basic","Basic"),k("oauth","OAuth")]})}),r.authType==="basic"?e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"Username",required:!0,children:e.jsx(L,{placeholder:"svc-zopnight",value:r.username,onChange:f("username")})}),e.jsx(I,{label:"Password",required:!0,children:e.jsx(L,{type:"password",placeholder:"Integration user password",value:r.password,onChange:f("password")})})]}):e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"Client ID",required:!0,children:e.jsx(L,{placeholder:"OAuth client ID",value:r.clientId,onChange:f("clientId")})}),e.jsx(I,{label:"Client secret",required:!0,children:e.jsx(L,{type:"password",placeholder:"OAuth client secret",value:r.clientSecret,onChange:f("clientSecret")})})]}),h?e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"Default table",required:!0,children:e.jsx(J,{value:r.defaultTable,onChange:f("defaultTable"),options:Be.map(i=>({value:i.name,label:i.label}))})}),e.jsx(I,{label:"Default assignment group",required:!0,children:e.jsx(J,{value:r.defaultAssignmentGroup,onChange:f("defaultAssignmentGroup"),options:Re.map(i=>({value:i.name,label:i.name}))})})]}):e.jsx(A,{type:"button",variant:"secondary",disabled:!p,onClick:m,children:"Verify & load"}),e.jsxs("p",{className:"text-xs text-text-muted",children:["Playground demo â ServiceNow tickets are created as ",Ke(r.defaultTable)," records (e.g. INC0010023) in the selected assignment group."]})]})})}function Et({canManage:t}){const{data:s}=at(),{mutate:n,isPending:a}=it(),{mutate:d}=ct(),u=!!s,c=s?.config??{},[x,r]=l.useState({open:!1,mode:"connect"}),[o,h]=l.useState(!1),[g,f]=l.useState(!1),p=l.useRef(null);return l.useEffect(()=>{if(!g)return;const m=w=>{p.current&&!p.current.contains(w.target)&&f(!1)};return document.addEventListener("mousedown",m),()=>document.removeEventListener("mousedown",m)},[g]),e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"border rounded-xl bg-surface shadow-card transition-all duration-normal hover:shadow-card-hover border-border-strong relative overflow-hidden flex flex-col",children:[e.jsxs("div",{className:"p-4 flex-1",children:[e.jsxs("div",{className:"flex justify-between items-start",children:[e.jsxs("div",{className:"min-w-0 flex-1 flex items-center gap-3",children:[e.jsx("span",{className:"flex items-center justify-center h-9 w-9 rounded-lg bg-surface-tertiary border border-border shrink-0",children:e.jsx(Pt,{className:"h-5 w-5"})}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("h3",{className:"font-semibold text-text-primary truncate",children:"ServiceNow"}),e.jsx("p",{className:"text-xs text-text-muted truncate",children:"ServiceNow ITSM"})]})]}),u&&e.jsxs("div",{ref:p,className:"relative",children:[e.jsx("button",{onClick:()=>f(m=>!m),className:"p-1 rounded-md hover:bg-surface-elevated transition-colors duration-fast text-text-muted hover:text-text-primary","aria-label":"Integration actions",children:e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"w-5 h-5",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"})})}),g&&e.jsxs("div",{className:"absolute right-0 top-full mt-1 w-32 bg-surface border border-border-strong rounded-lg shadow-lg z-10 py-1",children:[e.jsx("button",{disabled:!t,onClick:()=>{f(!1),r({open:!0,mode:"edit"})},className:`w-full text-left px-3 py-1.5 text-sm text-text-primary hover:bg-surface-elevated transition-colors duration-fast ${t?"":"op
1acity-50 cursor-not-allowed"}`,children:"Edit"}),e.jsx("button",{disabled:!t,onClick:()=>{f(!1),h(!0)},className:`w-full text-left px-3 py-1.5 text-sm text-danger-500 hover:bg-surface-elevated transition-colors duration-fast ${t?"":"opacity-50 cursor-not-allowed"}`,children:"Remove"})]})]})]}),e.jsx("div",{className:"mt-3",children:e.jsx(X,{variant:u?"success":"neutral",children:u?"Connected":"Not connected"})}),u?e.jsxs("div",{className:"mt-3 space-y-1.5",children:[e.jsx(je,{label:"Instance",value:c.instanceUrl}),e.jsx(je,{label:"Default table",value:Ke(c.defaultTable)}),e.jsx(je,{label:"Assignment group",value:c.defaultAssignmentGroup})]}):e.jsx("p",{className:"mt-3 text-sm text-text-secondary",children:"Turn cost and cleanup recommendations into ServiceNow incidents â routed to the right assignment group and tracked to resolved."})]}),!u&&e.jsx("div",{className:"px-4 py-3 border-t border-border flex items-center justify-end",children:t?e.jsx(A,{size:"sm",onClick:()=>r({open:!0,mode:"connect"}),children:"Connect"}):e.jsx("span",{className:"text-xs text-text-muted",children:"Ask an admin to connect"})})]}),e.jsx(Dt,{open:x.open,mode:x.mode,integration:s,isPending:a,onClose:()=>r(m=>({...m,open:!1})),onConnect:m=>n(m,{onSuccess:()=>r(w=>({...w,open:!1}))}),onDisconnect:()=>{r(m=>({...m,open:!1})),d()}}),e.jsx(ie,{open:o,onClose:()=>h(!1),onConfirm:()=>{d(),h(!1)},title:"Remove ServiceNow?",message:"Disconnect the ServiceNow integration for this playground session.",confirmLabel:"Remove"})]})}function Lt(){const t=U();return Ue({queryKey:H.integrations.list(t),queryFn:()=>O.get("/integrations"),select:s=>{const n=Array.isArray(s)?s:Array.isArray(s?.data)?s.data:s?.items??[],a=n.filter(d=>d.provider==="github_app");return n.find(d=>d.provider==="github")??a.find(d=>d.role==="write")??a[0]??null}})}function Mt(){const t=ce(),s=U();return le({mutationFn:({token:n,serverUrl:a})=>O.post("/integrations",{name:"GitHub",provider:"github",category:"scm",config:JSON.stringify({serverUrl:a||""}),credential:JSON.stringify({token:n})}),onSuccess:()=>{t.invalidateQueries({queryKey:H.integrations.all(s)}),D.success("GitHub connected")},onError:n=>D.error(n.message||"Failed to connect GitHub")})}function Ot(){const t=ce(),s=U();return le({mutationFn:n=>O.del(`/integrations/${n}`),onSuccess:()=>{t.invalidateQueries({queryKey:H.integrations.all(s)}),D.success("GitHub disconnected")},onError:n=>D.error(n.message||"Failed to disconnect GitHub")})}function _t(){return le({mutationFn:()=>O.get("/integrations/github/install",{role:"write"}),onSuccess:t=>{const s=t?.url;if(s){try{sessionStorage.setItem("ghAppReturnTo",window.location.pathname)}catch{}window.location.assign(s)}else D.error("GitHub App is not configured on this environment")},onError:t=>D.error(t.message||"GitHub App unavailable â not configured on this environment")})}function Ft({className:t="h-5 w-5"}){return e.jsx("svg",{className:t,viewBox:"0 0 16 16",fill:"currentColor","aria-hidden":"true",children:e.jsx("path",{d:"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"})})}function Pe(){return e.jsx("span",{className:"inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent align-[-2px]"})}function qt({canManage:t}){const{data:s}=Lt(),n=Mt(),a=Ot(),d=_t(),[u,c]=l.useState(!1),[x,r]=l.useState(!1),[o,h]=l.useState(!1),g=l.useRef(null),[f,p]=l.useState(""),[m,w]=l.useState(""),b=!!s;l.useEffect(()=>{if(!o)return;const i=C=>{g.current&&!g.current.contains(C.target)&&h(!1)};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)}
1,[o]);const k=i=>{i.preventDefault(),f.trim()&&n.mutate({token:f.trim(),serverUrl:m.trim()},{onSuccess:()=>{c(!1),p(""),w("")}})};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"border rounded-xl bg-surface shadow-card transition-all duration-normal hover:shadow-card-hover border-border-strong relative overflow-hidden flex flex-col",children:[e.jsxs("div",{className:"p-4 flex-1",children:[e.jsxs("div",{className:"flex justify-between items-start",children:[e.jsxs("div",{className:"min-w-0 flex-1 flex items-center gap-3",children:[e.jsx("span",{className:"flex items-center justify-center h-9 w-9 rounded-lg bg-surface-tertiary border border-border shrink-0 text-text-primary",children:e.jsx(Ft,{className:"h-5 w-5"})}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("h3",{className:"font-semibold text-text-primary truncate",children:"GitHub"}),e.jsx("p",{className:"text-xs text-text-muted truncate",children:"Repositories & IaC governance"})]})]}),b&&e.jsxs("div",{ref:g,className:"relative",children:[e.jsx("button",{onClick:i=>{i.stopPropagation(),h(C=>!C)},className:"p-1 rounded-md hover:bg-surface-elevated transition-colors duration-fast text-text-muted hover:text-text-primary","aria-label":"Integration actions",children:e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"w-5 h-5",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"})})}),o&&e.jsxs("div",{className:"absolute right-0 top-full mt-1 w-36 bg-surface border border-border-strong rounded-lg shadow-lg z-10 py-1",children:[e.jsx("button",{disabled:!t,onClick:i=>{i.stopPropagation(),h(!1),c(!0)},className:`w-full text-left px-3 py-1.5 text-sm text-text-primary hover:bg-surface-elevated transition-colors duration-fast ${t?"":"opacity-50 cursor-not-allowed"}`,title:t?void 0:"You don't have permission. Contact your admin.",children:"Reconnect"}),e.jsx("button",{disabled:!t,onClick:i=>{i.stopPropagation(),h(!1),r(!0)},className:`w-full text-left px-3 py-1.5 text-sm text-danger-500 hover:bg-surface-elevated transition-colors duration-fast ${t?"":"opacity-50 cursor-not-allowed"}`,title:t?void 0:"You don't have permission. Contact your admin.",children:"Disconnect"})]})]})]}),e.jsx("div",{className:"mt-3",children:e.jsx(X,{variant:b?"success":"neutral",children:b?"Connected":"Not connected"})}),b?e.jsx("p",{className:"mt-3 text-sm text-text-secondary",children:"ZopNight reads your Terraform/OpenTofu repos and gates pull requests against your IaC policies. Manage the token from the ⯠menu."}):e.jsxs("p",{className:"mt-3 text-sm text-text-secondary",children:["Connect a Personal Access Token (",e.jsx("code",{className:"text-xs",children:"repo"})," scope) so ZopNight can read your Terraform/OpenTofu repos and gate pull requests against your IaC policies."]})]}),!b&&e.jsx("div",{className:"px-4 py-3 border-t border-border flex items-center justify-end",children:t?e.jsx(A,{size:"sm","aria-label":"Connect GitHub",onClick:()=>c(!0),children:"Connect"}):e.jsx("span",{className:"text-xs text-text-muted",children:"Ask an admin to connect"})})]}),e.jsx(ae,{open:u,onClose:()=>c(!1),title:b?"Reconnect GitHub":"Connect GitHub",subtitle:"Install the GitHub App, or paste a token",footer:e.jsxs("div",{className:"flex items-center gap-3 w-full",children:[e.jsx("div",{className:"flex-1"}),e.jsx(A,{variant:"secondary",onClick:()=>c(!1),disabled:n.isPending,children:"Cancel"}),e.jsx(A,{type:"submit",form:"github-form",className:"min-w-[140px]",disabled:n.isPending||!f.trim(),children:n.isPending?e.jsxs(e.Fragment,{children:[e.jsx(Pe,{})," Connectingâ¦"]}):"Connect with token"})]}),children:e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{className:"rounded-lg border border-border-strong bg-surface-tertiary/40 p-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h4",{className:"font-semibold text-text-primary",children:"GitHub App"}),e.jsx(X,{variant:"success",children:"Recommended"})]}),e.jsx("p",{className:"mt-1 text-xs text-text-muted",children:"Install ZopNight on the orgs/repos y
1ou choose. Repositories list automatically and installation tokens rotate on their own â no PAT to create or renew."}),e.jsx("div",{className:"mt-3 flex justify-end",children:e.jsx(A,{size:"sm",onClick:()=>d.mutate(),disabled:d.isPending,children:d.isPending?e.jsxs(e.Fragment,{children:[e.jsx(Pe,{})," Redirectingâ¦"]}):"Install GitHub App â"})})]}),e.jsxs("div",{className:"flex items-center gap-3 text-xs text-text-muted",children:[e.jsx("span",{className:"h-px flex-1 bg-border"}),"or connect with a token",e.jsx("span",{className:"h-px flex-1 bg-border"})]}),e.jsxs("form",{id:"github-form",onSubmit:k,className:"space-y-4",children:[e.jsx(I,{label:"Personal access token",required:!0,children:e.jsx(L,{type:"password",placeholder:"ghp_⦠or github_pat_â¦",value:f,onChange:i=>p(i.target.value),required:!0,autoComplete:"off"})}),e.jsxs("p",{className:"text-xs text-text-muted -mt-2",children:["Create at github.com â Settings â Developer settings â Personal access tokens. Grant ",e.jsx("b",{children:"repo"})," scope (classic) or Contents + Pull requests (fine-grained). Stored encrypted; never shown again."]}),e.jsx(I,{label:"GitHub Enterprise URL (optional)",children:e.jsx(L,{type:"url",placeholder:"https://github.your-company.com",value:m,onChange:i=>w(i.target.value)})})]})]})}),e.jsx(ie,{open:x,onClose:()=>r(!1),onConfirm:()=>{r(!1),s&&a.mutate(s.id)},title:"Disconnect GitHub",message:"This removes the stored token. Repo reads and PR gating stop until you reconnect.",confirmLabel:"Disconnect",variant:"delete",isLoading:a.isPending})]})}function Je(){const t=U();return Ue({queryKey:H.integrations.list(t),queryFn:()=>O.get("/integrations",{category:"ai_gateway"}),select:s=>{let n=[];return Array.isArray(s)?n=s:Array.isArray(s?.data)?n=s.data:Array.isArray(s?.items)&&(n=s.items),n.filter(a=>Ut.includes(a.provider))}})}const Ut=["openai","anthropic","openrouter","aws_bedrock"],Bt={openai:"OpenAI",anthropic:"Anthropic",openrouter:"OpenRouter",aws_bedrock:"AWS Bedrock"};function Gt(){const t=ce(),s=U();return le({mutationFn:({provider:n,cloudAccountId:a,region:d})=>O.post(`/ai/providers/${n}/registrations`,a?{cloudAccountId:a,region:d}:{}),onSuccess:(n,{provider:a})=>{t.invalidateQueries({queryKey:H.integrations.all(s)}),t.invalidateQueries({queryKey:["ai-gateway"]}),t.invalidateQueries({queryKey:["budgetRows","ai"]});const d=Array.isArray(n?.registered)?n.registered.length:0,u=Bt[a]??a;D.success(`${u} connected â ${d} model${d===1?"":"s"} added`)},onError:()=>D.warning("Provider connected, but its models could not be added automatically.")})}function $t(){const t=ce(),s=U();return le({mutationFn:({name:n,provider:a,config:d,credential:u})=>O.post("/integrations",{name:n,provider:a,category:"ai_gateway",config:d?JSON.stringify(d):"",credential:u?JSON.stringify(u):""}),onSuccess:()=>{t.invalidateQueries({queryKey:H.integrations.all(s)})},onError:n=>D.error(n.message||"Failed to connect provider")})}function Kt(){const t=ce(),s=U();return le({mutationFn:n=>O.del(`/integrations/${n}`),onSuccess:()=>{t.invalidateQueries({queryKey:H.integrations.all(s)}),t.invalidateQueries({queryKey:["ai-gateway"]}),D.success("Provider disconnected")},onError:n=>D.error(n.message||"Failed to disconnect provider")})}function Jt({provider:t,providerLabel:s,models:n,recommended:a,isLoading:d,isError:u,onRetry:c,selectedId:x,onPick:r,query:o,onQuery:h,browseAll:g,onToggleBrowse:f,readOnly:p,onSeedRecommended:m,seeding:w}){const b=o.trim().toLowerCase(),k=l.useMemo(()=>b?(n??[]).filter(C=>(C.name||"").toLowerCase().includes(b)||(C.modelId||"").toLowerCase().includes(b)):null,[n,b]);if(d)return e.jsx("div",{className:"py-6",children:e.jsx(Y,{label:"Discovering models"})});if(u)return e.jsxs("div",{className:"rounded-lg border border-border bg-surface-tertiary/40 px-3 py-2.5 text-sm text-text-secondary",children:["Couldnât load the model list."," ",e.jsx("button",{type:"button",onClick:c,className:"font-medium text-primary-600 hover:text-primary-700",children:"Retry"})," ","or add a model by id below."]});if(!n?.length)return e.jsx("p",{className:"text-sm text-text-muted",children:"No models found â add one by id below."});const i=!p&&a.length?e.jsx(A,{variant:"secondary",disabled:w,onClick:m,children:w?"Addingâ¦":`Add recommended set (${a.length})`}):null;return e.jsxs("div",{className:"space-y-3",children:[e.jsx(L,{type:"search","aria-label":"Search models",value:o,onChange:C=>h(C.target.value),placeholder:"Search modelsâ¦"}),k?e.jsx(Ce,{provider:t,providerLabel:s,models:k,selectedId:x,onPick:r,readOnly:p,emptyMessage:`No models match â${o.trim()}â.`}):g?e.jsx(De,{provider:t,providerLabel:s,models:n,selectedId:x,onPick:r,readOnly:p,hideLegacy:!0}):a.length?e.jsx(De,{provider:t,providerLabel:s,models:a,selectedId:x,onPick:r,readOnly:p,header:i}):e.jsx(oe,{message:"No recommended models here â browse all or add a model by id below."}),k?null:e.jsx("button",{type:"button",onClick:f,className:"text-sm font-medium text-primary-600 hover:text-primary-700",children:g?"Show recommended only":"Browse all models"})]})}function De({provider:t,providerLabel:s,models:n,selectedId:a,onPick:d,readOnly:u,hideLegacy:c=!1,header:x=null}){const r=l.useMemo(()=>Wt(n),[n]);return e.jsxs("div",{className:"space-y-4",children:[x,r.map(([o,h])=>e.jsx(Ht,{provider:t,providerLabel:s,family:o,items:h,selectedId:a,onPick:d,readOnly:u,hideLegacy:c},o))]})}function Ht({provider:t,providerLabel:s,family:n,items:a,selectedId:d,onPick:u,readOnly:c,hideLegacy:x=!1}){const[r,o]=l.useState(!1),h=x?a.filter(f=>!f.legacy):a,g=x?a.filter(f=>f.legacy):[];return e.jsxs("div",{className:"space-y-2",children:[e.jsx("h4",{className:"text-xs font-semibold uppercase tracking-wide text-text-muted",children:n}),e.jsx(Ce,{provider:t,providerLabel:s,models:h,selectedId:d,onPick:u,readOnly:c}),g.length?r?e.jsx(Ce,{provider:t,providerLabel:s,models:g,selectedId:d,onPick:u,readOnly:c}):e.jsxs("button",{type:"button",onClick:()=>o(!0),className:"text-xs font-medium text-primary-600 hover:text-primary-700",children:["Show older versions (",g.length,")"]}):null]})}function Ce({provider:t,providerLabel:s,models:n,selectedId:a,onPick:d,readOnly:u,emptyMessage:c}){if(!n.length)return c?e.jsx(oe,{message:c}):null;const x="flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left",r=o=>e.jsxs(e.Fragment,{children:[e.jsx(pe,{provider:t,label:s,className:"h-7 w-7"}),e.jsxs("span",{className:"min-w-0 flex-1",children:[e.jsx("span",{className:"block truncate text-sm font-medium text-text-primary",children:o.name||o.modelId}),e.jsx("span",{className:"block truncate font-mono text-xs text-text-muted",children:o.modelId})]}),e.jsx($e,{tier:o.tier})]});return e.jsx("ul",{className:"space-y-2",role:"listbox","aria-label":"Models",children:n.map(o=>{const h=o.modelId===a;return e.jsx("li",{children:u?e.jsx("div",{className:`${x} border-border`,children:r(o)}):e.jsx("button",{type:"button",role:"option","aria-selected":h,onClick:()=>d(o),className:`${x} transition-colors duration-fast ${h?"border-primary-600/40 bg-primary-600/5":"border-border hover:border-border-strong hover:bg-surface-tertiary"}`,children:r(o)})},o.modelId)})})}function Wt(t){const s=new Map;for(const n of t??[]){const a=n.family||"Other";s.has(a)||s.set(a,[]),s.get(a).push(n)}return[...s.entries()].sort((n,a)=>n[0].localeCompare(a[0]))}const zt=["cheap","strong"].map(t=>({value:t,label:It(t)})),ye={aws_bedrock:{label:"AWS Bedrock",fields:["awsAccount","regi
1on"],idPlaceholder:"bedrock/us.amazon.nova-lite-v1:0",notListedHint:"Enable it in AWS Bedrock â Model access."},openrouter:{label:"OpenRouter",fields:[],idPlaceholder:"openrouter/anthropic/claude-3.5-sonnet",notListedHint:"Browse all at openrouter.ai/models."}},Ee=t=>(t||"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),He=t=>Ee(t.name)||Ee((t.modelId||"").split("/").pop()),Vt=(t,s,n)=>({logicalName:He(t),provider:s,providerModel:t.modelId,tier:t.tier||"cheap",...n}),Le={region:"us-east-1",accountId:"",tier:"cheap",logicalName:"",customId:""};function Zt({open:t,onClose:s,provider:n}){const a=qe(),[d,u]=l.useState(""),[c,x]=l.useState(Le),[r,o]=l.useState(""),[h,g]=l.useState(""),[f,p]=l.useState(!1),[m,w]=l.useState(!1),b=j=>x(_=>({..._,...j})),{data:k}=Ae(),i=l.useMemo(()=>(k??[]).filter(j=>j?.provider==="aws"&&j?.cloudAccountId),[k]),{data:C}=Je(),T=l.useMemo(()=>{const j=new Set((C??[]).map(_=>_.provider));return Object.keys(ye).filter(_=>_==="aws_bedrock"?i.length>0:j.has(_))},[C,i]),P=n||d||(T.length===1?T[0]:""),v=ye[P],y=v?.fields.includes("awsAccount")??!1,S=v?.fields.includes("region")??!1,R=y||S,E=c.region.trim(),B=R?{cloudAccountId:c.accountId,region:E}:{},{data:W,isLoading:G,isError:M,refetch:$}=wt(P,{cloudAccountId:c.accountId,region:E,needsContext:R}),z=Nt(),ee=l.useMemo(()=>(W??[]).filter(j=>j.recommended),[W]),ue=()=>{x(Le),o(""),g(""),p(!1)},te=()=>{u(""),ue()},V=()=>{te(),s()},me=j=>{u(j),ue()},he=j=>{o(j.modelId),b({customId:"",tier:j.tier||"cheap",logicalName:He(j)})},fe=j=>{o(""),b({customId:j})},ge=async()=>{if(!ee.length)return;w(!0);const j=await Promise.allSettled(ee.map(K=>z.mutateAsync({...Vt(K,P,B),silent:!0})));w(!1);const _=j.filter(K=>K.status==="fulfilled").length,de=ee.filter((K,be)=>j[be].status==="rejected").map(K=>K.name);if(_&&D.success(`Added ${_} recommended model${_===1?"":"s"}`),de.length){const K=de.slice(0,3).join(", "),be=de.length>3?` +${de.length-3} more`:"";D.error(`Could not add: ${K}${be}`)}_&&!de.length&&V()},N=(c.customId||r).trim(),F=R?!!(c.accountId&&E):!0,q=!!(P&&F&&N&&c.logicalName.trim()),se=()=>{z.mutate({logicalName:c.logicalName.trim(),provider:P,providerModel:N,tier:c.tier,...B},{onSuccess:V})},Z=[{value:"",label:"Select a providerâ¦"},...T.map(j=>({value:j,label:ye[j].label}))],Q=[{value:"",label:"Select an AWS accountâ¦"},...i.map(j=>({value:j.cloudAccountId,label:j.name?.trim()||j.cloudAccountId}))],We=!!P&&F,ze=z.isPending||m;return e.jsx(ae,{open:t,onClose:V,title:"Add a model",footer:e.jsxs(e.Fragment,{children:[a?null:e.jsx(A,{disabled:!q||ze,onClick:se,children:z.isPending?"Addingâ¦":"Add model"}),e.jsx(A,{variant:"secondary",onClick:V,children:a?"Close":"Cancel"})]}),children:e.jsxs("div",{className:"space-y-4",children:[!n&&e.jsx(I,{label:"Provider",required:!0,children:T.length?e.jsx(J,{"aria-label":"Provider",options:Z,value:P,onChange:j=>me(j.target.value)}):e.jsx("p",{className:"text-sm text-text-muted",children:"Connect a provider in Settings â Integrations to add models."})}),y?e.jsx(I,{label:"AWS account",required:!0,children:e.jsx(J,{"aria-label":"AWS account",options:Q,value:c.accountId,onChange:j=>b({accountId:j.target.value})})}):null,S?e.jsx(I,{label:"Region",required:!0,children:e.jsx(L,{value:c.region,onChange:j=>b({region:j.target.value}),placeholder:"us-east-1"})}):null,We?e.jsx(Jt,{provider:P,providerLabel:v.label,models:W,recommended:ee,isLoading:G,isError:M,onRetry:$,selectedId:r,onPick:he,query:h,onQuery:g,browseAll:f,onToggleBrowse:()=>p(j=>!j),readOnly:a,onSeedRecommended:ge,seeding:m}):null,!a&&P?e.jsxs(I,{label:"Or enter a model id",children:[e.jsx(L,{value:c.customId,onChange:j=>fe(j.target.value),placeholder:v.idPlaceholder}),e.jsxs("p",{className:"mt-1 text-xs text-text-muted",children:["Not listed? ",v.notListedHint]})]}):null,!a&&P?e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsx(I,{label:"Tier",children:e.jsx(J,{"aria-label":"Tier",options:zt,value:c.tier,onChange:j=>b({tier:j.target.value})})}),e.jsx(I,{label:"Name",required:!0,children:e.jsx(L,{value:c.logicalName,onChange:j=>b({logicalName:j.target.value}),placeholder:"nova-lite"})})]}):null]})})}const Qt=t=>t==="aws_bedrock"?"bedrock":t,Yt=t=>(t.providerModel??"").split("/").pop()||t.logicalName||"";function Xt({open:t,provider:s,providerLabel:n,onClose:a}){const[d,u]=l.useState(!1),[c,x]=l.useState(null),{data:r,isLoading:o,isError:h,error:g,refetch:f}=Ge(),p=Ct(),m=Qt(s),w=(r??[]).filter(i=>i.provider===m),b=[{id:"model",header:"Model",cell:({row:i})=>e.jsx("span",{className:"font-mono text-xs text-text-primary",children:Yt(i.original)})},{id:"tier",header:"Tier",cell:({row:i})=>i.original.tier?e.jsx($e,{tier:i.original.tier}):e.jsx("span",{className:"text-text-muted",children:"â"})},{id:"actions",header:"",cell:({row:i})=>e.jsx("div",{className:"flex justify-end",children:e.jsx(A,{variant:"delete",size:"xs",onClick:()=>x(i.original),children:"Remove"})})}],k=o?e.jsx(St,{rows:4,cols:3}):h?e.jsx(kt,{error:g,onRetry:f}):w.length?e.jsx(At,{columns:b,data:w}):e.jsx(oe,{title:"No models",description:`Add a model to route ${n??"this provider"} through the gateway.`});return e.jsxs(e.Fragment,{children:[e.jsx(ae,{open:t,onClose:a,title:`Manage ${n??""} models`.replace(/\s+/g," ").trim(),footer:e.jsx(A,{variant:"secondary",onClick:a,children:"Close"}),children:e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"flex justify-end",children:e.jsx(A,{onClick:()=>u(!0),children:"Add model"})}),k]})}),e.jsx(Zt,{open:d,provider:s,onClose:()=>u(!1)}),e.jsx(ie,{open:!!c,title:"Remove this model?",message:c?`${c.logicalName} will no longer be routable through the gateway.`:"",confirmLabel:"Remove",variant:"delete",onConfirm:()=>{p.mutate(c.id),x(null)},onClose:()=>x(null)})]})}const es="https://id.atlassian.com/manage-profile/security/api-tokens",Me={baseUrl:"",projectKey:"",issueType:"Bug",email:"",apiToken:""},ts={scope:{targetType:"organisation",targetId:""},match:{}};function ss({className:t="h-6 w-6"}){return e.jsxs("svg",{viewBox:"0 0 32 32",className:t,fill:"none",children:[e.jsx("path",{d:"M16.003 2C8.27 2 2 8.27 2 16.003 2 23.73 8.27 30 16.003 30 23.73 30 30 23.73 30 16.003 30 8.27 23.73 2 16.003 2z",fill:"#2684FF"}),e.jsx("path",{d:"M21.8 9.6h-5.9a2.7 2.7 0 0 0-2.7 2.7v.4h4.4a2.3 2.3 0 0 1 2.3 2.3v4.4h.4a2.7 2.7 0 0 0 2.7-2.7v-4.4a2.7 2.7 0 0 0-2.2-2.7z",fill:"#fff"}),e.jsx("path",{d:"M19.05 12.35h-5.9a2.7 2.7 0 0 0-2.7 2.7v.4h4.4a2.3 2.3 0 0 1 2.3 2.3v4.4h.4a2.7 2.7 0 0 0 2.7-2.7v-4.4a2.7 2.7 0 0 0-1.2-2.7z",fill:"#fff",opacity:".7"}),e.jsx("path",{d:"M16.3 15.1h-5.9a2.7 2.7 0 0 0-2.7 2.7v4.4a2.7 2.7 0 0 0 2.7 2.7h5.9a2.7 2.7 0 0 0 2.7-2.7v-4.4a2.7 2.7 0 0 0-2.7-2.7z",fill:"#fff",opacity:".5"})]})}function ke(t){try{const s=JSON.parse(t?.config??"{}");return{baseUrl:s.baseUrl??"",projectKey:s.defaultProjectKey??"",issueType:s.defaultIssueType??"",email:s.email??""}}catch{return{baseUrl:"",projectKey:"",issueType:"",email:""}}}function we({label:t,value:s}){return s?e.jsxs("div",{className:"flex items-baseline justify-between gap-3 text-sm",children:[e.jsx("span",{className:"text-text-muted shrink-0",children:t}),e.jsx("span",{className:"text-text-secondary font-medium truncate text-right",children:s})]}):null}function ns({integration:t,canEdit:s,canRemove:n,onConnect:a,onEdit:d,onRemove:u,ticketingAvailable:c,openTicketCount:x}){const[r,o]=l.useState(!1),h=l.useRef(null),g=!!t;l.useEffect(()=>{if(!r)return;const p=m=>{h.current&&!h.current.contains(m.target)&&o(!1)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[r]);const f=ke(t);return e.jsxs("div",{className:"border rounded-xl bg-surface shadow-card transition-all duration-normal hover:shadow-card-hover border-border-strong relative overflow-hidden flex flex-col",children:[e.jsxs("div",{className:"p-4 flex-1",children:[e.jsxs("div",{className:"flex justify-between items-start",children:[e.jsxs("div",{className:"min-w-0 flex-1 flex items-center gap-3",children:[e.jsx("span",{className:"flex items-center justify-center h-9 w-9 rounded-lg bg-surface-tertiary border border-border shrink-0",children:e.jsx(ss,{className:"h-5 w-5"})}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("h3",{className:"font-semibold text-text-primary truncate",children:"Jira"}),e.jsx("p",{className:"text-xs text-text-muted truncate",children:"Atlassian Jira"})]})]}),g&&e.jsxs("div",{ref:h,className:"relative",children:[e.jsx("button",{onClick:p=>{p.stopPropagation(),o(m=>!m)},className:"p-1 rounded-md hover:bg-surface-elevated transition-colors duration-fast text-text-muted hover:text-text-primary","aria-label":"Integration actions",children:e.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:"w-5 h-5",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"})})}),r&&e.jsxs("div",{className:"absolute right-0 top-full mt-1 w-32 bg-surface border border-border-strong rounded-lg shadow-lg z-10 py-1",children:[e.jsx("button",{disabled:!s,onClick:p=>{p.stopPropagation(),o(!1),d(t)},className:`w-full text-left px-3 py-1.5 text-sm text-text-primary hover:bg-surface-elevated transition-colors duration-fast ${s?"":"opacity-50 cursor-not-allowed"}`,title:s?void 0:"You don't have permission. Contact your admin.",children:"Edit"}),e.jsx("button",{disabled:!n,onClick:p=>{p.stopPropagation(),o(!1),u(t)},className:`w-full text-left px-3 py-1.5 text-sm text-danger-500 hover:bg-surface-elevated transition-colors duration-fast ${n?"":"opacity-50 cursor-not-allowed"}`,title:n?void 0:"You don't have permission. Contact your admin.",children:"Remove"})]})]})]}),e.jsx("div",{className:"mt-3",children:e.jsx(X,{variant:g?"success":"neutral",children:g?"Connected":"Not connected"})}),g?e.jsxs("div",{className:"mt-3 space-y-1.5",children:[e.jsx(we,{label:"Site",value:f.baseUrl}),e.jsx(we,{label:"Default project",value:f.projectKey}),e.jsx(we,{label:"Default issue type",value:f.issueType})]}):e.jsx("p",{className:"mt-3 text-sm text-text-secondary",children:"Turn cost and cleanup recommendations into Jira tickets â assigned to the right people, tracked to done, and kept in sync both ways."})]}),g&&c&&e.jsxs("div",{className:"px-4 py-2.5 border-t border-border flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs text-text-muted",children:"Automatic ticketing"}),e.jsx(_e,{to:"../../policies?domain=recommendation",className:"text-xs text-primary-600 hover:underline font-medium",children:x>0?`${x} ${x===1?"policy":"policies"} â`:"Set up â"})]}),!g&&e.jsx("div",{className:"px-4 py-3 border-t border-border flex items-center justify-end",children:s?e.jsx(A,{size:"sm",onClick:a,children:"Connect"}):e.jsx("span",{className:"text-xs text-text-muted",children:"Ask an admin to connect"})})]})}function rs({open:t,mode:s,integration:n,onClose:a,onConnect:d,onUp
1date:u,isPending:c,ticketingAvailable:x}){const r=s==="edit",[o,h]=l.useState(Me),[g,f]=l.useState({projects:[],issueTypes:[]}),[p,m]=l.useState(!1),[w,b]=l.useState(!1),[k,i]=l.useState(!1),C=pt(),T=ht(),P=N=>{const F=N??{},q=F.projects??[],se=F.issueTypes??[];f({projects:q,issueTypes:se}),h(Z=>({...Z,projectKey:q.some(Q=>Q.key===Z.projectKey)?Z.projectKey:q[0]?.key??"",issueType:se.some(Q=>Q.name===Z.issueType)?Z.issueType:se.find(Q=>Q.name==="Task")?.name??se[0]?.name??""})),m(!0)};l.useEffect(()=>{if(!t)return;const N=r&&n?ke(n):{baseUrl:"",projectKey:"",issueType:"",email:""};h({...Me,baseUrl:N.baseUrl,projectKey:N.projectKey,issueType:N.issueType,email:N.email}),f({projects:[],issueTypes:[]}),m(!1),b(!1),i(!1),C.reset(),T.reset(),r&&n&&T.mutate(n.id,{onSuccess:P})},[t,r,n]);const v=!!(o.baseUrl&&o.email&&o.apiToken),y=C.isPending||T.isPending,S=C.error?.message||T.error?.message,R=y||c,E=!r&&x,{data:B,isFetching:W}=jt(ts,{enabled:E&&p}),G=B?.total,M=W&&G===void 0?"Counting open recommendationsâ¦":G===void 0?"Couldn't estimate the number of recommendations right now.":`â ${G.toLocaleString()} open ${G===1?"recommendation":"recommendations"} will get a ticket.`,$=N=>F=>{h(q=>({...q,[N]:F.target.value})),m(!1)},z=N=>F=>h(q=>({...q,[N]:F})),ee=()=>{b(!0),h(N=>({...N,apiToken:""})),f({projects:[],issueTypes:[]}),m(!1)},ue=()=>{!v||R||C.mutate({baseUrl:o.baseUrl,email:o.email,apiToken:o.apiToken},{onSuccess:P})},te=p&&!!(o.projectKey&&o.issueType),V=N=>({baseUrl:N.baseUrl??"",projectKey:N.projectKey??"",issueType:N.issueType??"",email:N.email??""}),me=r&&n&&nt({...V(ke(n)),apiToken:""},{...V(o),apiToken:o.apiToken},{secretFields:["apiToken"]}),he=N=>{if(N.preventDefault(),!(!te||R)){if(me){a();return}r?u({id:n.id,...o}):d(o,k?"all":"later")}},fe=()=>{!te||R||d(o,"specific")},ge=e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex-1"}),e.jsx(A,{variant:"secondary",onClick:a,disabled:R,children:"Cancel"}),e.jsx(A,{type:"submit",form:"jira-form",className:"min-w-[150px] h-10 whitespace-nowrap",disabled:R||!te||me,children:c?e.jsxs(e.Fragment,{children:[e.jsx(Y,{className:"w-4 h-4"}),r?"Savingâ¦":"Connectingâ¦"]}):r?"Save changes":"Connect"})]});return e.jsx(ae,{open:t,onClose:a,title:r?"Edit Jira connection":"Connect Jira",footer:ge,children:e.jsxs("form",{id:"jira-form",onSubmit:he,className:"space-y-4",children:[e.jsx(I,{label:"Jira site URL",required:!0,children:e.jsx(L,{type:"url",placeholder:"https://your-org.atlassian.net",value:o.baseUrl,onChange:$("baseUrl"),required:!0,disabled:R})}),e.jsxs(I,{label:"Atlassian account email",required:!0,children:[e.jsx(L,{type:"email",placeholder:"[email protected]",value:o.email,onChange:$("email"),required:!0,disabled:R}),e.jsx("p",{className:"text-xs text-text-muted mt-1",children:"The Atlassian account that owns the API token â tickets are created as this user."})]}),r&&!w?e.jsxs(I,{label:"API token",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3 px-3 py-2 bg-surface-tertiary border border-border-strong rounded-lg",children:[e.jsx("span",{className:"text-sm text-text-muted tracking-[0.2em] font-mono select-none",children:"â¢â¢â¢â¢â¢â¢â¢â¢â¢â¢â¢â¢"}),e.jsx(A,{type:"button",variant:"secondary",size:"xs",onClick:ee,disabled:R,className:"shrink-0",children:"Replace"})]}),e.jsx("p",{className:"text-xs text-text-muted mt-1",children:"A token is already saved. Replace it only if it changed in Jira."})]}):e.jsxs(I,{label:"API token",required:!0,children:[e.jsx(L,{type:"password",autoComplete:"off",placeholder:"Paste your Jira API token",value:o.apiToken,onChange:$("apiToken"),required:!0,disabled:R,autoFocus:w}),e.jsxs("a",{href:es,target:"_blank",rel:"noreferrer",className:"mt-1.5 inline-flex items-center gap-1 text-sm font-medium text-primary-600 hover:text-primary-700 hover:underline",children:["Create an API token",e.jsx("svg",{className:"w-3.5 h-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"})})]})]}),(!r||w)&&e.jsx("div",{className:"min-h-[40px] flex items-center",children:p&&!S?e.jsxs("p",{className:"flex items-center gap-1.5 text-sm text-success-600",children:[e.jsx("svg",{className:"w-4 h-4 shrink-0",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2.5,children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m4.5 12.75 6 6 9-13.5"})}),"Connection verified â choose your defaults below."]}):e.jsx(A,{type:"button",variant:"secondary",onClick:ue,disabled:!v||R,className:"h-9 min-w-[150px] whitespace-nowrap",children:y?e.jsxs(e.Fragment,{children:[e.jsx(Y,{className:"w-4 h-4"}),"Verifyingâ¦"]}):"Verify connection"})}),S&&e.jsx("div",{className:"p-3 rounded-lg bg-danger-500/10 border border-danger-500/20",children:e.jsx("p",{className:"text-sm text-danger-500",children:S})}),r&&y&&!p&&e.jsxs("p",{className:"flex items-center gap-2 text-xs text-text-muted",children:[e.jsx(Y,{className:"w-3.5 h-3.5"})," Loading your Jira projectsâ¦"]}),p&&e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"Default project",required:!0,children:e.jsx(J,{value:o.projectKey,onChange:z("projectKey"),disabled:R,options:[{value:"",label:"Select a project"},...g.projects.map(N=>({value:N.key,label:`${N.name} (${N.key})`}))]})}),e.jsx(I,{label:"Default issue type",required:!0,children:e.jsx(J,{value:o.issueType,onCh
1ange:z("issueType"),disabled:R,options:[{value:"",label:"Select an issue type"},...g.issueTypes.map(N=>({value:N.name,label:N.name}))]})})]}),E&&p&&e.jsx(I,{label:"Automatic ticketing",children:e.jsxs("div",{className:"rounded-lg border border-border-strong p-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-text-primary",children:"Open tickets automatically"}),e.jsx("p",{className:"text-xs text-text-muted mt-0.5",children:"Open a Jira ticket for every open recommendation, kept in sync as its status changes."})]}),e.jsx(st,{checked:k,onChange:i,label:""})]}),k&&e.jsxs("div",{className:"mt-3 pt-3 border-t border-border-strong/60 flex items-center justify-between gap-3",children:[e.jsx("p",{className:"text-xs text-text-secondary",children:M}),e.jsx(A,{type:"button",variant:"secondary",size:"xs",onClick:fe,disabled:R||!te,className:"shrink-0",children:"Customise scope"})]})]})}),e.jsxs("div",{className:"text-xs text-text-muted leading-relaxed border-t border-border-strong/40 pt-3 space-y-2",children:[e.jsxs("p",{children:["ZopNight creates and updates Jira tickets for you. To sync changes made",e.jsx("span",{className:"text-text-secondary",children:" in Jira"})," back to ZopNight, a Jira admin approves the connection â you can connect now without it."]}),e.jsxs("details",{className:"group",children:[e.jsxs("summary",{className:"cursor-pointer inline-flex items-center gap-1 text-sm font-medium text-primary-600 hover:text-primary-700 list-none marker:content-none",children:[e.jsx("svg",{className:"w-3.5 h-3.5 transition-transform group-open:rotate-90",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m9 5 7 7-7 7"})}),"What access does this need?"]}),e.jsx("p",{className:"mt-1.5",children:"The account needs permission to browse projects and to create, transition, assign, and comment on issues. Two-way sync registers a Jira webhook on connect, which needs a Jira admin; a non-admin can still connect (tickets are created and updated from ZopNight), but changes made in Jira won't sync back until an admin registers the webhook."})]})]})]})})}const os=[{provider:"openai",label:"OpenAI",subtitle:"GPT models"},{provider:"anthropic",label:"Anthropic",subtitle:"Claude models"},{provider:"openrouter",label:"OpenRouter",subtitle:"Multi-model router"},{provider:"aws_bedrock",label:"AWS Bedrock",subtitle:"Claude on Bedrock",auth:"aws"}],xe="You need the Create AI model permission",Oe={apiKey:"",cloudAccountId:"",region:"us-east-1"},as=new Set(["openrouter","aws_bedrock"]);function is({meta:t,integration:s,canManage:n,modelCount:a,connectionMissing:d,onConnect:u,onDisconnect:c,onManageModels:x}){const r=!!s,o=r&&as.has(t.provider);return e.jsxs("div",{className:"border rounded-xl bg-surface shadow-card border-border-strong p-4 flex flex-col",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(pe,{provider:t.provider,label:t.label}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h3",{className:"font-semibold text-text-primary truncate",children:t.label}),e.jsx("p",{className:"text-xs text-text-muted truncate",children:t.subtitle})]}),o&&e.jsx(Fe,{ariaLabel:"Provider actions",items:[{label:"Manage models",onClick:()=>x(t),disabled:!n,disabledTitle:xe},{label:"Disconnect",danger:!0,onClick:()=>c(s),disabled:!n,disabledTitle:xe}]})]}),e.jsxs("div",{className:"mt-3 flex items-center gap-2",children:[e.jsx(X,{variant:r?d?"warning":"success":"neutral",children:r?"Connected":"Not connected"}),r&&d?e.jsx("span",{className:"text-xs text-warning-600",title:"The AI Gateway connection for this org isn't set up, so models can't be listed or used. Contact support.",children:"AI Gateway not set up"}):o&&e.jsxs("span",{className:"text-xs text-text-muted",children:[a," ",a===1?"model":"models"]})]}),!o&&e.jsx("div",{className:"mt-4 flex items-center justify-end gap-2",children:r?e.jsx(Ne,{content:n?"":xe,wrapDisabled:!0,placement:"top-end",children:e.jsx(A,{size:"sm",variant:"secondary",disabled:!n,onClick:()=>c(s),children:"Disconnect"})}):e.jsx(Ne,{content:n?"":xe,wrapDisabled:!0,placement:"top-end",children:e.jsx(A,{size:"sm",disabled:!n,onClick:()=>u(t),children:"Connect"})})})]})}function cs({open:t,meta:s,onClose:n,onSubmit:a,isPending:d}){const[u,c]=l.useState(Oe),x=m=>w=>c(b=>({...b,[m]:w.target.value})),r=s?.auth==="aws",{data:o}=Ae({enabled:t&&r}),h=l.useMemo(()=>(o??[]).filter(m=>m?.provider==="aws"&&m?.cloudAccountId),[o]);l.useEffect(()=>{t&&c(Oe)},[t]);
1const g=r?!!(u.cloudAccountId&&u.region.trim()):!!u.apiKey,f=m=>{m.preventDefault(),!(!g||d)&&a(r?{name:s.label,provider:s.provider,config:{cloudAccountId:u.cloudAccountId,region:u.region.trim()}}:{name:s.label,provider:s.provider,credential:{apiKey:u.apiKey.trim()}})},p=e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex-1"}),e.jsx(A,{variant:"secondary",onClick:n,disabled:d,className:"h-10",children:"Cancel"}),e.jsx(A,{type:"submit",form:"ai-provider-form",disabled:!g||d,className:"min-w-[150px] h-10 whitespace-nowrap",children:d?e.jsxs(e.Fragment,{children:[e.jsx(Y,{className:"w-4 h-4"}),"Connectingâ¦"]}):"Connect"})]});return e.jsx(ae,{open:t,onClose:n,title:s?`Connect ${s.label}`:"Connect",footer:p,children:e.jsx("form",{id:"ai-provider-form",onSubmit:f,className:"space-y-4",children:r?h.length===0?e.jsx("p",{className:"text-sm text-text-secondary",children:"Connect an AWS cloud account first (under Cloud Accounts), then enable Bedrock here â it routes through that account, so there's no API key to enter."}):e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"AWS account",required:!0,children:e.jsx(J,{value:u.cloudAccountId,onChange:x("cloudAccountId"),options:[{value:"",label:"Select an accountâ¦"},...h.map(m=>({value:m.cloudAccountId,label:m.name?.trim()||m.cloudAccountId}))]})}),e.jsx(I,{label:"Region",required:!0,children:e.jsx(L,{placeholder:"us-east-1",value:u.region,onChange:x("region"),required:!0})}),e.jsx("p",{className:"text-xs text-text-muted",children:"Bedrock routes through this account's credentials (minted on demand) â no API key needed."})]}):e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"API key",required:!0,children:e.jsx(L,{type:"password",autoComplete:"off",placeholder:"sk-â¦",value:u.apiKey,onChange:x("apiKey"),required:!0})}),e.jsx("p",{className:"text-xs text-text-muted",children:"Credentials are stored encrypted and used to route AI traffic. They are never shown again."})]})})})}const ls=[{provider:"langfuse",label:"Langfuse",kind:"Observability",enabled:et,description:"LLM tracing â cost per session, cache efficiency, and quality analytics.",to:"/reports?tab=estate",cta:"View observability"},{provider:"langdock",label:"Langdock",kind:"AI platform",enabled:tt,description:"Enterprise AI workspace spend by workspace, assistant, and user.",to:"/reports?tab=estate",cta:"View AI spend"}];function ds(){const t=ls.filter(s=>s.enabled());return t.length===0?null:e.jsxs("section",{className:"mt-8",children:[e.jsx("h2",{className:"text-base font-semibold text-text-primary",children:"AI Tools"}),e.jsx("p",{className:"mt-1 text-sm text-text-secondary",children:"Connected sources that feed your unified AI Estate â spend, governance, and savings across every tool, in Cost Reports â AI Spend."}),e.jsx("div",{className:"mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:t.map(s=>e.jsxs("div",{className:"rounded-lg border border-border bg-surface p-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(pe,{provider:s.provider,label:s.label}),e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-semibold text-text-primary",children:s.label}),e.jsx(X,{variant:"success",children:"Connected"})]}),e.jsx("span",{className:"text-xs text-text-muted",children:s.kind})]})]}),e.jsx("p",{className:"mt-3 text-sm text-text-secondary",children:s.description}),e.jsxs(_e,{to:s.to,className:"mt-3 inline-block text-sm font-medium text-primary-600 hover:text-primary-500",children:[s.cta," â"]})]},s.provider))})]})}function us({canManage:t}){const{data:s,isLoading:n,isError:a,refetch:d}=Je(),{mutate:u,isPending:c}=$t(),{mutate:x}=Kt(),{mutate:r}=Gt(),{data:o,isError:h,error:g}=Ge(),f=h&&ot(g),[p,m]=l.useState({open:!1,meta:null}),[w,b]=l.useState(null),[k,i]=l.useState({open:!1,meta:null}),C=l.useRef(null),{hash:T}=Qe();l.useEffect(()=>{T==="#ai-providers"&&C.current?.scrollIntoView?.({behavior:"smooth"})},[T]);const P=y=>(o??[]).filter(S=>S.provider===(y==="aws_bedrock"?"bedrock":y)).length,v=l.useMemo(()=>{const y={};for(const S of s??[])y[S.provider]=S;return y},[s]);return e.jsxs("section",{ref:C,id:"ai-providers",className:"mt-8 scroll-mt-24",children:[e.jsx("h2",{className:"text-base font-semibold text-text-primary",children:"AI Providers"}),e.jsx("p",{className:"mt-1 text-sm text-text-secondary",children:"Connect upstream LLM vendors so the AI gateway can route requests to them."}),e.jsx("div",{className:"mt-4",children:n?e.jsx("div",{className:"flex items-center justify-center py-10",children:e.jsx(Y,{className:"w-6 h-6"})}):a?e.jsx(oe,{title:"Failed to load AI providers",description:"Could not fetch provider status.",action:e.jsx(A,{size:"sm",variant:"secondary",onClick:()=>d(),children:"Retry"})}):e.jsx("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:os.map(y=>e.jsx(is,{meta:y,integration:v[y.provider],canManage:t,modelCount:P(y.provider),connectionMissing:f,onConnect:S=>m({open:!0,meta:S}),onDisconnect:S=>b(S),onManageModels:S=>i({open:!0,meta:S})},y.provider))})}),e.jsx(cs,{open:p.open,meta:p.meta,isPending:c,onClose:()=>m(y=>({...y,open:!1})),onSubmit:y=>u(y,{onSuccess:()=>{m(S=>({...S,open:!1})),r({provider:y.provider,cloudAccountId:y.config?.cloudAccountId,region:y.config?.region})}})}),e.jsx(ie,{open:!!w,onClose:()=>b(null),onConfirm:()=>{w&&x(w.id),b(null)},title:"Disconnect provider",message:"This removes the provider connection and its stored credentials. AI traffic can no longer be routed to it.",confirmLabel:"Disconnect",variant:"delete"}),e.jsx(Xt,{open:k.open,provider:k.meta?.provider,providerLabel:k.meta?.label,onClose:()=>i(y=>
1({...y,open:!1}))})]})}function Ls(){const t=ne(re.CLOUD_ACCOUNT_VIEW),s=ne(re.CLOUD_ACCOUNT_CREATE),n=qe(),a=ne(re.AI_MODEL_CREATE)&&!n,d=ne(re.CLOUD_ACCOUNT_CREATE)&&!n,u=ne(re.CLOUD_ACCOUNT_DELETE)&&!n,c=ne(re.POLICY_VIEW),x=U(),r=Ze(),{data:o,isLoading:h,isError:g}=lt(),{mutate:f,isPending:p}=dt(),{mutate:m,isPending:w}=ut(),{mutate:b,isPending:k}=mt(),{data:i}=ft("recommendation",{enabled:c}),{data:C}=xt("recommendation",{limit:100,enabled:c}),T=!!i&&(i.effects?.options??[]).some(M=>M.value==="open_ticket"),P=l.useMemo(()=>(C?.items??[]).filter(M=>gt(M.spec)==="open_ticket").length,[C]),v=bt(),[y,S]=l.useState({open:!1,mode:"connect"}),[R,E]=l.useState(!1);if(!t)return e.jsx(oe,{title:"Access restricted",description:"You don't have permission to view integrations."});if(h)return e.jsx("div",{className:"flex items-center justify-center py-16",children:e.jsx(Y,{className:"w-6 h-6"})});if(g)return e.jsx(oe,{title:"Failed to load integrations",description:"Could not fetch integration status. Refresh to try again."});const B=()=>S(M=>({...M,open:!1})),W=()=>{if(!i)return;const M={...vt(i),effect:"open_ticket",name:"All recommendations â Jira",description:"Opens a Jira ticket for every open recommendation."};v.mutate(yt(M,i,x))},G=(M,$)=>{f(M,{onSuccess:()=>{B(),$==="all"?W():$==="specific"&&r("../../policies?domain=recommendation&new=open_ticket")}})};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3",children:[Se()!=="servicenow"&&e.jsx(ns,{integration:o,canEdit:s,canRemove:s,ticketingAvailable:T,openTicketCount:P,onConnect:()=>S({open:!0,mode:"connect"}),onEdit:()=>S({open:!0,mode:"edit"}),onRemove:()=>E(!0)}),Se()==="servicenow"&&e.jsx(Et,{canManage:s}),e.jsx(qt,{canManage:s})]}),Xe()&&e.jsx(us,{canManage:a}),e.jsx(Rt,{canManage:d,canDisconnect:u}),e.jsx(ds,{}),e.jsx(rs,{open:y.open,mode:y.mode,integration:o,ticketingAvailable:T,onClose:B,onConnect:G,onUpdate:M=>m(M,{onSuccess:B}),isPending:p||w}),e.jsx(ie,{open:R,onClose:()=>E(!1),onConfirm:()=>{E(!1),o&&b(o.id)},title:"Remove Jira connection",message:"This revokes the Jira integration. Existing tickets stay in Jira, but new tickets won't be created and sync will stop.",confirmLabel:"Remove",variant:"delete",isLoading:k})]})}export{rs as ConnectDrawer,Ls 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.