feat(cases): enforce purpose-bound case governance
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user