'use client' import { useState } from 'react' import { toast } from 'sonner' import { Check } from 'lucide-react' import { setOpsAcl } from '@/lib/actions' export interface AclRoleOption { value: string; label: string } interface Props { entityType: 'secret' | 'kb' | 'process' entityId: string options: AclRoleOption[] initial: string[] } // Toggling roles here writes ops_acl. An empty selection means the item // falls back to its default visibility (admin/board for secrets; the // visibility column for KB). Selecting roles WIDENS read access to those // roles in addition to the default. export function OpsAclEditor({ entityType, entityId, options, initial }: Props) { const [selected, setSelected] = useState>(new Set(initial)) const [saving, setSaving] = useState(false) async function toggle(value: string, on: boolean) { const next = new Set(selected) if (on) next.add(value) else next.delete(value) setSelected(next) setSaving(true) const result = await setOpsAcl({ entity_type: entityType, entity_id: entityId, roles: Array.from(next) }) setSaving(false) if (result.error) { toast.error(result.error) setSelected(prev => { const back = new Set(prev) if (on) back.delete(value) else back.add(value) return back }) } else { toast.success('Access updated') } } return (

Who can access {saving && …}

{options.length === 0 ? (

No roles available.

) : (
{options.map(o => { const on = selected.has(o.value) return ( ) })}
)}

Admins and board always have access. Empty selection keeps the default visibility.

) }