import React, { useState, useEffect, useCallback } from 'react'; import { ethers } from 'ethers'; import { connectWallet as connectWalletUtil } from './utils/contract'; import Header from './components/Header'; import ConnectWallet from './components/ConnectWallet'; import WorkflowStatus from './components/WorkflowStatus'; import VoterManagement from './components/VoterManagement'; import ProposalManagement from './components/ProposalManagement'; import VotingSession from './components/VotingSession'; function App() { const [account, setAccount] = useState(null); const [contract, setContract] = useState(null); const [isOwner, setIsOwner] = useState(false); const [isVoter, setIsVoter] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [currentStatus, setCurrentStatus] = useState(0); const [proposals, setProposals] = useState([]); const [voterInfo, setVoterInfo] = useState({ isRegistered: false, hasVoted: false, votedProposalId: 0 }); // Connexion au portefeuille Ethereum const connectWalletHandler = async () => { try { setLoading(true); setError(null); const { accounts, contract: votingContract } = await connectWalletUtil(); setAccount(accounts[0]); setContract(votingContract); // Vérification si l'utilisateur est le propriétaire du contrat const contractOwner = await votingContract.owner(); setIsOwner(accounts[0].toLowerCase() === contractOwner.toLowerCase()); // Récupération du statut actuel du workflow const status = await votingContract.workflowStatus(); setCurrentStatus(status.toString()); // Vérification si l'utilisateur est un électeur inscrit try { const voter = await votingContract.getVoter(accounts[0]); setIsVoter(voter.isRegistered); setVoterInfo({ isRegistered: voter.isRegistered, hasVoted: voter.hasVoted, votedProposalId: voter.votedProposalId.toString() }); } catch (error) { // Si l'utilisateur n'est pas un électeur, une erreur est générée setIsVoter(false); } setLoading(false); } catch (error) { console.error("Erreur lors de la connexion:", error); setError("Impossible de se connecter au portefeuille. Assurez-vous que MetaMask est installé et connecté."); setLoading(false); } }; // Mise à jour du statut du workflow const updateWorkflowStatus = useCallback(async () => { if (!contract) return; try { const status = await contract.workflowStatus(); setCurrentStatus(status.toString()); } catch (error) { console.error("Erreur lors de la récupération du statut:", error); } }, [contract]); // Actions liées au workflow const handleWorkflowAction = async (action, duration = 60) => { if (!contract) return; try { setLoading(true); let tx; switch (action) { case 'startProposalsRegistration': tx = await contract.startProposalsRegistration(duration); break; case 'endProposalsRegistration': tx = await contract.endProposalsRegistration(); break; case 'startVotingSession': tx = await contract.startVotingSession(duration); break; case 'endVotingSession': tx = await contract.endVotingSession(); break; case 'tallyVotes': tx = await contract.tallyVotes(); break; default: throw new Error("Action non reconnue"); } await tx.wait(); await updateWorkflowStatus(); // Si nous passons à la phase de vote ou de résultats, récupérons les propositions if (action === 'startVotingSession' || action === 'tallyVotes') { await fetchProposals(); } setLoading(false); } catch (error) { console.error(`Erreur lors de l'action ${action}:`, error); setError(`Erreur lors de l'action: ${error.message}`); setLoading(false); } }; // Ajout d'un électeur const addVoter = async (voterAddress) => { if (!contract) return; try { setLoading(true); const tx = await contract.registerVoter(voterAddress); await tx.wait(); setLoading(false); } catch (error) { console.error("Erreur lors de l'ajout de l'électeur:", error); setError(`Erreur: ${error.message}`); setLoading(false); } }; // Récupération des informations de l'électeur const fetchVoterInfo = useCallback(async () => { if (!contract || !account || !isVoter) return; try { const voter = await contract.getVoter(account); setVoterInfo({ isRegistered: voter.isRegistered, hasVoted: voter.hasVoted, votedProposalId: voter.votedProposalId.toString() }); } catch (error) { console.error("Erreur lors de la récupération des infos de l'électeur:", error); } }, [contract, account, isVoter]); // Soumission d'une proposition const submitProposal = async (description) => { if (!contract) return; try { setLoading(true); const tx = await contract.registerProposal(description); await tx.wait(); await fetchProposals(); setLoading(false); } catch (error) { console.error("Erreur lors de la soumission de la proposition:", error); setError(`Erreur: ${error.message}`); setLoading(false); } }; // Récupération des propositions const fetchProposals = useCallback(async () => { if (!contract || !isVoter) return; try { const proposalsCount = await contract.getProposalsCount(); const proposalsList = []; for (let i = 0; i < proposalsCount; i++) { const proposal = await contract.getProposal(i); proposalsList.push({ description: proposal.description, voteCount: proposal.voteCount.toString() }); } setProposals(proposalsList); } catch (error) { console.error("Erreur lors de la récupération des propositions:", error); } }, [contract, isVoter]); // Soumission d'un vote const submitVote = async (proposalId) => { if (!contract) return; try { setLoading(true); const tx = await contract.vote(proposalId); await tx.wait(); await fetchVoterInfo(); setLoading(false); } catch (error) { console.error("Erreur lors du vote:", error); setError(`Erreur: ${error.message}`); setLoading(false); } }; // Délégation de vote const delegateVote = async (delegateAddress) => { if (!contract) return; try { setLoading(true); const tx = await contract.delegateVoteTo(delegateAddress); await tx.wait(); await fetchVoterInfo(); setLoading(false); } catch (error) { console.error("Erreur lors de la délégation du vote:", error); setError(`Erreur: ${error.message}`); setLoading(false); } }; // Détection des changements de compte useEffect(() => { if (window.ethereum) { window.ethereum.on('accountsChanged', (accounts) => { window.location.reload(); }); } }, []); // Mise à jour automatique des données useEffect(() => { const interval = setInterval(() => { if (contract) { updateWorkflowStatus(); if (isVoter) { fetchVoterInfo(); fetchProposals(); } } }, 10000); // Mettre à jour toutes les 10 secondes return () => clearInterval(interval); }, [contract, isVoter, updateWorkflowStatus, fetchVoterInfo, fetchProposals]); return ( <div className="App"> <Header account={account} isOwner={isOwner} /> <div className="container"> {error && ( <div className="status error"> {error} <button className="close-button" onClick={() => setError(null)}>×</button> </div> )} {!account ? ( <ConnectWallet connectWallet={connectWalletHandler} loading={loading} /> ) : ( <> <WorkflowStatus status={currentStatus} isOwner={isOwner} onAction={handleWorkflowAction} loading={loading} /> <VoterManagement isOwner={isOwner} addVoter={addVoter} loading={loading} isVoter={isVoter} currentStatus={currentStatus} /> <ProposalManagement isVoter={isVoter} submitProposal={submitProposal} fetchProposals={fetchProposals} currentStatus={currentStatus} proposals={proposals} loading={loading} /> <VotingSession isVoter={isVoter} submitVote={submitVote} delegateVote={delegateVote} fetchVoterInfo={fetchVoterInfo} fetchProposals={fetchProposals} currentStatus={currentStatus} proposals={proposals} loading={loading} voterInfo={voterInfo} /> </> )} </div> </div> ); } export default App;