Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
NEXT_PUBLIC_SEPOLIA_RPC_URL = ENTER_RPC_URL
NEXT_PUBLIC_SEPOLIA_RPC_URL = http://127.0.0.1:8545
NEXT_PUBLIC_AMOY_RPC_URL = ENTER_RPC_URL
NEXT_PUBLIC_FUJI_RPC_URL = ENTER_RPC_URL
NEXT_PUBLIC_PINATA_JWT = ENTER_PINATA_API_KEY
2 changes: 1 addition & 1 deletion client/app/components/Hooks/GetElectionInformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const useElectionInformation = ({
const electionContract = {
abi: Election,
address: electionAddress,
chainId: sepolia.id,
// chainId: sepolia.id,
};
const CCIPContract = {
abi: CCIPSender,
Expand Down
4 changes: 2 additions & 2 deletions client/app/components/Hooks/GetMiniElectionInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const useMiniElectionInfo = ({
electionAddress: `0x${string}`;
}) => {
const { data: electionInfo, isLoading } = useReadContract({
chainId: sepolia.id,
// chainId: sepolia.id,
abi: Election,
address: electionAddress,
functionName: "electionInfo",
Expand All @@ -22,7 +22,7 @@ export const useMiniOwnerInfo = ({
electionAddress: `0x${string}`;
}) => {
const { data: owner, isLoading: loadingOwner } = useReadContract({
chainId: sepolia.id,
// chainId: sepolia.id,
abi: Election,
address: electionAddress,
functionName: "owner",
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/Hooks/GetOpenElections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useReadContract } from "wagmi";

export const useOpenElection = () => {
const { data: elections, isLoading } = useReadContract({
chainId: sepolia.id,
// chainId: sepolia.id,
abi: ElectionFactory,
address: ELECTION_FACTORY_ADDRESS,
functionName: "getOpenElections",
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/Hooks/GetUserElections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useReadContract } from "wagmi";

export const useUserElections = () => {
const { data: elections, isLoading } = useReadContract({
chainId: sepolia.id,
// chainId: sepolia.id,
abi: ElectionFactory,
address: ELECTION_FACTORY_ADDRESS,
functionName: "getOpenElections",
Expand Down
12 changes: 6 additions & 6 deletions client/app/constants.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// export const ELECTION_FACTORY_ADDRESS =
// "0x64c720eBD5227bba57a5E8282893e71087cCcBb8";
export const ELECTION_FACTORY_ADDRESS =
"0x2F432d25CceD9442882188801F47c596Eaaf176d";
// export const CCIP_FUJI_ADDRESS = "0xf267f0603672E791779E52B153BD9A39C9928767";
export const ELECTION_FACTORY_ADDRESS =
process.env.NODE_ENV === "development"
? "0x5FbDB2315678afecb367f032d93F642f64180aa3" // Local Hardhat Address
: "0x64c720eBD5227bba57a5E8282893e71087cCcBb8"; // Production Sepolia Address

export const CCIP_FUJI_ADDRESS = "0xca2d3fc837b349caf17cbd965856fc33120b1c0f";
export const SEPOLIA_CHAIN_SELECTOR = BigInt("16015286601757825753");
export const LINK_FUJI = "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846";
Expand All @@ -11,4 +11,4 @@ export const AVATARS = [
"/avatar1.png",
"/avatar2.png",
"/avatar3.png",
];
];
67 changes: 40 additions & 27 deletions client/app/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { DatePicker } from "rsuite";
import toast, { Toaster } from "react-hot-toast";
import { ErrorMessage } from "../helpers/ErrorMessage";
import { CalendarIcon } from "@heroicons/react/24/outline";
import { sepolia } from "viem/chains";
import { ArrowPathIcon , PlusIcon, TrashIcon} from "@heroicons/react/24/solid";
import { sepolia, hardhat } from "viem/chains";
import { ArrowPathIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/solid";
import { useRouter } from "next/navigation";
import ElectionInfoPopup from "../components/Modal/ElectionInfoPopup";

Expand All @@ -20,25 +20,28 @@ const CreatePage: React.FC = () => {
const { switchChain } = useSwitchChain();
const { chain } = useAccount();
const { writeContractAsync } = useWriteContract();
const isDevelopment = process.env.NODE_ENV === 'development';
const [startTime, setStartTime] = useState<Date | null>(new Date());
const [endTime, setEndTime] = useState<Date | null>(new Date());
const [candidates,setCandidates] = useState<Candidate[]>([])
const [candidates, setCandidates] = useState<Candidate[]>([])
const changeChain = () => {
switchChain({ chainId: sepolia.id });
}
switchChain({
chainId: process.env.NODE_ENV === 'development' ? hardhat.id : sepolia.id
});
}
interface Candidate {
name: string;
description: string;
}
const addCandidate = () => {
setCandidates([...candidates, { name: "", description: "" }]);
};

const removeCandidate = (index: number) => {
const newCandidates = candidates.filter((_, i) => i !== index);
setCandidates(newCandidates);
};

const updateCandidate = (index: number, field: keyof Candidate, value: string) => {
const newCandidates = candidates.map((candidate, i) => {
if (i === index) {
Expand All @@ -55,10 +58,10 @@ const CreatePage: React.FC = () => {
const description = formData.get("description") as string;
const ballotType = BigInt(selectedBallot);

if (candidates.length<2){
if (candidates.length < 2) {
toast.error("At least 2 candidates are required!");
return;

}

if (!startTime || !endTime) {
Expand All @@ -73,19 +76,29 @@ const CreatePage: React.FC = () => {
toast.error("Invalid timing. End time must be after start time.");
return;
}
if(candidates.length>0 && candidates.some(candidate=>!candidate.name || !candidate.description)){
const normalizedCandidates = candidates.map((candidate) => ({
...candidate,
name: candidate.name.trim(),
description: candidate.description.trim(),
}));

if (normalizedCandidates.some(candidate => !candidate.name || !candidate.description)) {
toast.error("please enter all candidate information or remove empty candidates.")
return
return
}
// passed candidates to the create election function
// passed candidates to the create election function
try {
await writeContractAsync({
address: ELECTION_FACTORY_ADDRESS,
abi: ElectionFactory,
functionName: "createElection",
args: [
{ startTime: start, endTime: end, name, description }, // ElectionInfo object
candidates.map((c, index) => ({ candidateID: BigInt(index), name: c.name, description: c.description })),
normalizedCandidates.map((candidate, index) => ({
candidateID: BigInt(index),
name: candidate.name,
description: candidate.description,
})),
ballotType,
ballotType,
],
Expand All @@ -104,17 +117,17 @@ const CreatePage: React.FC = () => {

return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="h-screen w-full bg-gradient-to-br pt-[50px] from-gray-100 to-gray-200 flex flex-col items-center justify-start p-4 overflow-y-auto"
>
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.2, duration: 0.5 }}
className="w-full max-w-2xl bg-white rounded-3xl shadow-2xl p-8 space-y-8 my-12"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="h-screen w-full bg-gradient-to-br pt-[50px] from-gray-100 to-gray-200 flex flex-col items-center justify-start p-4 overflow-y-auto"
>
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.2, duration: 0.5 }}
className="w-full max-w-2xl bg-white rounded-3xl shadow-2xl p-8 space-y-8 my-12"
>
<h2 className="text-3xl font-extrabold text-gray-800 mb-6 text-center">
Create New Election
</h2>
Expand All @@ -129,7 +142,7 @@ const CreatePage: React.FC = () => {
label="Description"
placeholder="Describe the election"
/>
{/* candidate section shows placeholder if empty candidate and allows to add cnadidates*/ }
{/* candidate section shows placeholder if empty candidate and allows to add cnadidates*/}
<div className="space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium text-gray-900">Candidates</h3>
Expand All @@ -144,7 +157,7 @@ const CreatePage: React.FC = () => {
Add Candidate
</motion.button>
</div>

{candidates.length === 0 ? (
<p className="text-gray-500 text-sm italic text-center py-4">
No candidates added yet. Click "Add Candidate" to begin adding candidates.
Expand Down Expand Up @@ -233,7 +246,7 @@ const CreatePage: React.FC = () => {
</motion.button>
</form>
</motion.div>
{chain?.id !== sepolia.id && <ChainSwitchModal onSwitch={changeChain} />}
{chain?.id !== sepolia.id && (!isDevelopment || chain?.id !== hardhat.id) && <ChainSwitchModal onSwitch={changeChain} />}
<Toaster />
</motion.div>
);
Expand Down Expand Up @@ -341,7 +354,7 @@ const ChainSwitchModal: React.FC<ChainSwitchModalProps> = ({ onSwitch }) => (
className="bg-white rounded-lg p-8 shadow-xl text-center"
>
<p className="text-xl mb-4 text-gray-800">
Creating Elections is supported only on Sepolia
Creating Elections is supported only on Sepolia (or Hardhat in development).
</p>
<motion.button
onClick={onSwitch}
Expand Down
14 changes: 11 additions & 3 deletions client/app/helpers/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@
import { getDefaultConfig } from "@rainbow-me/rainbowkit";
import { QueryClient } from "@tanstack/react-query";
import { http } from "wagmi";
import { sepolia, polygonAmoy, avalancheFuji } from "wagmi/chains";
// 1. We added 'hardhat' to the imports here
import { sepolia, polygonAmoy, avalancheFuji, hardhat } from "wagmi/chains";

export const config = getDefaultConfig({
appName: "Agora-Blockchain",
projectId: "8501447cf73c4e68061f7ed912d6a8ee",
chains: [sepolia, avalancheFuji],
chains: [
...(process.env.NODE_ENV === 'development' ? [hardhat] : []),
sepolia,
avalancheFuji,
],
ssr: true,
transports: {
...(process.env.NODE_ENV === 'development' && {
[hardhat.id]: http('http://127.0.0.1:8545'),
}),
[sepolia.id]: http(process.env.NEXT_PUBLIC_SEPOLIA_RPC_URL),
[polygonAmoy.id]: http(process.env.NEXT_PUBLIC_AMOY_RPC_URL),
[avalancheFuji.id]: http(process.env.NEXT_PUBLIC_FUJI_RPC_URL),
},
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export const queryClient = new QueryClient();
export const queryClient = new QueryClient();
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@emotion/is-prop-valid": "^1.4.0",
"@headlessui/react": "^2.1.0",
"@material-tailwind/react": "^2.1.9",
"@nextui-org/theme": "^2.2.6",
Expand Down