Skip to content

Commit a41e4a6

Browse files
committed
feat(governance): enhance ballot and proposal components with type definitions and error handling
1 parent e2ec4e4 commit a41e4a6

File tree

3 files changed

+54
-31
lines changed

3 files changed

+54
-31
lines changed

src/components/pages/wallet/governance/ballot/ballot.tsx

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,39 @@
11
import { useSiteStore } from "@/lib/zustand/site";
22
import { getTxBuilder } from "@/utils/get-tx-builder";
33
import useTransaction from "@/hooks/useTransaction";
4-
import { keepRelevant, Quantity, Unit, UTxO } from "@meshsdk/core";
4+
import { keepRelevant } from "@meshsdk/core";
5+
import type { Quantity, Unit, UTxO } from "@meshsdk/core";
56
import { useWalletsStore } from "@/lib/zustand/wallets";
67
import useMultisigWallet from "@/hooks/useMultisigWallet";
7-
import { toast, useToast } from "@/hooks/use-toast";
8+
import { useToast } from "@/hooks/use-toast";
89
import React, { useState } from "react";
910
import CardUI from "@/components/ui/card-content";
11+
import {
12+
Select,
13+
SelectContent,
14+
SelectGroup,
15+
SelectItem,
16+
SelectTrigger,
17+
SelectValue,
18+
} from "@/components/ui/select";
1019
import { Button } from "@/components/ui/button";
1120
import { api } from "@/utils/api";
1221
import { ToastAction } from "@/components/ui/toast";
1322

1423
const GovAction = 1;
1524

25+
// BallotType should be imported or defined. For now, define it here:
26+
export type BallotType = {
27+
id: string;
28+
type: number;
29+
description: string | null;
30+
walletId: string;
31+
createdAt: Date;
32+
items: string[];
33+
itemDescriptions: string[];
34+
choices: string[];
35+
};
36+
1637
export default function BallotCard({
1738
appWallet,
1839
onSelectBallot,
@@ -28,7 +49,7 @@ export default function BallotCard({
2849
}) {
2950
const [description, setDescription] = useState("");
3051
const [submitting, setSubmitting] = useState(false);
31-
const [ballots, setBallots] = useState<any[]>([]);
52+
const [ballots, setBallots] = useState<BallotType[]>([]);
3253
const [creating, setCreating] = useState(false);
3354

3455
const { toast } = useToast();
@@ -44,7 +65,7 @@ export default function BallotCard({
4465
// CreateBallot mutation
4566
const createBallot = api.ballot.create.useMutation();
4667
// Get ballots for wallet
47-
const getBallots = api.ballot.getByWallet.useQuery(
68+
const getBallots = api.ballot.getByWallet.useQuery<BallotType[]>(
4869
{ walletId: appWallet?.id },
4970
{ enabled: !!appWallet, refetchOnWindowFocus: false },
5071
);
@@ -119,7 +140,7 @@ export default function BallotCard({
119140
// Submit a vote for each proposal in the ballot
120141
for (let i = 0; i < selectedBallot.items.length; ++i) {
121142
const proposalId = selectedBallot.items[i];
122-
const voteKind = selectedBallot.choices?.[i] ?? "Abstain";
143+
const voteKind = (selectedBallot.choices?.[i] ?? "Abstain") as "Yes" | "No" | "Abstain";
123144
const [txHash, certIndex] = (proposalId || "").split("#");
124145
if (!txHash || certIndex === undefined) {
125146
// Skip invalid proposalId
@@ -161,7 +182,7 @@ export default function BallotCard({
161182
// Optionally refresh ballots
162183
await getBallots.refetch();
163184
onBallotChanged?.();
164-
} catch (error) {
185+
} catch (error: unknown) {
165186
if (
166187
error instanceof Error &&
167188
error.message.includes("User rejected transaction")
@@ -174,7 +195,7 @@ export default function BallotCard({
174195
} else {
175196
toast({
176197
title: "Ballot Vote Failed",
177-
description: `Error: ${error}`,
198+
description: `Error: ${error instanceof Error ? error.message : String(error)}`,
178199
duration: 10000,
179200
action: (
180201
<ToastAction
@@ -201,7 +222,7 @@ export default function BallotCard({
201222
}
202223

203224
async function handleSubmit(e: React.FormEvent | React.MouseEvent) {
204-
if (e.preventDefault) e.preventDefault();
225+
if ("preventDefault" in e && typeof e.preventDefault === "function") e.preventDefault();
205226
if (!description.trim()) return;
206227
setSubmitting(true);
207228
try {
@@ -220,14 +241,14 @@ export default function BallotCard({
220241
});
221242
await getBallots.refetch();
222243
onBallotChanged?.();
223-
} catch (e) {
244+
} catch (error: unknown) {
224245
// TODO: handle error
225246
}
226247
setSubmitting(false);
227248
}
228249

229250
// Find the selected ballot if selectedBallotId is set
230-
const selectedBallot = selectedBallotId
251+
const selectedBallot: BallotType | undefined = selectedBallotId
231252
? ballots.find((b) => b.id === selectedBallotId)
232253
: undefined;
233254

@@ -315,7 +336,7 @@ export default function BallotCard({
315336
<Button
316337
variant="destructive"
317338
className="mt-4"
318-
onClick={async () => {
339+
onClick={async (e: React.MouseEvent) => {
319340
try {
320341
await deleteBallot.mutateAsync({ ballotId: selectedBallot.id });
321342
await getBallots.refetch();
@@ -326,7 +347,7 @@ export default function BallotCard({
326347
variant: "destructive",
327348
});
328349
onBallotChanged?.();
329-
} catch (e) {
350+
} catch (error: unknown) {
330351
// handle error
331352
}
332353
}}
@@ -348,7 +369,7 @@ function BallotOverviewTable({
348369
refetchBallots,
349370
onBallotChanged,
350371
}: {
351-
ballot: any;
372+
ballot: BallotType;
352373
ballotId: string;
353374
refetchBallots: () => Promise<any>;
354375
onBallotChanged?: () => void;
@@ -365,23 +386,12 @@ function BallotOverviewTable({
365386
await removeProposalMutation.mutateAsync({ ballotId, index: idx });
366387
await refetchBallots();
367388
onBallotChanged?.();
368-
} catch (e) {
389+
} catch (error: unknown) {
369390
// Optionally handle error
370391
}
371392
setRemovingIdx(null);
372393
}
373394

374-
// Import Select UI
375-
const {
376-
Select,
377-
SelectContent,
378-
SelectGroup,
379-
SelectItem,
380-
SelectTrigger,
381-
SelectValue,
382-
} = require("@/components/ui/select");
383-
const { Button } = require("@/components/ui/button");
384-
385395
return (
386396
<div className="overflow-x-auto rounded-lg shadow">
387397
<table className="min-w-full divide-y divide-gray-200 text-sm dark:divide-gray-700">
@@ -393,7 +403,7 @@ function BallotOverviewTable({
393403
</tr>
394404
</thead>
395405
<tbody>
396-
{ballot.items.map((item: any, idx: number) => (
406+
{ballot.items.map((item: string, idx: number) => (
397407
<tr
398408
key={item + (ballot.choices?.[idx] ?? "") + idx}
399409
className={
@@ -422,7 +432,7 @@ function BallotOverviewTable({
422432
});
423433
await refetchBallots();
424434
onBallotChanged?.();
425-
} catch (e) {}
435+
} catch (error: unknown) {}
426436
setUpdatingIdx(null);
427437
}}
428438
disabled={updatingIdx === idx}

src/components/pages/wallet/governance/proposal/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export default function WalletGovernanceProposal({
113113
{appWallet && (
114114
<BallotCard
115115
appWallet={appWallet}
116+
utxos={manualUtxos}
116117
onSelectBallot={(ballotId) => setSelectedBallotId(ballotId)}
117118
/>
118119
)}

src/components/pages/wallet/governance/proposals.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ export default function AllProposals({ appWallet, utxos, selectedBallotId, onSel
4848
.then(async (proposalsData) => {
4949
const skeletons = proposalsData.map((p: any) => ({
5050
tx_hash: p.tx_hash,
51-
cert_index: p.cert_index,
51+
cert_index: Number(p.cert_index),
5252
governance_type: p.governance_type,
53+
hash: "",
54+
url: "",
55+
bytes: "",
5356
json_metadata: {
5457
body: {
5558
title: "Loading...",
@@ -77,8 +80,11 @@ export default function AllProposals({ appWallet, utxos, selectedBallotId, onSel
7780
key: p.tx_hash + "#" + p.cert_index,
7881
data: {
7982
tx_hash: p.tx_hash,
80-
cert_index: p.cert_index,
83+
cert_index: Number(p.cert_index),
8184
governance_type: p.governance_type,
85+
hash: "",
86+
url: "",
87+
bytes: "",
8288
json_metadata: {
8389
body: {
8490
title: "Metadata could not be loaded.",
@@ -127,8 +133,11 @@ export default function AllProposals({ appWallet, utxos, selectedBallotId, onSel
127133

128134
const skeletons: ProposalMetadata[] = newProposalsData.map(p => ({
129135
tx_hash: p.tx_hash,
130-
cert_index: p.cert_index,
136+
cert_index: Number(p.cert_index),
131137
governance_type: p.governance_type,
138+
hash: "",
139+
url: "",
140+
bytes: "",
132141
json_metadata: {
133142
body: {
134143
title: "Loading...",
@@ -160,8 +169,11 @@ export default function AllProposals({ appWallet, utxos, selectedBallotId, onSel
160169
key: p.tx_hash + "#" + p.cert_index,
161170
data: {
162171
tx_hash: p.tx_hash,
163-
cert_index: p.cert_index,
172+
cert_index: Number(p.cert_index),
164173
governance_type: p.governance_type,
174+
hash: "",
175+
url: "",
176+
bytes: "",
165177
json_metadata: {
166178
body: {
167179
title: "Metadata could not be loaded.",

0 commit comments

Comments
 (0)