mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-06-12 20:11:03 +00:00
Integrate live blockchain data for proposals page
- Updated ProposalsList to use useGovernance hook - Fetches treasury proposals and democracy referenda from blockchain - Displays both proposal types in unified list: - Treasury proposals: Show requested amount, beneficiary, proposer - Democracy referenda: Show aye/nay votes, voting threshold, end block - Added loading state with spinner - Added error handling with user-friendly messages - Added "Live Blockchain Data" badge - Format token amounts from blockchain units (12 decimals to HEZ) - Empty state when no proposals exist - Token symbol consistency: All amounts show as HEZ - Auto-refreshes every 30 seconds via useGovernance hook All proposals and referenda now display live blockchain data.
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FileText, Vote, Clock, TrendingUp, Users, AlertCircle } from 'lucide-react';
|
import { FileText, Vote, Clock, TrendingUp, Users, AlertCircle, Loader2, Activity } from 'lucide-react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';
|
||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/badge';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { Progress } from '../ui/progress';
|
import { Progress } from '../ui/progress';
|
||||||
|
import { Alert, AlertDescription } from '../ui/alert';
|
||||||
|
import { useGovernance } from '@/hooks/useGovernance';
|
||||||
|
import { formatNumber } from '@/lib/utils';
|
||||||
|
|
||||||
interface Proposal {
|
interface Proposal {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -21,35 +24,46 @@ interface Proposal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ProposalsList: React.FC = () => {
|
const ProposalsList: React.FC = () => {
|
||||||
const [proposals] = useState<Proposal[]>([
|
const { proposals: treasuryProposals, referenda, loading, error } = useGovernance();
|
||||||
{
|
|
||||||
id: 1,
|
// Format token amounts from blockchain units (12 decimals for HEZ)
|
||||||
title: 'Treasury Allocation for Development Fund',
|
const formatTokenAmount = (amount: string) => {
|
||||||
description: 'Allocate 500,000 PEZ for ecosystem development',
|
const value = BigInt(amount);
|
||||||
proposer: '5GrwvaEF...',
|
return formatNumber(Number(value) / 1e12, 2);
|
||||||
type: 'treasury',
|
};
|
||||||
status: 'active',
|
|
||||||
ayeVotes: 156,
|
// Convert blockchain data to UI format
|
||||||
nayVotes: 45,
|
const proposals: Proposal[] = [
|
||||||
totalVotes: 201,
|
// Treasury proposals
|
||||||
quorum: 60,
|
...treasuryProposals.map(p => ({
|
||||||
deadline: '2 days',
|
id: p.proposalIndex,
|
||||||
requestedAmount: '500,000 PEZ'
|
title: `Treasury Proposal #${p.proposalIndex}`,
|
||||||
},
|
description: `Requesting ${formatTokenAmount(p.value)} HEZ for ${p.beneficiary.substring(0, 10)}...`,
|
||||||
{
|
proposer: p.proposer,
|
||||||
id: 2,
|
type: 'treasury' as const,
|
||||||
title: 'Update Staking Parameters',
|
status: p.status as 'active' | 'passed' | 'rejected' | 'pending',
|
||||||
description: 'Increase minimum stake requirement to 1000 HEZ',
|
ayeVotes: 0, // Treasury proposals don't have votes until they become referenda
|
||||||
proposer: '5FHneW46...',
|
nayVotes: 0,
|
||||||
type: 'executive',
|
totalVotes: 0,
|
||||||
status: 'active',
|
quorum: 0,
|
||||||
ayeVotes: 89,
|
deadline: 'Pending referendum',
|
||||||
nayVotes: 112,
|
requestedAmount: `${formatTokenAmount(p.value)} HEZ`
|
||||||
totalVotes: 201,
|
})),
|
||||||
quorum: 60,
|
// Democracy referenda
|
||||||
deadline: '5 days'
|
...referenda.map(r => ({
|
||||||
}
|
id: r.index,
|
||||||
]);
|
title: `Referendum #${r.index}`,
|
||||||
|
description: `Voting on proposal with ${r.threshold} threshold`,
|
||||||
|
proposer: 'Democracy',
|
||||||
|
type: 'executive' as const,
|
||||||
|
status: r.status as 'active' | 'passed' | 'rejected' | 'pending',
|
||||||
|
ayeVotes: Number(BigInt(r.ayeVotes) / BigInt(1e12)),
|
||||||
|
nayVotes: Number(BigInt(r.nayVotes) / BigInt(1e12)),
|
||||||
|
totalVotes: Number((BigInt(r.ayeVotes) + BigInt(r.nayVotes)) / BigInt(1e12)),
|
||||||
|
quorum: 50,
|
||||||
|
deadline: `Block ${r.end}`,
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
const getStatusBadge = (status: string) => {
|
const getStatusBadge = (status: string) => {
|
||||||
switch(status) {
|
switch(status) {
|
||||||
@@ -69,9 +83,47 @@ const ProposalsList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center py-12">
|
||||||
|
<Loader2 className="h-8 w-8 animate-spin text-primary mr-3" />
|
||||||
|
<span className="text-muted-foreground">Loading proposals from blockchain...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<Alert variant="destructive">
|
||||||
|
<AlertCircle className="h-4 w-4" />
|
||||||
|
<AlertDescription>
|
||||||
|
Failed to load proposals: {error}
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{proposals.map((proposal) => {
|
{/* Live Data Badge */}
|
||||||
|
<div className="flex items-center gap-2 mb-4">
|
||||||
|
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
|
||||||
|
<Activity className="h-3 w-3 mr-1" />
|
||||||
|
Live Blockchain Data
|
||||||
|
</Badge>
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{proposals.length} active proposals & referenda
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{proposals.length === 0 ? (
|
||||||
|
<Card className="bg-gray-900/50 border-gray-800">
|
||||||
|
<CardContent className="pt-6 text-center text-gray-500">
|
||||||
|
No active proposals or referenda found on the blockchain.
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
proposals.map((proposal) => {
|
||||||
const ayePercentage = (proposal.ayeVotes / proposal.totalVotes) * 100;
|
const ayePercentage = (proposal.ayeVotes / proposal.totalVotes) * 100;
|
||||||
const nayPercentage = (proposal.nayVotes / proposal.totalVotes) * 100;
|
const nayPercentage = (proposal.nayVotes / proposal.totalVotes) * 100;
|
||||||
const quorumReached = (proposal.totalVotes / 300) * 100 >= proposal.quorum;
|
const quorumReached = (proposal.totalVotes / 300) * 100 >= proposal.quorum;
|
||||||
@@ -148,7 +200,8 @@ const ProposalsList: React.FC = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user