feat: implement committee decision workspace
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DismissibleAlert,
|
||||
FormField,
|
||||
type ApiSettings
|
||||
} from "@govoplan/core-webui";
|
||||
import {
|
||||
finalizeProviderBallot,
|
||||
type CommitteeRecord
|
||||
} from "../../api/committee";
|
||||
|
||||
|
||||
export default function CommitteeBallotDialog({
|
||||
settings,
|
||||
record,
|
||||
open,
|
||||
onClose,
|
||||
onSaved
|
||||
}: {
|
||||
settings: ApiSettings;
|
||||
record: CommitteeRecord;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSaved: (record: CommitteeRecord) => void;
|
||||
}) {
|
||||
const [providerBallotRef, setProviderBallotRef] = useState("");
|
||||
const [approvalId, setApprovalId] = useState("");
|
||||
const [changeReason, setChangeReason] = useState("Imported verified ballot aggregate.");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setProviderBallotRef("");
|
||||
setApprovalId("");
|
||||
setChangeReason("Imported verified ballot aggregate.");
|
||||
setError("");
|
||||
}, [open, record.object_id]);
|
||||
|
||||
async function finalize() {
|
||||
setBusy(true);
|
||||
setError("");
|
||||
try {
|
||||
const saved = await finalizeProviderBallot(settings, record, {
|
||||
providerBallotRef,
|
||||
approvalId,
|
||||
changeReason
|
||||
});
|
||||
onSaved(saved);
|
||||
onClose();
|
||||
} catch (reason) {
|
||||
setError(reason instanceof Error ? reason.message : "Ballot result could not be imported.");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
const providerId = String(record.attributes.provider_id ?? "");
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
title="Finalize provider ballot"
|
||||
onClose={onClose}
|
||||
closeDisabled={busy}
|
||||
portal
|
||||
className="committee-ballot-dialog"
|
||||
footer={
|
||||
<>
|
||||
<Button disabled={busy} onClick={onClose}>Cancel</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={busy || !providerBallotRef.trim() || !approvalId.trim() || !changeReason.trim()}
|
||||
onClick={() => void finalize()}
|
||||
>
|
||||
{busy ? "Importing" : "Finalize"}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="committee-record-form">
|
||||
{error ? <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert> : null}
|
||||
<p className="committee-dialog-note">
|
||||
Provider <strong>{providerId}</strong> returns only the verified aggregate result,
|
||||
receipt hash and evidence. Individual secret ballots are not stored in GovOPlaN.
|
||||
</p>
|
||||
<FormField label="Provider ballot reference">
|
||||
<input value={providerBallotRef} disabled={busy} onChange={(event) => setProviderBallotRef(event.target.value)} />
|
||||
</FormField>
|
||||
<FormField label="Approval ID">
|
||||
<input value={approvalId} disabled={busy} onChange={(event) => setApprovalId(event.target.value)} />
|
||||
</FormField>
|
||||
<FormField label="Change reason">
|
||||
<input value={changeReason} maxLength={1000} disabled={busy} onChange={(event) => setChangeReason(event.target.value)} />
|
||||
</FormField>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user