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

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
"@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-select": "^2.1.6",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.3",
"@tailwindcss/vite": "^4.0.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand Down
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Layout from '@/pages/layout';
import CandidatePage from '@/pages/AddCandidatePage';
import CreateElection from '@/pages/CreateElection';
import HomePage from '@/pages/Home';
import AdminLayout from '@/pages/admin/AdminLayout';
import AdminVerifyVotersPage from './pages/admin/Verify-voters';

function App() {
const auth = React.useContext(AuthContext);
Expand Down Expand Up @@ -71,6 +73,9 @@ function App() {
<Route path="upcoming" element={<div>upcoming election</div>} />
<Route path="active" element={<div>active election</div>} />
</Route>
<Route path="/admin" element={<AdminLayout />}>
<Route path="verify-voters" element={<AdminVerifyVotersPage />} />
</Route>
<Route path="/candidate/add" element={<CandidatePage />} />
<Route path="*" element={<div>not found return 404</div>} />
</Route>
Expand Down
78 changes: 78 additions & 0 deletions src/components/shared/admin/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Link } from 'react-router';
import { BarChart, Calendar, FileText, Home, Settings, Shield, Users, Vote } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

const sidebarItems = [
{
title: 'Dashboard',
href: '/admin',
icon: Home,
},
{
title: 'Elections',
href: '/admin/elections',
icon: Vote,
},
{
title: 'Voters',
href: '/admin/verify-voters',
icon: Users,
},
{
title: 'Results',
href: '/admin/results',
icon: BarChart,
},
{
title: 'Reports',
href: '/admin/reports',
icon: FileText,
},
{
title: 'Calendar',
href: '/admin/calendar',
icon: Calendar,
},
{
title: 'Security',
href: '/admin/security',
icon: Shield,
},
{
title: 'Settings',
href: '/admin/settings',
icon: Settings,
},
];

export function AdminSidebar() {
return (
<div className="hidden md:block">
<div className="space-y-4 py-4">
<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">
{sidebarItems.map((item) => (
<Button
key={item.href}
variant="ghost"
className={cn(
'w-full justify-start',
item.href === '/admin/verify-voters' && 'bg-accent text-accent-foreground'
)}
asChild
>
<Link to={item.href}>
<item.icon className="mr-2 h-4 w-4" />
{item.title}
</Link>
</Button>
))}
</div>
</div>
</div>
</div>
);
}
133 changes: 133 additions & 0 deletions src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"

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

function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
}

function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}

function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}

function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}

function DialogOverlay({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
className
)}
{...props}
/>
)
}

function DialogContent({
className,
children,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
)
}

function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
)
}

function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
)
}

function DialogTitle({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
)
}

function DialogDescription({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}

export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
}
Loading
Loading