Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added theme changer on Agora Blockchain #90

Closed
Closed
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
25 changes: 19 additions & 6 deletions client/app/components/ChatBot/ChatBot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import React, { useState, useRef, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { ArrowUpIcon, ChatBubbleLeftRightIcon } from "@heroicons/react/24/solid";
import {
ArrowUpIcon,
ChatBubbleLeftRightIcon,
} from "@heroicons/react/24/solid";

interface Message {
content: string;
Expand All @@ -12,7 +15,8 @@ interface Message {
const ChatBot: React.FC = () => {
const [messages, setMessages] = useState([
{
content: "Greetings! I'm here to help with any blockchain questions you have",
content:
"Greetings! I'm here to help with any blockchain questions you have",
role: "assistant",
},
]);
Expand Down Expand Up @@ -54,8 +58,12 @@ const ChatBot: React.FC = () => {
};

return (
<div className="fixed bottom-4 right-4 z-50">
<motion.div initial={false} animate={isOpen ? "open" : "closed"} className="relative">
<div className="fixed bottom-4 right-4 z-50 ">
<motion.div
initial={false}
animate={isOpen ? "open" : "closed"}
className="relative"
>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
Expand Down Expand Up @@ -84,7 +92,9 @@ const ChatBot: React.FC = () => {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={`flex ${msg.role === "assistant" ? "justify-start" : "justify-end"}`}
className={`flex ${
msg.role === "assistant" ? "justify-start" : "justify-end"
}`}
>
<div
className={`max-w-[80%] p-3 rounded-lg ${
Expand All @@ -110,7 +120,10 @@ const ChatBot: React.FC = () => {
placeholder="Type your message..."
className="flex-grow px-4 py-2 focus:outline-none"
/>
<button type="submit" className="bg-blue-600 text-white p-2 rounded-full">
<button
type="submit"
className="bg-blue-600 text-white p-2 rounded-full"
>
<ArrowUpIcon className="w-5 h-5" />
</button>
</div>
Expand Down
10 changes: 6 additions & 4 deletions client/app/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { motion } from "framer-motion";
import { ThemeToggle } from "../Helper/ThemeToggle";
import {
PlusCircleIcon,
UserIcon,
Expand All @@ -23,12 +24,12 @@ const Header = () => {

return (
<motion.header
className="bg-white border-b border-gray-200 fixed w-full z-30 shadow-sm"
className="bg-white border-b border-gray-200 fixed w-full z-30 shadow-sm dark:bg-dark"
initial={{ y: -100 }}
animate={{ y: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 dark:bg-dark">
<div className="flex justify-between h-16">
<Link href="/" className="flex-shrink-0 flex items-center">
<Image
Expand All @@ -38,12 +39,12 @@ const Header = () => {
src="/aossie.png"
alt="Agora Blockchain"
/>
<h1 className="ml-3 text-xl font-bold text-gray-800 hidden sm:block">
<h1 className="ml-3 text-xl font-bold text-gray-800 hidden sm:block dark:text-white">
Agora Blockchain
</h1>
</Link>

<nav className="flex items-center space-x-4">
<nav className="flex items-center space-x-4 dark:bg-dark">
{menuItems.map((item) => (
<Link key={item.name} href={item.href} className="relative">
<motion.button
Expand All @@ -70,6 +71,7 @@ const Header = () => {
</Link>
))}
<Web3Connect />
<ThemeToggle />
</nav>
</div>
</div>
Expand Down
51 changes: 51 additions & 0 deletions client/app/components/Helper/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";

type ThemeContextType = {
darkMode: boolean;
toggleTheme: () => void;
};

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export const ThemeProvider = ({ children }: { children: ReactNode }) => {
const [darkMode, setDarkMode] = useState(() => {
if (typeof window !== "undefined") {
return localStorage.getItem("theme") === "dark";
}
return false; // Default
});

useEffect(() => {
if (darkMode) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
} else {
document.documentElement.classList.remove("dark");
localStorage.setItem("theme", "light");
}
}, [darkMode]);

const toggleTheme = () => setDarkMode((prev) => !prev);

return (
<ThemeContext.Provider value={{ darkMode, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};

export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};
21 changes: 21 additions & 0 deletions client/app/components/Helper/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useTheme } from "../Helper/ThemeProvider"; // Import useTheme hook
import { FaMoon } from "react-icons/fa";
import { BsSunFill } from "react-icons/bs";

export const ThemeToggle = () => {
const { darkMode, toggleTheme } = useTheme(); // Use theme context

return (
<div
className="relative w-16 h-8 flex items-center dark:bg-gray-100 bg-teal-500 cursor-pointer rounded-full p-1"
onClick={toggleTheme}
>
<FaMoon className="text-white size={18}" />
<div
className="absolute bg-white w-6 h-6 rounded-full shadow-md transform transition-transform duration-300"
style={darkMode ? { left: "2px" } : { right: "2px" }}
></div>
<BsSunFill className="ml-auto text-yellow-400" size={18}></BsSunFill>
</div>
);
};
2 changes: 1 addition & 1 deletion client/app/components/Pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Dashboard from "./Dashboard";
const HomePage = () => {
const { isConnected } = useAccount();
return (
<main className="h-screen pt-20 w-full bg-white ">
<main className="h-screen pt-20 w-full bg-white dark:bg-dark ">
{isConnected ? <Dashboard /> : <LoginPage />}
</main>
);
Expand Down
56 changes: 36 additions & 20 deletions client/app/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,33 +75,38 @@ const CreatePage: React.FC = () => {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="min-h-screen w-full bg-gradient-to-br from-gray-100 to-gray-200 flex items-center justify-center p-4"
className="min-h-screen w-full bg-gradient-to-br from-gray-100 to-gray-200 flex items-center justify-center p-4 dark:bg-dark"
>
<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 mt-12"
className="w-full max-w-2xl bg-white rounded-3xl shadow-2xl p-8 space-y-8 mt-12 dark:bg-dark"
>
<h2 className="text-3xl font-extrabold text-gray-800 mb-6 text-center">
<h2 className="text-3xl font-extrabold text-gray-800 mb-6 text-center dark:text-white">
Create New Election
</h2>
<form onSubmit={createElection} className="space-y-6">
<form
onSubmit={createElection}
className="space-y-6 dark:bg-dark dark:text-white"
>
<InputField
name="name"
label="Election Name"
placeholder="Enter election name"
className="dark:text-white"
/>
<TextareaField
name="description"
label="Description"
placeholder="Describe the election"
className="dark:text-white"
/>
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">
<div className="space-y-2 dark:bg-dark">
<label className="block text-sm font-medium text-gray-700 dark:text-white">
Voting Type
</label>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-2 dark:text-white">
<select
value={selectedBallot}
onChange={handleBallotChange}
Expand All @@ -116,7 +121,7 @@ const CreatePage: React.FC = () => {
<ElectionInfoPopup id={selectedBallot} />
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-2 gap-4 dark:bg-dark">
<DatePickerField
value={startTime}
onChange={(value) => setStartTime(value)}
Expand All @@ -130,7 +135,7 @@ const CreatePage: React.FC = () => {
</div>
<motion.button
type="submit"
className="w-full py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
className="w-full py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-dark"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
Expand All @@ -148,27 +153,32 @@ interface InputFieldProps {
name: string;
label: string;
placeholder: string;
className?: string;
}

const InputField: React.FC<InputFieldProps> = ({
name,
label,
placeholder,
className,
}) => (
<motion.div
className="space-y-1"
className={`space-y-1 ${className}`}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: 0.3, duration: 0.5 }}
>
<label htmlFor={name} className="block text-sm font-medium text-gray-700">
<label
htmlFor={name}
className="block text-sm font-medium text-gray-700 dark:text-white"
>
{label}
</label>
<input
type="text"
name={name}
id={name}
className="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
className={`block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm ${className}`}
placeholder={placeholder}
required
/>
Expand All @@ -179,21 +189,25 @@ const TextareaField: React.FC<InputFieldProps> = ({
name,
label,
placeholder,
className,
}) => (
<motion.div
className="space-y-1"
className={`space-y-1 ${className}`}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: 0.4, duration: 0.5 }}
>
<label htmlFor={name} className="block text-sm font-medium text-gray-700">
<label
htmlFor={name}
className="block text-sm font-medium text-gray-700 dark:text-white"
>
{label}
</label>
<textarea
name={name}
id={name}
rows={4}
className="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
className={`block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm ${className}`}
placeholder={placeholder}
required
></textarea>
Expand All @@ -212,7 +226,9 @@ const DatePickerField: React.FC<DatePickerFieldProps> = ({
label,
}) => (
<div className="space-y-1">
<label className="block text-sm font-medium text-gray-700">{label}</label>
<label className="block text-sm font-medium text-gray-700 dark:text-white">
{label}
</label>
<DatePicker
value={value}
placement="topStart"
Expand Down Expand Up @@ -243,14 +259,14 @@ const ChainSwitchModal: React.FC<ChainSwitchModalProps> = ({ onSwitch }) => (
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
className="bg-white rounded-lg p-8 shadow-xl text-center"
className="bg-white rounded-lg p-8 shadow-xl text-center dark:bg-dark"
>
<p className="text-xl mb-4 text-gray-800">
<p className="text-xl mb-4 text-gray-800 dark:text-white">
Creating Elections is supported only on Sepolia
</p>
<motion.button
onClick={onSwitch}
className="inline-flex items-center px-4 py-2 bg-gradient-to-r from-indigo-500 to-purple-600 text-white font-semibold rounded-lg shadow-md hover:from-indigo-600 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-75"
className="inline-flex items-center px-4 py-2 bg-gradient-to-r from-indigo-500 to-purple-600 text-white font-semibold rounded-lg shadow-md hover:from-indigo-600 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-75 dark:bg-dark"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Expand All @@ -261,4 +277,4 @@ const ChainSwitchModal: React.FC<ChainSwitchModalProps> = ({ onSwitch }) => (
</motion.div>
);

export default CreatePage;
export default CreatePage;
17 changes: 8 additions & 9 deletions client/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,24 @@ body {
background-color: #ffffff;
}


::-webkit-scrollbar {
width: 1px;
height: 1px;
width: 1px;
height: 1px;
}

/* Scrollbar track */
::-webkit-scrollbar-track {
background: #f0f0f0;
border-radius: 4px;
border-radius: 4px;
}

/* Scrollbar thumb */
::-webkit-scrollbar-thumb {
background-color: #cfcaca;
border-radius: 1px;
border: 2px solid #f0f0f0;
background-color: #cfcaca;
border-radius: 1px;
border: 2px solid #f0f0f0;
}

::-webkit-scrollbar-thumb:hover {
background-color: #ebe4e4;
}
background-color: #ebe4e4;
}
Loading