Skip to content
Merged
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: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import AdminElectionsPage from '@/pages/admin/Election';
import ElectionDetailPage from '@/components/shared/election/page';
import VotePage from '@/pages/election/VotePage';
import ElectionResultsPage from '@/pages/election/ResultPage';
import ElectionDetails from '@/pages/election/ElectionPage';

function App() {
const auth = React.useContext(AuthContext);
Expand Down Expand Up @@ -85,6 +86,7 @@ function App() {
</Route>

<Route path="/admin" element={<AdminLayout />}>
<Route path="elections/:id" element={<ElectionDetails />} />
<Route path="elections" element={<AdminElectionsPage />} />
<Route path="create-election" element={<CreateElection />} />
<Route index element={<div>admin dashboard</div>} />
Expand Down
4 changes: 2 additions & 2 deletions src/components/shared/admin/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ const sidebarItems = [
export function AdminSidebar() {
const location = useLocation();
return (
<div className="hidden md:block ">
<div className="space-y-4 py-4">
<div className="hidden md:block border-r-1 ">
<div className="space-y-4 py-6">
<div className="px-3 py-2">
<h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">Admin Panel</h2>
<div className="space-y-1">
Expand Down
80 changes: 75 additions & 5 deletions src/pages/CreateElection.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type React from 'react';
import { useState } from 'react';
import { useContext, useState } from 'react';
import { Link } from 'react-router';
import { Info, Plus, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
Expand All @@ -24,12 +24,14 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { toast } from 'sonner';
import AuthContext from '@/context/AuthContext';

export default function CreateElectionPage() {
const [purpose, setPurpose] = useState('');

const [candidates, setCandidates] = useState([{ id: 1, name: '', slogan: '' }]);
const [successDialogOpen, setSuccessDialogOpen] = useState(false);
const { state } = useContext(AuthContext);

const addCandidate = () => {
setCandidates([...candidates, { id: candidates.length + 1, name: '', slogan: '' }]);
Expand All @@ -49,9 +51,75 @@ export default function CreateElectionPage() {
);
};

const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSuccessDialogOpen(true);

if (candidates.length < 2) {
toast.error('You need to have at least 2 candidates');
return;
}

if (!state.is_admin) {
toast.error('You do not have permission to create an election');
return;
}

let totalElections = 0;
let is_operation_successful = false;

const createElectionPromise = new Promise((resolve, reject) => {
(async () => {
try {
totalElections = await state.instance!.noOfElections();

for (let i = 1; i <= totalElections; i++) {
const electionData = await state.instance!.getElection(i);
if (
electionData.purpose &&
electionData.purpose.toLowerCase() === purpose.toLowerCase()
) {
throw new Error('This election is already created');
}
}

const tx = await state.instance!.createElection(purpose);
await tx.wait();

totalElections = await state.instance!.noOfElections();

for (const candidate of candidates) {
console.log('Adding candidate:', candidate);
const tx = await state.instance!.addCandidate(
candidate.name,
candidate.slogan,
totalElections
);
await tx.wait();
}
is_operation_successful = true;
resolve('Election created successfully');
} catch (error) {
reject(error);
}
})();
});

toast.promise(createElectionPromise, {
loading: 'Creating election...',
success: 'Election created successfully',
error: (error) => {
if (error.message.includes('user rejected transaction')) {
return 'Error: User rejected the transaction';
}
return `Error while creating the election`;
},
});

createElectionPromise.then(() => {
if (is_operation_successful) {
setSuccessDialogOpen(true);
}
});
};

return (
Expand Down Expand Up @@ -148,7 +216,9 @@ export default function CreateElectionPage() {
<Button variant="outline" asChild>
<Link to="/admin/elections">Cancel</Link>
</Button>
<Button type="submit">Create Election</Button>
<Button type="submit" className="cursor-pointer">
Create Election
</Button>
</CardFooter>
</Card>
</form>
Expand Down
4 changes: 2 additions & 2 deletions src/pages/admin/AdminLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { AdminSidebar } from '@/components/shared/admin/sidebar';

const AdminLayout = () => {
return (
<section className="flex-1 container grid gap-12 md:grid-cols-[200px_1fr] lg:grid-cols-[250px_1fr] px-4 py-6 md:px-6 md:py-8">
<section className="flex-1 container grid gap-12 md:grid-cols-[200px_1fr] lg:grid-cols-[250px_1fr] px-4 md:px-3 md:py-0">
<AdminSidebar />
<main>
<main className='min-h-screen'>
<Outlet />
</main>
</section>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/admin/Election.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default function AdminElectionsPage() {
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<h1 className="text-3xl font-bold tracking-tight">Elections</h1>
<Button asChild>
<Link to="/elections/create">
<Link to="/admin/create-election">
<Plus className="mr-2 h-4 w-4" />
Create Election
</Link>
Expand Down
Loading
Loading