feat(files): add safe connector space removal
This commit is contained in:
@@ -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 &&
|
||||
|
||||
Reference in New Issue
Block a user