import { h } from 'preact'; import { useState, useEffect } from 'preact/hooks'; import type { PanelACL, Grant, Folder } from './lib/types'; interface Props { ifaceId: string; ifaceName: string; folders: Folder[]; onClose: () => void; onSaved: () => void; } // ShareDialog edits a single panel's sharing settings: its folder, public // visibility, and explicit per-user / per-group grants. Only the owner can // reach it (the caller is gated upstream in InterfaceList). export default function ShareDialog({ ifaceId, ifaceName, folders, onClose, onSaved }: Props) { const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [owner, setOwner] = useState(''); const [folder, setFolder] = useState(''); const [pub, setPub] = useState<'' | 'read' | 'write'>(''); const [grants, setGrants] = useState([]); const [userGroups, setUserGroups] = useState([]); // New-grant row state. const [newKind, setNewKind] = useState<'user' | 'group'>('user'); const [newName, setNewName] = useState(''); const [newPerm, setNewPerm] = useState<'read' | 'write'>('read'); useEffect(() => { Promise.all([ fetch(`/api/v1/interfaces/${encodeURIComponent(ifaceId)}/acl`).then(r => r.ok ? r.json() : null), fetch('/api/v1/usergroups').then(r => r.ok ? r.json() : []), ]) .then(([acl, groups]: [PanelACL | null, string[]]) => { if (acl) { setOwner(acl.owner || ''); setFolder(acl.folder || ''); setPub(acl.public || ''); setGrants(Array.isArray(acl.grants) ? acl.grants : []); } setUserGroups(Array.isArray(groups) ? groups : []); }) .catch(() => setError('Failed to load sharing settings.')) .finally(() => setLoading(false)); }, [ifaceId]); function addGrant() { const name = newName.trim(); if (!name) return; // Replace an existing grant for the same kind+name rather than duplicating. const next = grants.filter(g => !(g.kind === newKind && g.name === name)); next.push({ kind: newKind, name, perm: newPerm }); setGrants(next); setNewName(''); } function removeGrant(idx: number) { setGrants(grants.filter((_, i) => i !== idx)); } async function save() { setSaving(true); setError(''); try { const res = await fetch(`/api/v1/interfaces/${encodeURIComponent(ifaceId)}/acl`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ folder, public: pub, grants }), }); if (!res.ok) { const msg = await res.json().catch(() => null); throw new Error(msg?.error || `Save failed (${res.status})`); } onSaved(); onClose(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setSaving(false); } } return (
e.stopPropagation()} style="max-width: 560px;">
Share — {ifaceName || ifaceId}
{loading ? (

Loading…

) : (
{owner && (

Owner: {owner}

)}
Folder
Public access
Shared with
{grants.length === 0 ? (

Not shared with anyone yet.

) : (
    {grants.map((g, idx) => (
  • {g.kind} {g.name} ({g.perm})
  • ))}
)}
{newKind === 'group' ? ( ) : ( setNewName((e.target as HTMLInputElement).value)} onKeyDown={e => e.key === 'Enter' && addGrant()} /> )}
{error &&

{error}

}
)}
); }