PageSourceSearch

https://unicorniq.ai/assets/directory-sync-Bo83vKGx.js

js unicorniq.ai collected 2026-10-05 16:12:16 UTC 37,857 bytes, 1 lines download raw bytes

1import{r as h,j as e}from"./index-DP61GySm.js";import{b as B,L}from"./index-MuG4xkEZ.js";import{u as oe,z as ie,k8 as de,k9 as ce,ka as le,a2 as y,kb as Y,kc as me,E as b,ai as ue,kd as he,ke as pe,kf as ge,kg as ye,kh as xe,ki as fe,kj as ve,kk as be}from"./loader-circle-eo-mph3g.js";import{u as F,C as j,c as N,a as D,b as _,d as M}from"./use-toast-C6aFH3Eg.js";import{O as we,a1 as Z,a2 as I,L as C,B as S,ap as U,cZ as je,ah as Ne,S as ke,aW as Se,aX as Ce,aY as Re,aZ as Ge,a_ as De,D as O}from"./App-0t6TVAR4.js";import{T as W,I as _e}from"./triangle-alert-CaKCF_Nq.js";import{d as Me,S as Ae,E as Ee}from"./section-chips-C68mRSIN.js";import{A as Ve}from"./sparkles-B4yqwNG6.js";import{S as J}from"./shield-check-BQHCFnRX.js";import{c as ee}from"./toaster-BDyw2x6L.js";import{X as Te}from"./x-D235ZOFZ.js";import"./vendor-shared-C5tCw9rW.js";import"./utils-hQAQiId-.js";import"./trademark-screen-DYufZ-CB.js";import"./waypoints-C6gFN1cM.js";import"./dialog-B9xd4dRw.js";import"./privacy-I8Fotoxq.js";import"./chevron-right-qcpptIv2.js";import"./chevron-down-C8mjF8sg.js";import"./image-off-CpRV4JaY.js";import"./trial-copy-BQN_0jwW.js";import"./unicorniq-logo-B9mOeuD0.js";const A={microsoft:{name:"Microsoft Entra",directory:"Microsoft directory",groups:"Entra groups",connectCta:"Approve as Microsoft administrator",connectHelp:"A Microsoft administrator of your directory approves Vera’s platform app once (read-only application permissions). Vera then reads users and groups on the schedule you choose.",connectedAs:"Consented directory",scopeAll:"Everyone in the directory",scopeGroups:"Selected Entra groups",groupSearchPlaceholder:"Search Entra groups by name",disconnectHelp:"Disconnecting stops the schedule and forgets the consent. Members, groups and links stay exactly as they are — this connector never deletes anything."},google:{name:"Google Workspace",directory:"Google Workspace directory",groups:"Google Groups",connectCta:"Connect as a Google Workspace admin",connectHelp:"Sign in with a Google Workspace admin account from one of the domains bound to this workspace and grant Vera read-only directory access (users, groups, members). Vera reads only the bound domains you select, on the schedule you choose, using that admin’s grant.",connectedAs:"Connected Google Workspace",scopeAll:"Everyone in the selected domains",scopeGroups:"Selected Google Groups",groupSearchPlaceholder:"Search Google Groups by name or email",disconnectHelp:"Disconnecting stops the schedule and removes the admin grant Vera was reading with. Members, groups and links stay exactly as they are — this connector never deletes anything."}},We={consent_revoked:{title:"Google grant revoked",detail:"Google no longer accepts the connecting admin’s grant — it was revoked in the Google Account (or Workspace admin) console, or expired. Scheduled runs stay paused until an admin connects again."},permission_missing:{title:"Directory scopes missing",detail:"The grant no longer carries the read-only Admin SDK directory scopes (users, groups, members), or the connecting account is no longer a Workspace admin with directory read rights. Connect again as a Google Workspace admin."},platform_app_missing:{title:"Platform Google app not registered",detail:"Vera’s team has not registered the platform Google app yet — nothing to connect with until it exists."},directory_unreachable:{title:"Google Admin SDK unreachable",detail:"The directory could not be read right now (network, rate limit or Google-side error). The next scheduled run retries; you can also run it again."},directory_mismatch:{title:"Wrong Google Workspace",detail:"The Google Workspace that answered is not the one recorded for this workspace. Connect again from an admin account in a bound domain."},consent_denied:{title:"Grant not given",detail:"The Google Workspace admin declined or cancelled. Nothing was changed in Vera."},consent_failed:{title:"Connection could not be recorded",detail:"Google answered but Vera could not complete or save the connection. Try again in a moment."}},E={domain_unbound:{title:"Google domain not bound to this workspace",detail:"Vera syncs only Google Worksp
1ace domains that Vera’s team has bound to this workspace. The connecting account’s domain (or a selected domain) is not bound, or no Google domain is bound yet. Nothing was written. Ask Vera’s team to bind the domain, then connect or choose domains again."},domains_required:{title:"Choose which domains to sync",detail:"More than one Google Workspace domain is bound to this workspace. Pick at least one before running or scheduling the sync."},domain_proof_required:{title:"Not a Google Workspace account",detail:"The Google account that signed in carries no hosted domain (personal Gmail accounts cannot administer a Workspace directory). Connect with a Workspace admin account from a bound domain."},credential_missing:{title:"Admin grant missing",detail:"The connecting admin’s Google grant is no longer stored (disconnected from the Integrations page or removed). Scheduled runs stay paused until an admin connects again."},owner_inactive:{title:"Connecting admin deactivated",detail:"The Vera member whose Google grant powers this sync was deactivated or removed, so their grant cannot be used. Another Google Workspace admin must connect. Scheduled runs stay paused until then."},provider_connected:{title:"Another directory is already connected",detail:"A workspace mirrors one directory provider at a time. Disconnect the current one first; nothing was changed."},provider_required:{title:"Choose a directory provider",detail:"This workspace signs in without Google or Microsoft, so pick which directory to connect: Google Workspace or Microsoft Entra."},directory_sync_unavailable:{title:"Directory sync is not switched on",detail:"Directory sync is a platform-enabled add-on and is currently off for this workspace. Nothing was disconnected; ask Vera’s team to switch it on from the platform tenant console."},consent_revoked:{title:"Admin consent revoked",detail:"Microsoft no longer lets Vera read this directory — the administrator approval was withdrawn or never completed. A Microsoft administrator must approve Vera again."},permission_missing:{title:"Permission missing",detail:"Microsoft refused a directory read because the platform app lacks one of the application permissions for this directory (User.Read.All, Group.Read.All, GroupMember.Read.All), or they were not admin-consented."},platform_app_missing:{title:"Platform Microsoft app not registered",detail:"Vera’s team has not registered the platform Microsoft app yet — nothing to approve or sync until it exists."},directory_unreachable:{title:"Microsoft Graph unreachable",detail:"The directory could not be read right now (network or Microsoft-side error). The next scheduled run retries; you can also run it again."},directory_mismatch:{title:"Wrong directory",detail:"The directory Microsoft returned is not the one this workspace signs in with (or the one your own Microsoft sign-in belongs to). Approve from that directory instead."},directory_proof_required:{title:"Directory ownership not yet verifiable",detail:"This workspace has no Microsoft sign-in binding and you did not sign in to Vera with a Microsoft work account, so Vera cannot confirm the returned directory is yours. Sign in with your Microsoft work account first, or ask Vera’s team to bind your directory."},directory_invalid:{title:"Directory not recognised",detail:"Microsoft returned no usable directory id (personal Microsoft accounts cannot be synced). Approve with a work or school administrator account."}
1,consent_denied:{title:"Approval not granted",detail:"The Microsoft administrator declined or cancelled. Nothing was changed in Vera."},consent_failed:{title:"Approval could not be recorded",detail:"The approval came back but Vera could not save it. Try again in a moment."},settings_changed:{title:"Superseded by a settings change",detail:"The sync was paused, disconnected or re-scoped while this run was reading the directory, so nothing was written. The next run uses the new settings."},local_state_changed:{title:"Retrying — members kept appearing",detail:"Members were being created by sign-ins faster than this run could reconcile them. Nothing was written by the interrupted batch; the run retries."},apply_incomplete:{title:"Not finished — more members to create",detail:"This run reached its per-attempt limit with members still to be created. Everything counted so far is written; the run continues from where it stopped, and a run that could not finish within its attempts is reported here with what remains — never as completed."},sync_failed:{title:"Sync failed",detail:"The run hit an unexpected error. Each apply batch commits whole or not at all; the counts show what earlier batches of this run had already written."}};function v(t,o=null){return(t&&o==="google"?We[t]:void 0)??(t?E[t]:void 0)??{title:"Something went wrong",detail:t?`Unrecognised reason code: ${t}.`:"No reason code was reported."}}function q(t){return t.dryRun?"preview (dry run)":t.trigger==="scheduled"?"scheduled sync":"manual sync"}const Pe={create_member:{label:"Create member",className:"border-emerald-500/40 text-emerald-700"},link_member:{label:"Link existing member",className:"border-sky-500/40 text-sky-700"},update_member:{label:"Update member",className:"border-sky-500/40 text-sky-700"},reactivate_member:{label:"Reactivate member",className:"border-emerald-500/40 text-emerald-700"},deactivate_member:{label:"Deactivate member",className:"border-amber-500/40 text-amber-700"},create_group:{label:"Create group",className:"border-emerald-500/40 text-emerald-700"},link_group:{label:"Link existing group",className:"border-sky-500/40 text-sky-700"},update_group:{label:"Update group",className:"border-sky-500/40 text-sky-700"},add_membership:{label:"Add to group",className:"border-sky-500/40 text-sky-700"},remove_membership:{label:"Remove from group",className:"border-amber-500/40 text-amber-700"}};function X(t){return Pe[t]??{label:t,className:""}}function K(t){const o=[],n=(d,s)=>{d>0&&o.push(`${d} ${s}`)};n(t.createdUsers,"created"),n(t.linkedUsers,"linked"),n(t.updatedUsers,"updated"),n(t.reactivatedUsers,"reactivated"),n(t.deactivatedUsers,"deactivated"),n(t.createdGroups,t.createdGroups===1?"group created":"groups created"),n(t.updatedGroups,t.updatedGroups===1?"group renamed":"groups renamed"),n(t.addedMemberships+t.removedMemberships,"membership changes"),n(t.skipped,"skipped");const c=`${t.directoryUsers} directory user${t.directoryUsers===1?"":"s"}, ${t.directoryGroups} group${t.directoryGroups===1?"":"s"} read`;return o.length===0?`No changes — ${c}.`:`${o.join(", ")} — ${c}.`}function $e(t,o){return t.provider??t.offeredProvider??o}const R=(t,o)=>t?.data?.error??o,G=t=>t?new Date(t).toLocaleString():"—";function ht(){const{toast:t}=F(),o=oe(),{data:n}=ie(),c=de({query:{queryKey:Y(),refetchInterval:m=>m.state.data?.liveRun?3e3:!1}}),d=c.data,[s,r]=h.useState(null),i=d?$e(d,s):null,l=()=>Promise.all([o.invalidateQueries({queryKey:Y()}),o.invalidateQueries({queryKey:me()})]);h.useEffect(()=>{const m=new URLSearchParams(window.location.search),p=m.get("consent");if(!p)return;if(p==="ok")t({title:"Directory connected",description:"The directory grant was recorded. Choose what to sync below."});
1else{const k=v(m.get("code"),m.get("provider")==="google"?"google":null);t({title:k.title,description:k.detail,variant:"destructive"})}m.delete("consent"),m.delete("code");const w=m.toString();window.history.replaceState(null,"",window.location.pathname+(w?`?${w}`:""))},[]);const x=ce({mutation:{onSuccess:m=>{window.location.href=m.authorizeUrl},onError:m=>{const p=m?.data?.code??null,w=p&&p in E?v(p,i):null;t({title:w?.title??"Could not start the connection",description:w?.detail??R(m,"Try again in a moment."),variant:"destructive"})}}}),u=le({mutation:{onSuccess:()=>{t({title:"Directory sync disconnected",description:"Nothing was deleted — members and groups stay as they are."}),l()},onError:m=>t({title:"Could not disconnect",description:R(m,"Try again."),variant:"destructive"})}});return n&&!n.roles.includes("tenant_admin")?e.jsx(B,{to:"/"}):n&&!n.directorySyncAvailable?e.jsx(B,{to:"/members"}):e.jsx("div",{className:"flex-1 overflow-y-auto bg-muted/20",children:e.jsxs("div",{className:"max-w-4xl mx-auto p-6 space-y-6","data-testid":"page-directory-sync","data-feature-anchor":"directory-sync-page",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(L,{href:"/members",children:e.jsxs("a",{className:"inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",children:[e.jsx(Ve,{className:"w-4 h-4"})," Back to Members"]})}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(we,{className:"w-6 h-6 text-primary"}),e.jsx("h1",{className:"text-2xl font-semibold","data-testid":"directory-sync-title",children:i?`${A[i].name} directory sync`:"Directory sync"})]}),e.jsxs("p",{className:"text-sm text-muted-foreground max-w-2xl",children:[i==="google"?"Mirror your Google Workspace users and Google Groups (from the domains bound to this workspace) as workspace members and groups.":i==="microsoft"?"Mirror your Microsoft Entra ID users and groups as workspace members and groups.":"Mirror your Google Workspace or Microsoft Entra ID users and groups as workspace members and groups."," ","Vera creates people before they sign in, keeps group membership (including nesting) current, and deactivates leavers — it never deletes anyone, never touches founding admins, and never overrides a role an admin set by hand. Nothing is ever written back to ",i==="google"?"Google":i==="microsoft"?"Entra":"the directory","."]})]}),c.isLoading&&e.jsxs("div",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:[e.jsx(y,{className:"w-4 h-4 animate-spin"})," Loading…"]}),c.isError&&e.jsx(j,{className:"border-destructive/40",children:e.jsx(N,{className:"pt-6 text-sm text-destructive",children:"Could not load directory sync status."})}),d&&!d.available&&e.jsx(j,{className:"border-amber-500/40","data-testid":"directory-sync-unavailable-notice",children:e.jsxs(N,{className:"pt-6 text-sm",children:[e.jsx("p",{className:"font-medium",children:v("directory_sync_unavailable").title}),e.jsxs("p",{className:"text-muted-foreground mt-1",children:[v("directory_sync_unavailable").detail,d.paused?" Your schedule is still on and resumes on the next tick once switched back on.":""]})]})}),d&&!d.provider&&!d.offeredProvider&&e.jsx(Le,{chosen:s,onChoose:r}),d&&i&&e.jsxs(e.Fragment,{children:[e.jsx(Ie,{s:d,provider:i,starting:x.isPending,onStart:()=>x.mutate({data:{provider:i}}),disconnecting:u.isPending,onDisconnect:()=>{window.confirm("Disconnect directory sync? Scheduled syncs stop. No members or groups are deleted.")&&u.mutate()}}),d.consented&&e.jsxs(e.Fragment,{children:[e.jsx(Oe,{s:d,provider:i,onRefresh:()=>void l()}),e.jsx(qe,{s:d,provider:i,onSaved:()=>void l()}),e.jsx(Fe,{s:d,onChanged:()=>void l()}),e.jsx(Ue,{})]})]})]})})}function Le({chosen:t,onChoose:o}){return e.jsxs(j,{"data-testid":"directory-sync-provider-chooser","data-feature-anchor":"directory-sync-provider-chooser",children:[e.jsxs(D,{children:[e.jsx(_,{className:"text-lg",children:"Which directory?"}),e.jsx(M,{children:"This workspace does not sign in with Google or Microsoft, so choose the directory to mirror. Whatever you pick, Vera connects only when the connecting administrator proves the directory is yours."})]}),e.jsx(N,{children:e.jsx(Z,{value:t??"",onValueChange:n=>o(n),className:"space-y-2",children:["google","microsoft"].map(n=>e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(I,{value:n,id:`provider-${n}`,"data-testid":`directory-sync-provider-${n}`}),e.jsxs(C,{htmlFor:`provider-${n}`,className:"font-normal leading-tight",children:[e.jsx("span",{className:"font-medium",children:A[n].name})," — ",A[n].groups," and users"]})]},n))})})]})}function Ie({s:t,provider:o,starting:n,onStart:c,disconnecting:d,onDisconnect:s}){const r=A[o],i=o==="google",l=i?t.googleAppConfigured:t.platformAppConfigured,x=i&&t.boundDomains.length===0;return e.jsxs(j,{"data-testid":"directory-sync-consent-card","data-feature-anchor":"directory-sync-consent","data-provider":o,children:[e.jsxs(D,{children:[e.jsxs(_,{className:"flex items-center gap-2 text-lg",children:[t.consented?e.jsx(J,{className:"w-5 h-5 text-emerald-600"}):e.jsx(ee,{className:"w-5 h-5 text-primary"}
1),i?"Google Workspace admin connection":"Microsoft administrator approval"]}),e.jsx(M,{children:t.consented?i?e.jsxs(e.Fragment,{children:["Google Workspace customer ",e.jsx("code",{className:"font-mono text-xs",children:t.directoryId})," connected on ",G(t.consentedAt),t.connectingAccount?e.jsxs(e.Fragment,{children:[" as ",e.jsx("span",{className:"font-mono text-xs","data-testid":"directory-sync-connecting-account",children:t.connectingAccount})]}):null,"."]}):e.jsxs(e.Fragment,{children:["Directory ",e.jsx("code",{className:"font-mono text-xs",children:t.directoryId})," approved Vera on ",G(t.consentedAt),"."]}):i?r.connectHelp:"A Microsoft global or privileged-role administrator approves the platform app once for your directory. Vera then reads users and groups app-only with the permissions below."})]}),e.jsxs(N,{className:"space-y-3",children:[e.jsxs("div",{className:"text-xs text-muted-foreground",children:[e.jsx("div",{className:"font-medium text-foreground mb-1",children:i?"Read-only directory scopes the admin grants (platform Google app)":"Application permissions requested (platform app)"}),e.jsx("div",{className:"flex flex-wrap gap-1.5","data-testid":"directory-sync-permissions",children:(i?t.directoryScopes:t.applicationPermissions).map(u=>e.jsx(S,{variant:"outline",className:"font-mono text-[11px]",children:u},u))})]}),!i&&t.boundDirectoryIds.length>0&&!t.consented&&e.jsxs("p",{className:"text-xs text-muted-foreground",children:["This workspace signs in with Microsoft directory",t.boundDirectoryIds.length>1?"ies":""," ",e.jsx("span",{className:"font-mono",children:t.boundDirectoryIds.join(", ")})," — approval from any other directory is refused."]}),i&&t.boundDomains.length>0&&e.jsxs("p",{className:"text-xs text-muted-foreground","data-testid":"directory-sync-bound-domains",children:["Bound Google Workspace domain",t.boundDomains.length>1?"s":"",":"," ",e.jsx("span",{className:"font-mono",children:t.boundDomains.join(", ")})," — the connecting admin must belong to one, and only these can be synced."]}),x&&e.jsxs("div",{className:"rounded-md border border-amber-500/40 bg-amber-500/5 p-3 text-sm","data-testid":"directory-sync-domain-unbound",children:[e.jsxs("div",{className:"font-medium flex items-center gap-1.5",children:[e.jsx(W,{className:"w-4 h-4 text-amber-600"})," No Google domain bound yet"]}),e.jsx("p",{className:"text-muted-foreground mt-1",children:E.domain_unbound.detail})]}),!l&&e.jsxs("div",{className:"rounded-md border border-amber-500/40 bg-amber-500/5 p-3 text-sm","data-testid":"directory-sync-platform-app-missing",children:[e.jsxs("div",{className:"font-medium flex items-center gap-1.5",children:[e.jsx(W,{className:"w-4 h-4 text-amber-600"})," Not yet available"]}),e.jsx("p",{className:"text-muted-foreground mt-1",children:i?e.jsxs(e.Fragment,{children:["Vera’s team has not registered the platform Google app yet, so there is nothing to connect with. See the ",e.jsx(L,{href:"/google-setup",children:e.jsx("a",{className:"underline",children:"Google setup guide"})}),"."]}):e.jsxs(e.Fragment,{children:["Vera’s team has not registered the platform Microsoft app yet, so there is nothing for your administrator to approve. See the ",e.jsx(L,{href:"/microsoft-setup",children:e.jsx("a",{className:"underline",children:"Microsoft setup guide"})}),"."]})})]}),e.jsx("div",{className:"flex flex-wrap gap-2",children:t.consented?e.jsxs(e.Fragment,{children:[e.jsxs(b,{variant:"outline",size:"sm",onClick:c,disabled:n,"data-testid":"directory-sync-reconsent",children:[n?e.jsx(y,{className:"w-4 h-4 animate-spin"}):e.jsx(U,{className:"w-4 h-4"}),i?"Re-connect":"Re-approve"]}),e.jsxs(b,{variant:"ghost",size:"sm",onClick:s,disabled:d,className:"text-muted-foreground","data-testid":"directory-sync-disconnect",children:[d?e.jsx(y,{className:"w-4 h-4 animate-spin"}):e.jsx(je,{className:"w-4 h-4"}),"Disconnect"]})]}):e.jsxs(b,{onClick:c,disabled:n||!l||x,"data-testid":"directory-sync-connect",children:[n?e.jsx(y,{className:"w-4 h-4 animate-spin"}):e.jsx(J,{className:"w-4 h-4"}
1),i?r.connectCta:"Connect — open Microsoft admin approval"]})})]})]})}function Oe({s:t,provider:o,onRefresh:n}){const c=t.lastRunErrorCode?v(t.lastRunErrorCode,o):null,d=t.lastRunErrorCode==="consent_revoked"||t.lastRunErrorCode==="permission_missing"||t.lastRunErrorCode==="credential_missing"||t.lastRunErrorCode==="owner_inactive",s=t.lastRunErrorCode==="domain_unbound",r=t.lastRunErrorCode==="sync_failed"&&/interrupted/i.test(t.lastRunErrorMessage??""),i=t.liveRun?"live":c?"error":t.lastRunStatus==="completed"?"ok":"idle";return e.jsxs(j,{className:i==="error"?"border-destructive/40":void 0,"data-testid":"directory-sync-status-card","data-feature-anchor":"directory-sync-status","data-status":i,children:[e.jsxs(D,{children:[e.jsxs(_,{className:"flex items-center gap-2 text-lg",children:[i==="live"&&e.jsx(y,{className:"w-5 h-5 animate-spin text-primary"}),i==="ok"&&e.jsx(ue,{className:"w-5 h-5 text-emerald-600"}),i==="error"&&e.jsx(W,{className:"w-5 h-5 text-destructive"}),i==="idle"&&e.jsx(Me,{className:"w-5 h-5 text-muted-foreground"}),"Status",e.jsx(S,{variant:t.enabled?"default":"outline",className:"ml-1","data-testid":"directory-sync-enabled-badge",children:t.enabled?"Scheduled":"Off"}),e.jsx(b,{variant:"ghost",size:"sm",className:"ml-auto",onClick:n,"aria-label":"Refresh status",children:e.jsx(U,{className:"w-4 h-4"})})]}),e.jsxs(M,{children:[t.liveRun?`A ${q(t.liveRun)} is ${t.liveRun.status==="queued"?"queued":"running"}…`:t.lastRunAt?`Last sync ${G(t.lastRunAt)} — ${t.lastRunStatus==="completed"?"completed":"failed"}.`:"No sync has run yet. Preview first, then run.",t.enabled&&t.nextRunAt?` Next scheduled run ${G(t.nextRunAt)} (every ${t.intervalHours}h).`:""]})]}),e.jsxs(N,{className:"space-y-3",children:[c&&e.jsxs("div",{className:"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm","data-testid":`directory-sync-error-${t.lastRunErrorCode}`,children:[e.jsxs("div",{className:"font-medium flex items-center gap-1.5",children:[e.jsx(ee,{className:"w-4 h-4 text-destructive"})," ",c.title]}),e.jsx("p",{className:"text-muted-foreground mt-1",children:c.detail}),t.lastRunErrorMessage&&e.jsx("p",{className:"text-xs text-muted-foreground mt-1 font-mono break-words",children:t.lastRunErrorMessage}),r&&e.jsxs("p",{className:"text-xs mt-2","data-testid":"directory-sync-interrupted-hint",children:["Nothing is running now — ",e.jsx("span",{className:"font-medium",children:"Sync now"})," (or a dry run) below starts a fresh run; batches that had already committed are kept."]}),d&&e.jsx("p",{className:"text-xs mt-2","data-testid":"directory-sync-reconnect-hint",children:o==="google"?e.jsxs(e.Fragment,{children:["Scheduled runs stay paused. Use ",e.jsx("span",{className:"font-medium",children:"Re-connect"})," above as a Google Workspace admin to restore Vera’s read access."]}):e.jsxs(e.Fragment,{children:["Use ",e.jsx("span",{className:"font-medium",children:"Re-approve"})," above once your Microsoft administrator has restored Vera’s permissions."]})}),s&&e.jsx("p",{className:"text-xs mt-2",children:"Scheduled runs stay paused until the domain choice below matches the bound domains again."})]}),t.lastRun?.counts&&e.jsxs("div",{className:"text-sm","data-testid":"directory-sync-last-counts",children:[e.jsx("span",{className:"text-muted-foreground",children:"Last run:"})," ",K(t.lastRun.counts)]})]})]})}function qe({s:t,provider:o,onSaved:n}){const{toast:c}=F(),d=A[o],s=o==="google",[r,i]=h.useState(t.scopeMode),[l,x]=h.useState(t.selectedGroups),[u,m]=h.useState(t.selectedDomains),[p,w]=h.useState(t.intervalHours),[k,H]=h.useState(t.enabled),[P,se]=h.useState(""),[V,re]=h.useState("");h.useEffect(()=>{const a=setTimeout(()=>re(P.trim()),350);return()=>clearTimeout(a)},[P]),h.useEffect(()=>{i(t.scopeMode),x(t.selectedGroups),w(t.intervalHours),H(t.enabled),m(t.selectedDomains)},[t.scopeMode,t.selectedGroups,t.intervalHours,t.enabled,t.selectedDomains]);const g=he({q:V},{query:{enabled:r==="groups"&&V.length>=2,queryKey:ve({q:V})}}),$=pe({mutation:{onSuccess:()=>{c({title:"Directory sync settings saved"}),n()},onError:a=>{const f=a?.data?.code??null,T=f&&f in E?v(f,o):null;c({title:T?.title??"Could not save",description:T?.detail??R(a,"Check the settings and try again."),variant:"destructive"})}}}),ae=h.useMemo(()=>r!==t.scopeMode||p!==t.intervalHours||k!==t.enabled||JSON.stringify(l.map(a=>a.id))!==JSON.stringify(t.selectedGroups.map(a=>a.id))||s&&JSON.stringify([...u].sort())!==JSON.stringify([...t.selectedDomains].sort()),[r,p,k,l,u,s,t]),Q=new Set(l.map(a=>a.id)),z=s&&u.length===0;return e.jsxs(j,{"data-testid":"directory-sync-scope-card","data-feature-anchor":"directory-sync-scope",children:[e.jsxs(D,{children:[e.jsx(_,{className:"text-lg",children:"Scope & schedule"}),e.jsx(M,{children:"Choose who is synced and how often. Selected groups are mirrored as Vera groups with their nesting; people outside the scope are never created."})]}),e.jsxs(N,{className:"space-y-5",children:[s&&e.jsxs("div",{className:"space-y-2","data-testid":"directory-sync-domain-picker","data-feature-anchor":"directory-sync-domain-picker",children:[e.jsx("div",{className:"text-sm font-medium",children:"Domains to sync"}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Only Google Worksp
1ace domains bound to this workspace by Vera’s team can be synced; users and groups outside the chosen domains are never touched.",t.boundDomains.length===1?" One domain is bound, so it is selected for you.":""]}),t.boundDomains.length===0?e.jsx("p",{className:"text-xs text-destructive",children:E.domain_unbound.detail}):e.jsxs("div",{className:"space-y-1.5",children:[t.boundDomains.map(a=>{const f=u.includes(a);return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Ne,{id:`domain-${a}`,checked:f,disabled:t.boundDomains.length===1,onCheckedChange:T=>m(T?[...u,a]:u.filter(ne=>ne!==a)),"data-testid":`directory-sync-domain-${a}`}),e.jsx(C,{htmlFor:`domain-${a}`,className:"font-mono text-sm font-normal",children:a})]},a)}),u.filter(a=>!t.boundDomains.includes(a)).map(a=>e.jsxs("div",{className:"flex items-center gap-2 text-xs text-destructive","data-testid":"directory-sync-domain-stale",children:[e.jsx(W,{className:"w-3.5 h-3.5"})," ",e.jsx("span",{className:"font-mono",children:a})," is no longer bound — remove it to save.",e.jsx("button",{type:"button",className:"underline",onClick:()=>m(u.filter(f=>f!==a)),children:"Remove"})]},a))]})]}),e.jsxs(Z,{value:r,onValueChange:a=>i(a),className:"space-y-2",children:[e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(I,{value:"all",id:"scope-all","data-testid":"directory-sync-scope-all"}),e.jsxs(C,{htmlFor:"scope-all",className:"font-normal leading-tight",children:[e.jsx("span",{className:"font-medium",children:s?"Everyone in the selected domains":"Everyone"})," — every ",s?"active user in those domains (suspended and archived users are deactivated)":"enabled member user in the directory"]})]}),e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(I,{value:"groups",id:"scope-groups","data-testid":"directory-sync-scope-groups"}),e.jsxs(C,{htmlFor:"scope-groups",className:"font-normal leading-tight",children:[e.jsxs("span",{className:"font-medium",children:["Only members of selected ",d.groups]})," — transitive membership, nested groups mirrored"]})]})]}),r==="groups"&&e.jsxs("div",{className:"space-y-2 pl-6",children:[e.jsxs("div",{className:"flex flex-wrap gap-1.5","data-testid":"directory-sync-selected-groups",children:[l.length===0&&e.jsx("span",{className:"text-xs text-muted-foreground",children:"No groups selected yet."}),l.map(a=>e.jsxs(S,{variant:"secondary",className:"gap-1 pr-1",children:[a.displayName,e.jsx("button",{type:"button","aria-label":`Remove ${a.displayName}`,onClick:()=>x(l.filter(f=>f.id!==a.id)),className:"rounded hover:bg-muted p-0.5",children:e.jsx(Te,{className:"w-3 h-3"})})]},a.id))]}),e.jsxs("div",{className:"relative max-w-md",children:[e.jsx(ke,{className:"w-4 h-4 absolute left-2.5 top-2.5 text-muted-foreground"}),e.jsx(_e,{value:P,onChange:a=>se(a.target.value),placeholder:`${d.groupSearchPlaceholder}…`,className:"pl-8","data-testid":"directory-sync-group-search"})]}),g.isFetching&&e.jsxs("div",{className:"text-xs text-muted-foreground flex items-center gap-1",children:[e.jsx(y,{className:"w-3 h-3 animate-spin"})," Searching your directory…"]}),g.isError&&e.jsxs("div",{className:"text-xs text-destructive","data-testid":"directory-sync-group-search-error",children:[v(g.error?.data?.code??null,o).title,": ",R(g.error,`Could not read groups from ${d.name}.`)]}),g.data&&g.data.groups.length===0&&!g.isFetching&&e.jsxs("div",{className:"text-xs text-muted-foreground",children:["No groups match “",V,"”."]}),g.data&&g.data.groups.length>0&&e.jsx("ul",{className:"rounded-md border divide-y max-w-md","data-testid":"directory-sync-group-results",children:g.data.groups.map(a=>e.jsxs("li",{className:"flex items-center justify-between px-3 py-1.5 text-sm",children:[e.jsx("span",{className:"truncate",children:a.displayName}),e.jsx(b,{size:"sm",variant:"ghost",disabled:Q.has(a.id)||l.length>=50,onClick:()=>x([...l,a]),children:Q.has(a.id)?"Added":"Add"})]},a.id))})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-6",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(C,{htmlFor:"sync-interval",className:"text-sm",children:"Run every"}),e.jsxs(Se,{value:String(p),onValueChange:a=>w(Number(a)),children:[e.jsx(Ce,{id:"sync-interval",className:"w-36","data-testid":"directory-sync-interval",children:e.jsx(Re,{})}),e.jsx(Ge,{children:t.intervalOptions.map(a=>e.jsx(De,{value:String(a),children:a===168?"week":`${a} hours`},a))})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Ae,{id:"sync-enabled",checked:k,onCheckedChange:H,"data-testid":"directory-sync-enabled"}),e.jsx(C,{htmlFor:"sync-enabled",className:"text-sm",children:"Scheduled sync on"})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(b,{onClick:()=>$.mutate({data:{scopeMode:r,selectedGroups:l,...s?{selectedDomains:u}:{},intervalHours:p,enabled:k}}),disabled:!ae||$.isPending||r==="groups"&&l.length===0||z,"data-testid":"directory-sync-save",children:[$.isPending&&e.jsx(y,{className:"w-4 h-4 animate-spin"})," Save settings"]}),r==="groups"&&l.length===0&&e.jsx("span",{className:"text-xs text-muted-foreground",children:"Select at least one group."}),z&&e.jsx("span",{className:"text-xs text-muted-foreground",children:"Choose at least one domain."})]})]})]})}function Fe({s:t,onChanged:o}){const{toast:n}=F(),[c,d]=h.useState(null),s=ge({mutation:{onSuccess:l=>{d(l),o()},onError:l=>n({title:"Preview failed",description:R(l,"Try again."),variant:"destructive"})}}),r=ye({mutation:{onSuccess:()=>{n({title:"Sync queued",description:"The status card updates as it runs."}),o()},onError:l=>n({title:"Could not start the sync",description:R(l,"Try again."),variant:"destructive"})}}),i=s.isPending||r.isPending||!!t.liveRun;return e.jsxs(j,{"data-testid":"directory-sync-preview-card","data-feature-anchor":"directory-sync-preview",children:[e.jsxs(D,{children:[e.jsx(_,{className:"text-lg",children:"Preview & run"}),e.jsx(M,{children:"A dry run lists exactly what would be created, updated, added to or removed from groups, and deactivated — nothing is written. Running re-plans from the live state and applies it in bounded all-or-nothing batches (a large first sync commits several; each batch's counts are written with its changes, so the card never shows changes that were not made) and audit-logs every change with the sync as actor."})]}),e.jsxs(N,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs(b,{variant:"outline",onClick:()=>s.mutate(),disabled:i,"data-testid":"directory-sync-preview",children:[s.isPending?e.jsx(y,{className:"w-4 h-4 animate-spin"}):e.jsx(Ee,{className:"w-4 h-4"})," Dry run (preview)"]}
1),e.jsxs(b,{onClick:()=>r.mutate(),disabled:i||!t.enabled,"data-testid":"directory-sync-run",children:[r.isPending?e.jsx(y,{className:"w-4 h-4 animate-spin"}):e.jsx(U,{className:"w-4 h-4"})," Sync now"]}),!t.enabled&&e.jsx("span",{className:"text-xs text-muted-foreground self-center",children:"Turn the scheduled sync on and save to enable “Sync now”."})]}),c&&e.jsx(te,{run:c,heading:"Preview result"})]})]})}function te({run:t,heading:o}){const n=t.errorCode?v(t.errorCode):null,c=[{id:"action",header:"Change",sortValue:s=>X(s.action).label,cellClassName:"w-44",cell:s=>{const r=X(s.action);return e.jsx(S,{variant:"outline",className:r.className,children:r.label})}},{id:"label",header:"Who / what",alwaysVisible:!0,sortValue:s=>s.label,cellClassName:"font-medium",cell:s=>s.label},{id:"detail",header:"Detail",cellClassName:"text-muted-foreground text-xs",cell:s=>s.detail}],d=[{id:"kind",header:"Kind",sortValue:s=>s.kind,cellClassName:"w-24 text-xs text-muted-foreground",cell:s=>s.kind},{id:"label",header:"Who / what",alwaysVisible:!0,sortValue:s=>s.label,cellClassName:"font-medium",cell:s=>s.label},{id:"reason",header:"Reason",sortValue:s=>s.reasonLabel,cellClassName:"text-xs text-muted-foreground",cell:s=>s.reasonLabel}];return e.jsxs("div",{className:"space-y-3","data-testid":"directory-sync-run-detail","data-run-status":t.status,children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("span",{className:"font-medium text-sm",children:o}),e.jsx(S,{variant:t.status==="completed"?"default":t.status==="failed"?"destructive":"outline",children:t.status}),t.dryRun&&e.jsx(S,{variant:"outline",children:"dry run"}),e.jsx("span",{className:"text-xs text-muted-foreground",children:G(t.finishedAt??t.createdAt)})]}),n&&e.jsxs("div",{className:"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm",children:[e.jsx("div",{className:"font-medium",children:n.title}),e.jsx("p",{className:"text-muted-foreground mt-1",children:t.errorMessage??n.detail})]}),t.counts&&e.jsx("div",{className:"text-sm","data-testid":"directory-sync-run-counts",children:K(t.counts)}),t.planRows.length>0&&e.jsxs("div",{children:[e.jsx(O,{tableId:"directory-sync-plan",title:"Directory sync plan",columns:c,rows:t.planRows,rowKey:s=>`${s.action}:${s.label}:${s.detail}`,rowProps:s=>({"data-plan-action":s.action}),"data-testid":"directory-sync-plan-rows",emptyMessage:"No planned changes."}),t.planTruncated>0&&e.jsxs("div",{className:"px-3 py-2 text-xs text-muted-foreground",children:["…and ",t.planTruncated," more change",t.planTruncated===1?"":"s"," not shown."]})]}),t.status==="completed"&&t.planRows.length===0&&t.dryRun&&e.jsx("div",{className:"text-sm text-muted-foreground",children:"Everything is already in step — nothing would change."}),t.skipped.length>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"px-3 py-2 text-xs font-medium text-muted-foreground border-b",children:["Skipped (",t.skipped.length,") — with reasons"]}),e.jsx(O,{tableId:"directory-sync-skipped",title:"Skipped directory sync rows",columns:d,rows:t.skipped,rowKey:s=>`${s.kind}:${s.entraId??s.label}:${s.reason}`,rowProps:s=>({"data-skip-reason":s.reason}),"data-testid":"directory-sync-skipped-rows",emptyMessage:"No skipped rows."})]})]})}function Ue(){const t=xe(),[o,n]=h.useState(null),c=fe(o??"",{query:{enabled:!!o,queryKey:be(o??"")}}),d=t.data?.runs??[],s=[{id:"when",header:"When",alwaysVisible:!0,align:"right",defaultDir:"desc",sortValue:r=>Date.parse(r.finishedAt??r.createdAt),filterValue:()=>"",cellClassName:"text-xs whitespace-nowrap",cell:r=>G(r.finishedAt??r.createdAt)},{id:"kind",header:"Kind",sortValue:r=>q(r),cellClassName:"text-xs",cell:r=>q(r)},{id:"status",header:"Status",sortValue:r=>r.status,cell:r=>e.jsx(S,{variant:r.status==="completed"?"default":r.status==="failed"?"destructive":"outline",children:r.status})},{id:"result",header:"Result",cellClassName:"text-xs text-muted-foreground",cell:r=>r.errorCode?v(r.errorCode).title:r.counts?K(r.counts):"—"},{id:"actions",header:"",label:"Actions",alwaysVisible:!0,align:"right",cell:r=>e.jsx(b,{size:"sm",variant:"ghost",onClick:()=>n(o===r.id?null:r.id),children:o===r.id?"Hide":"Open"})}];return e.jsxs(j,{"data-testid":"directory-sync-history-card","data-feature-anchor":"directory-sync-history",children:[e.jsxs(D,{children:[e.jsx(_,{className:"text-lg",children:"Run history"}),e.jsx(M,{children:"Previews and syncs, newest first. Open a run to see its rows and skipped items."})]}),e.jsxs(N,{className:"space-y-3",children:[t.isLoading&&e.jsx("div",{className:"text-sm text-muted-foreground",children:"Loading…"}),d.length===0&&!t.isLoading&&e.jsx("div",{className:"text-sm text-muted-foreground",children:"No runs yet."}),d.length>0&&e.jsx(O,{tableId:"directory-sync-runs",title:"Directory sync runs",columns:s,rows:d,rowKey:r=>r.id,rowProps:r=>({"data-testid":`directory-sync-run-${r.id}`}),sort:{id:"when",dir:"desc"},emptyMessage:"No runs yet."}
1),o&&c.data&&e.jsx(te,{run:c.data,heading:"Run detail"}),o&&c.isLoading&&e.jsxs("div",{className:"text-xs text-muted-foreground flex items-center gap-1",children:[e.jsx(y,{className:"w-3 h-3 animate-spin"})," Loading run…"]})]})]})}export{ht 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.