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
@@ -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>
);
}