feat(cases): enforce purpose-bound case governance
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-08-24 20:05:31 +02:00
parent 9435116299
commit 17415d8831
28 changed files with 786 additions and 142 deletions
+18 -2
View File
@@ -1,5 +1,6 @@
import { Search } from "lucide-react";
import { useEffect, useMemo, useState, type FormEvent } from "react";
import { useSearchParams } from "react-router";
import { Button,
DocumentationHelpLink,
DismissibleAlert,
@@ -18,6 +19,7 @@ import { Button,
type PlatformRouteContext
} from "@govoplan/core-webui";
import {
DEFAULT_CASE_ACCESS_PURPOSE,
listCaseCatalog,
listCases,
type CaseCatalog,
@@ -28,8 +30,12 @@ import { CASES_DOCUMENTATION, CASES_I18N } from "./interfacePatterns";
export default function CasesPage({ settings }: PlatformRouteContext) {
const navigate = useGuardedNavigate();
const [searchParams] = useSearchParams();
const initialPurpose = searchParams.get("purpose")?.trim() || DEFAULT_CASE_ACCESS_PURPOSE;
const [query, setQuery] = useState("");
const [submittedQuery, setSubmittedQuery] = useState("");
const [purpose, setPurpose] = useState(initialPurpose);
const [submittedPurpose, setSubmittedPurpose] = useState(initialPurpose);
const [status, setStatus] = useState("");
const [cases, setCases] = useState<CaseRecord[]>([]);
const [catalog, setCatalog] = useState<CaseCatalog>({ statuses: [], types: [] });
@@ -46,6 +52,7 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
listCases(settings, {
query: submittedQuery,
statuses: status ? [status] : undefined,
purpose: submittedPurpose,
limit: 200
}, controller.signal),
listCaseCatalog(settings, controller.signal)
@@ -62,7 +69,7 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
}).
finally(() => setLoading(false));
return () => controller.abort();
}, [reloadKey, settings, status, submittedQuery]);
}, [reloadKey, settings, status, submittedPurpose, submittedQuery]);
const statusLabels = useMemo(
() => new Map(catalog.statuses.map((item) => [item.status_key, item])),
@@ -76,6 +83,7 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
function submit(event: FormEvent) {
event.preventDefault();
setSubmittedQuery(query.trim());
setSubmittedPurpose(purpose.trim());
}
return (
@@ -96,6 +104,14 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
aria-label="Search cases"
placeholder="Search cases"
/>
<input
value={purpose}
onChange={(event) => setPurpose(event.target.value)}
aria-label="Case access purpose"
placeholder="Case access purpose"
data-help-context-id="cases.field.access-purpose"
required
/>
<Button
type="submit"
variant="primary"
@@ -135,7 +151,7 @@ export default function CasesPage({ settings }: PlatformRouteContext) {
<SelectionListItem
selected={false}
key={item.reference.object_id}
onClick={() => navigate(`/cases/${encodeURIComponent(item.reference.object_id)}`)}>
onClick={() => navigate(`/cases/${encodeURIComponent(item.reference.object_id)}?purpose=${encodeURIComponent(submittedPurpose)}`)}>
<SelectionListItemContent title={item.title} description={`${item.case_number} · ${typeLabels.get(item.case_type_key) ?? humanize(item.case_type_key)} · ${formatDate(item.deadline_at)}`} />
<StatusBadge
status={statusDefinition?.terminal ? "inactive" : "active"}