import { useEffect, useMemo, useState } from "react"; import { Trash2 } from "lucide-react"; import { Button, ConfirmDialog, DataGrid, DismissibleAlert, FormField, ReferenceSelect, StatusBadge, TableActionGroup, type ApiSettings, type DataGridColumn } from "@govoplan/core-webui"; import { createFileShare, fileShareTargetProvider, listFileShares, revokeFileShare, type FileShare, type ManagedFile } from "../../../api/files"; import { FileDialog } from "./FileManagerComponents"; import { formatDate } from "../utils/fileManagerUtils"; type ShareTargetType = "user" | "group" | "tenant"; type SharePermission = "read" | "write" | "manage"; export default function FileShareDialog({ settings, file, tenantId, onClose, onChanged }: { settings: ApiSettings; file: ManagedFile; tenantId: string; onClose: () => void; onChanged: () => void; }) { const [shares, setShares] = useState([]); const [targetType, setTargetType] = useState("user"); const [targetId, setTargetId] = useState(""); const [permission, setPermission] = useState("read"); const [expiresAt, setExpiresAt] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [revokeTarget, setRevokeTarget] = useState(null); const targetProvider = useMemo( () => targetType === "tenant" ? null : fileShareTargetProvider(settings, file.id, targetType), [file.id, settings, targetType] ); useEffect(() => { void refresh(); }, [file.id]); async function refresh() { setBusy(true); setError(""); try { const response = await listFileShares(settings, file.id, { includeInactive: true }); setShares(response.shares); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(false); } } async function grant() { const effectiveTargetId = targetType === "tenant" ? tenantId : targetId; if (!effectiveTargetId) return; setBusy(true); setError(""); try { await createFileShare(settings, file.id, { target_type: targetType, target_id: effectiveTargetId, permission, expires_at: expiresAt ? new Date(expiresAt).toISOString() : null }); setTargetId(""); setExpiresAt(""); await refresh(); onChanged(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setBusy(false); } } async function revoke() { if (!revokeTarget) return; setBusy(true); setError(""); try { await revokeFileShare(settings, file.id, revokeTarget.id); setRevokeTarget(null); await refresh(); onChanged(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setBusy(false); } } const columns = useMemo[]>(() => [ { id: "target", header: "Target", minWidth: 220, resizable: true, sortable: true, filterable: true, value: (share) => `${share.target_type}: ${share.target_id}` }, { id: "permission", header: "Permission", width: 115, sortable: true, filterable: true, value: (share) => share.permission }, { id: "created", header: "Created", minWidth: 160, resizable: true, sortable: true, value: (share) => formatDate(share.created_at) }, { id: "creator", header: "Created by", minWidth: 150, resizable: true, filterable: true, value: (share) => share.created_by_user_id || "System" }, { id: "expiry", header: "Expires", minWidth: 160, resizable: true, sortable: true, value: (share) => share.expires_at ? formatDate(share.expires_at) : "No expiry" }, { id: "status", header: "Status", width: 115, sortable: true, value: (share) => shareStatus(share), render: (share) => ( ) }, { id: "actions", header: "Actions", width: 72, sticky: "end", align: "right", render: (share) => (