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
38 changes: 38 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-navigation-menu": "^1.2.5",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-slot": "^1.1.2",
"@tailwindcss/vite": "^4.0.9",
"class-variance-authority": "^0.7.1",
Expand Down
Binary file added public/images/photo1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/photo8.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import AuthContext from './context/AuthContext';
import connectToBlockchain from './config';
import Layout from '@/pages/layout';
import CandidatePage from './pages/AddCandidatePage';

function App() {
const auth = React.useContext(AuthContext);
Expand Down Expand Up @@ -66,6 +67,7 @@ function App() {
<Route path="create" element={<div>create election</div>} />
<Route path="active" element={<div>active election</div>} />
</Route>
<Route path="/candidate/add" element={<CandidatePage />} />
<Route path="*" element={<div>not found return 404</div>} />
</Route>
</Routes>
Expand Down
54 changes: 12 additions & 42 deletions src/components/shared/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type React from 'react';
import React from 'react';
import { useState } from 'react';
import { Link } from 'react-router';
import { Vote, Menu, User, LogOut } from 'lucide-react';
import { Vote, Menu } from 'lucide-react';

import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
Expand All @@ -15,16 +15,11 @@ import {
navigationMenuTriggerStyle,
} from '@/components/ui/navigation-menu';
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { getCurrentConnection } from '@/config';

export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const is_connected = !!getCurrentConnection();

return (
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 px-2">
Expand Down Expand Up @@ -74,8 +69,8 @@ export default function Navbar() {
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink href="/how-it-works" className={navigationMenuTriggerStyle()}>
How It Works
<NavigationMenuLink href="/voter/add" className={navigationMenuTriggerStyle()}>
Register as voter
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
Expand All @@ -87,37 +82,12 @@ export default function Navbar() {
</NavigationMenu>

<div className="flex items-center gap-4">
{/* User Account Dropdown */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="hidden md:flex">
<User className="h-5 w-5" />
<span className="sr-only">User account</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>
<Link to="/profile" className="flex w-full">
My Profile
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Link to="/my-votes" className="flex w-full">
My Votes
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Link to="/logout" className="flex w-full items-center">
<LogOut className="mr-2 h-4 w-4" />
Logout
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

<Button className="hidden md:inline-flex">Connect Wallet</Button>

<Button
className="hidden md:inline-flex hover:cursor-pointer disabled:opacity-80"
disabled={is_connected}
>
{is_connected ? 'Connected to wallet' : 'Connect Wallet'}
</Button>
<Sheet open={isOpen} onOpenChange={setIsOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="md:hidden">
Expand Down
142 changes: 142 additions & 0 deletions src/components/shared/auth/Add-candidate-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import React from 'react';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
import { AddCandidateSchema } from '@/schemas';
import { zodResolver } from '@hookform/resolvers/zod';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { FormError } from '../Form-error';
import AuthContext from '@/context/AuthContext';
import { toast } from 'sonner';
import { Textarea } from '@/components/ui/textarea';

export function AddCandidateForm() {
const [error, setError] = React.useState<string | undefined>('');
const [isPending, startTransition] = React.useTransition();
const auth = React.useContext(AuthContext);

const { state } = auth;

const form = useForm<z.infer<typeof AddCandidateSchema>>({
resolver: zodResolver(AddCandidateSchema),
defaultValues: {
name: '',
slogan: '',
election_id: '',
},
});

const onSubmit = (values: z.infer<typeof AddCandidateSchema>) => {
setError('');
startTransition(async () => {
if (!state.is_admin) {
toast.error('only admin can add candidates');
return;
}

const electionid = values.election_id as unknown as number;

try {
if (state.instance !== null) {
await state.instance.methods.addCandidate(values.name, values.slogan, electionid).send({
from: state.account,
gas: 1000000,
});

toast.success('Candidate added successfully');
form.reset();
}
} catch (error) {
console.error('Error while adding candidate:', error);
toast.error('Error while adding candidate');
}
});
};

return (
<Form {...form}>
<form className={cn('flex flex-col gap-6')} onSubmit={form.handleSubmit(onSubmit)}>
<div className="flex flex-col items-center gap-2 text-center">
<h1 className="text-2xl font-bold">Add Candidate</h1>
<p className="text-sm text-muted-foreground">
Verify details before adding. Candidates cannot be removed once added.
</p>
</div>
<div className="grid gap-6">
<div className="grid gap-2">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel htmlFor="name">Name</FormLabel>
<FormControl>
<Input
{...field}
disabled={isPending}
id="name"
placeholder="atharv sawant"
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="slogan"
render={({ field }) => (
<FormItem>
<FormLabel htmlFor="name">Slogan</FormLabel>
<FormControl>
<Textarea
{...field}
disabled={isPending}
id="name"
placeholder="I will make the world a better place"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="election_id"
render={({ field }) => (
<FormItem>
<FormLabel htmlFor="name">Election Id</FormLabel>
<FormControl>
<Input
{...field}
disabled={isPending}
id="name"
placeholder="atharv sawant"
type="text"
className="overflow-scroll"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormError message={error} />
<Button type="submit" className="w-full" disabled={isPending}>
Add Candidate
</Button>
</div>
</form>
</Form>
);
}
46 changes: 46 additions & 0 deletions src/components/ui/popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"

import { cn } from "@/lib/utils"

function Popover({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot="popover" {...props} />
}

function PopoverTrigger({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}

function PopoverContent({
className,
align = "center",
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
)
}

function PopoverAnchor({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}

export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
18 changes: 18 additions & 0 deletions src/components/ui/textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from "react"

import { cn } from "@/lib/utils"

function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
<textarea
data-slot="textarea"
className={cn(
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
{...props}
/>
)
}

export { Textarea }
20 changes: 20 additions & 0 deletions src/pages/AddCandidatePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AddCandidateForm } from '@/components/shared/auth/Add-candidate-form';

export default function CandidatePage() {
return (
<div className="grid min-h-[calc(100vh-4.1rem)] lg:grid-cols-2 ">
<div className="flex flex-1 items-center justify-center">
<div className="w-full max-w-xs">
<AddCandidateForm />
</div>
</div>
<div className="relative hidden bg-muted lg:block">
<img
src="/images/photo8.jpg"
alt="Image"
className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
/>
</div>
</div>
);
}
Loading
Loading