feat(files): add safe connector space removal

This commit is contained in:
2026-08-19 23:25:43 +02:00
parent e1361427b8
commit a9c7a7a40c
7 changed files with 149 additions and 1 deletions
+51
View File
@@ -25,6 +25,7 @@ import {
createFolder,
confirmArchiveUpload,
deleteFolder,
deleteFileConnectorSpace,
downloadFile,
downloadFilesAsZip,
fetchResourceAccessExplanation,
@@ -168,6 +169,7 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
const [connectorSpaceItemsBySpace, setConnectorSpaceItemsBySpace] = useState<Record<string, FileConnectorBrowseItem[]>>({});
const [connectorSpaceLibraryBySpace, setConnectorSpaceLibraryBySpace] = useState<Record<string, string | null>>({});
const [connectorSpaceSelectedItem, setConnectorSpaceSelectedItem] = useState<FileConnectorBrowseItem | null>(null);
const [connectorSpaceRemovalTarget, setConnectorSpaceRemovalTarget] = useState<FileSpace | null>(null);
const [connectorSpaceLoading, setConnectorSpaceLoading] = useState(false);
const [connectorSpaceError, setConnectorSpaceError] = useState("");
const [message, setMessage] = useState("");
@@ -1119,6 +1121,35 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
}
}
async function removeConnectorSpace() {
const target = connectorSpaceRemovalTarget;
if (!canOrganize || !target?.connector_space_id || !isConnectorSpace(target)) return;
setBusy(true);
setError("");
setMessage("");
try {
await deleteFileConnectorSpace(settings, target.connector_space_id);
setConnectorSpaceRemovalTarget(null);
setConnectorSpaceItemsBySpace((current) => {
const next = { ...current };
delete next[target.id];
return next;
});
setConnectorSpaceLibraryBySpace((current) => {
const next = { ...current };
delete next[target.id];
return next;
});
setConnectorSpaceSelectedItem(null);
setMessage(`Removed connector space “${target.label}”. Remote content and managed GovOPlaN files were not changed.`);
await loadSpaces();
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setBusy(false);
}
}
function connectorMetadataString(item: FileConnectorBrowseItem, key: string): string | null {
const value = item.metadata[key];
if (typeof value === "string" && value.trim()) return value;
@@ -2150,6 +2181,15 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
<Button onClick={() => accessExplainableTarget && void openAccessExplanation(accessExplainableTarget)} disabled={Boolean(accessExplanationBlocker)} disabledReason={accessExplanationBlocker}><KeyRound size={16} aria-hidden="true" /> i18n:govoplan-files.explain_access.4d5fac37</Button>
<Button variant="danger" onClick={() => void deleteSelected()} disabled={Boolean(deleteBlocker)} disabledReason={deleteBlocker}><Trash2 size={16} aria-hidden="true" /> i18n:govoplan-files.delete.f6fdbe48</Button>
{activeSpaceIsConnector &&
<Button
variant="danger"
onClick={() => activeSpace && setConnectorSpaceRemovalTarget(activeSpace)}
disabled={busy || !canOrganize || !activeSpace?.connector_space_id}
disabledReason={workingBlocker || (!canOrganize ? "File organization permission is required to remove a connector space." : !activeSpace?.connector_space_id ? "This space is not a removable connector space." : "")}>
<Trash2 size={16} aria-hidden="true" /> Remove space
</Button>
}
{activeSpaceIsConnector &&
<Button onClick={() => activeSpace && void loadConnectorSpaceContents(activeSpace)} disabled={busy || connectorSpaceLoading || !activeSpace} disabledReason={workingBlocker || (connectorSpaceLoading ? "This connector space is already refreshing." : !activeSpace ? "Select a connector space first." : "")}>
<RefreshCw size={16} aria-hidden="true" /> i18n:govoplan-files.refresh.56e3badc
</Button>
@@ -2520,6 +2560,17 @@ export default function FilesPage({ settings, auth }: {settings: ApiSettings;aut
busy={busy}
onConfirm={() => void performConfirmedDelete()}
onCancel={() => setDeleteDialog(null)} />
<ConfirmDialog
open={Boolean(connectorSpaceRemovalTarget)}
title="Remove connector space?"
message={connectorSpaceRemovalTarget ? `Remove “${connectorSpaceRemovalTarget.label}” from Files? This retires only the local connector-space link. Remote files and folders remain at the provider. Imported GovOPlaN files, metadata and shares remain managed in their owner spaces. The connector profile, credentials and remote references are not deleted and can be linked again.` : ""}
confirmLabel="Remove space"
cancelLabel="i18n:govoplan-files.cancel.77dfd213"
tone="danger"
busy={busy}
onConfirm={() => void removeConnectorSpace()}
onCancel={() => setConnectorSpaceRemovalTarget(null)} />
{contextMenu &&