'use client' import { useState, useEffect, useRef } from 'react' import { Lock, Eye, EyeOff, Trash2, Copy, Check } from 'lucide-react' import { revealSecret, deleteSecret } from '@/lib/actions/secrets' import { OpsAclEditor } from '@/components/ops/ops-acl-editor' import { toast } from 'sonner' import { useConfirm } from '@/components/ui/confirm' import type { AclRoleOption, Secret } from '../types' // ─── Secret Row (reveal on click) ───────────────────────────────────────────── export function SecretRow({ secret, onDelete, canManageAcl, aclRoleOptions, aclInitial }: { secret: Secret onDelete: (id: string) => void canManageAcl?: boolean aclRoleOptions?: AclRoleOption[] aclInitial?: string[] }) { const confirm = useConfirm() const [revealed, setRevealed] = useState(false) const [value, setValue] = useState(null) const [loading, setLoading] = useState(false) const [showAcl, setShowAcl] = useState(false) const [copied, setCopied] = useState(false) const hideTimer = useRef | null>(null) function hide() { if (hideTimer.current) { clearTimeout(hideTimer.current); hideTimer.current = null } setRevealed(false) setValue(null) setCopied(false) } async function reveal() { if (revealed) { hide(); return } setLoading(true) const result = await revealSecret(secret.id) setLoading(false) if (result.error) { toast.error(result.error); return } setValue(result.value ?? '') setRevealed(true) if (hideTimer.current) clearTimeout(hideTimer.current) hideTimer.current = setTimeout(hide, 30000) } async function copy() { if (!value) return try { await navigator.clipboard.writeText(value) setCopied(true) setTimeout(() => setCopied(false), 1500) } catch { toast.error('Could not copy to clipboard') } } // Re-hide a revealed secret when the window loses focus (screen share / // shoulder surfing), and drop the plaintext + timer on unmount. useEffect(() => { const onBlur = () => hide() window.addEventListener('blur', onBlur) return () => { window.removeEventListener('blur', onBlur) if (hideTimer.current) clearTimeout(hideTimer.current) } }, []) async function handleDelete() { if (!(await confirm({ title: 'Delete secret', description: `"${secret.title}" cannot be undone.`, confirmText: 'Delete', destructive: true }))) return const result = await deleteSecret(secret.id) if (result.error) { toast.error(result.error); return } toast.success('Secret deleted') onDelete(secret.id) } return (

{secret.title}

{secret.area &&

{secret.area}

} {revealed && value && (

{value}

)}
{canManageAcl && ( )}
{showAcl && canManageAcl && ( )}
) }