feat(webui): add bounded file quick access

This commit is contained in:
2026-08-19 18:47:45 +02:00
parent 25140fb0b6
commit 21122e058e
3 changed files with 94 additions and 12 deletions
+11 -4
View File
@@ -578,6 +578,8 @@ manifest = ModuleManifest(
required_any=("files:file:read",), required_any=("files:file:read",),
order=10, order=10,
modes=("browse", "select", "upload"), modes=("browse", "select", "upload"),
returned_reference_kinds=("files.file-version",),
help_context_id="files.quick_access.files",
), ),
), ),
), ),
@@ -684,8 +686,10 @@ manifest = ModuleManifest(
summary="Use managed files in the Records and documents area and keep a compact file surface beside current work.", summary="Use managed files in the Records and documents area and keep a compact file surface beside current work.",
body=( body=(
"Files contributes its authorized workspace to Records and documents. When Quick Access is enabled, the owner-rendered " "Files contributes its authorized workspace to Records and documents. When Quick Access is enabled, the owner-rendered "
"file-space summary appears in Files on the right rail and links back to the complete workspace. View and rail settings " "recent-file selector appears in Files on the right rail and can return one exact file-version reference to the current task. "
"do not bypass file ownership, shares, connector policy, integrity gates, or purpose-aware access." "Opening the selector causes Files to re-run its own authorization; completion and cancellation are explicit, and the full Files "
"workspace remains available as a deep-link fallback. View and rail settings may recommend or focus this tool, but do not bypass "
"file ownership, shares, connector policy, integrity gates, or purpose-aware access."
), ),
layer="configured", layer="configured",
documentation_types=("user", "admin"), documentation_types=("user", "admin"),
@@ -711,8 +715,11 @@ manifest = ModuleManifest(
"summary": "Verwaltete Dateien im Produktbereich Akten und Dokumente und optional neben der aktuellen Arbeit verwenden.", "summary": "Verwaltete Dateien im Produktbereich Akten und Dokumente und optional neben der aktuellen Arbeit verwenden.",
"body": ( "body": (
"Files ordnet seinen berechtigten Arbeitsbereich Akten und Dokumente zu. Ist der Schnellzugriff aktiviert, erscheint " "Files ordnet seinen berechtigten Arbeitsbereich Akten und Dokumente zu. Ist der Schnellzugriff aktiviert, erscheint "
"die vom Modul gerenderte Übersicht der Dateibereiche rechts neben der aktuellen Seite. Ansichts- und Leistenkonfiguration " "die vom Modul gerenderte Auswahl zuletzt berechtigter Dateien rechts neben der aktuellen Seite und kann einen exakten "
"umgehen weder Eigentum, Freigaben, Connector-Richtlinien, Integritätsprüfungen noch zweckgebundenen Zugriff." "Dateiversionsverweis an die aktuelle Aufgabe zurückgeben. Files prüft die Berechtigung beim Öffnen erneut; Abschluss und "
"Abbruch sind ausdrücklich, und der vollständige Files-Arbeitsbereich bleibt als Ausweichziel erreichbar. Ansichts- und "
"Leistenkonfiguration dürfen das Werkzeug empfehlen oder fokussieren, umgehen aber weder Eigentum, Freigaben, Connector-"
"Richtlinien, Integritätsprüfungen noch zweckgebundenen Zugriff."
), ),
} }
}, },
@@ -0,0 +1,81 @@
import { FileText, X } from "lucide-react";
import { useCallback } from "react";
import {
Button,
DismissibleAlert,
LoadingFrame,
SelectionList,
SelectionListItem,
SelectionListItemContent,
useDashboardWidgetData,
type QuickAccessToolRenderContext
} from "@govoplan/core-webui";
import { listFiles } from "../../api/files";
type Props = Pick<
QuickAccessToolRenderContext,
"settings" | "launchContext" | "complete" | "cancel"
>;
/**
* Bounded file selection. listFiles remains the owner-side authorization
* check; the host receives only a versioned reference after explicit choice.
*/
export default function FileQuickAccess({
settings,
launchContext,
complete,
cancel
}: Props) {
const load = useCallback(
async () => (await listFiles(settings, { page_size: 7 })).files,
[settings]
);
const { data: files, loading, error } = useDashboardWidgetData(load, 0);
return (
<LoadingFrame loading={loading} label="Loading recent files">
{launchContext.activeObject ? (
<p className="muted small-note">
Select a file for {launchContext.activeObject.label}. Files checks
your current access again before showing this list.
</p>
) : null}
{error ? <DismissibleAlert tone="warning" resetKey={error}>{error}</DismissibleAlert> : null}
<SelectionList variant="navigation" label="Recent authorized files">
{(files ?? []).map((file) => (
<SelectionListItem
key={file.version_id}
selected={false}
onClick={() => complete({
contractVersion: "1",
outcome: "completed",
action: "selected",
reference: {
ownerModule: "files",
kind: "file-version",
objectId: file.version_id,
tenantId: file.tenant_id,
label: file.filename,
version: file.version_id,
path: `/files?fileId=${encodeURIComponent(file.id)}&versionId=${encodeURIComponent(file.version_id)}`
}
})}
>
<SelectionListItemContent
leading={<FileText size={16} aria-hidden="true" />}
title={file.filename}
description={file.display_path}
/>
</SelectionListItem>
))}
</SelectionList>
{!loading && !error && !(files ?? []).length ? (
<p className="muted">No authorized files are available.</p>
) : null}
<div className="button-row compact-actions">
<Button onClick={() => cancel("user")}><X size={15} aria-hidden="true" /> Cancel selection</Button>
</div>
</LoadingFrame>
);
}
+2 -8
View File
@@ -12,6 +12,7 @@ import { FolderTree } from "./features/files/components/FileManagerComponents";
import FileConnectorSettingsPanel from "./features/files/FileConnectorSettingsPanel"; import FileConnectorSettingsPanel from "./features/files/FileConnectorSettingsPanel";
import ManagedFileChooser from "./features/files/components/ManagedFileChooser"; import ManagedFileChooser from "./features/files/components/ManagedFileChooser";
import FileSpacesWidget from "./features/files/FileSpacesWidget"; import FileSpacesWidget from "./features/files/FileSpacesWidget";
import FileQuickAccess from "./features/files/FileQuickAccess";
import { listFileSpaces, listFiles, listFilesDelta, listFolders, resolveFilePatterns, shareFilesWithTarget } from "./api/files"; import { listFileSpaces, listFiles, listFilesDelta, listFolders, resolveFilePatterns, shareFilesWithTarget } from "./api/files";
import { generatedTranslations } from "./i18n/generatedTranslations"; import { generatedTranslations } from "./i18n/generatedTranslations";
import "./styles/file-manager.css"; import "./styles/file-manager.css";
@@ -72,14 +73,7 @@ const fileQuickAccessTools: QuickAccessToolsUiCapability = {
tools: [ tools: [
{ {
id: "files.recent", id: "files.recent",
render: ({ settings }) => createElement(FileSpacesWidget, { render: (context) => createElement(FileQuickAccess, context)
settings,
refreshKey: 0,
configuration: {
maxItems: 7,
includeConnectors: true
}
})
} }
] ]
}; };