diff --git a/src/App.css b/src/App.css index f1d8c73cdcf9eaacb01fec99963ad78d591305ae..c79a8641c475f78a94c544e4919068de8c37e884 100644 --- a/src/App.css +++ b/src/App.css @@ -1 +1,2 @@ @import "tailwindcss"; +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/src/App.jsx b/src/App.jsx index 82823910a46581ede9f7806d2067b8ca68573079..3835c81e8e3427d01fdd24ed8162de52e8aff0ce 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,25 +5,28 @@ import Home from "@pages/Home"; import "./App.css"; import Todolist from "@pages/Todolist"; import { TasksProvider } from "@context/TasksContext"; +import { ThemeProvider } from "@context/ThemeContext"; import Navbar from "@components/Navbar.jsx"; function App() { return ( - <TasksProvider> - <Router> - <div className="min-h-screen bg-gray-50"> + <ThemeProvider> + <TasksProvider> + <Router> + <div className="min-h-screen bg-gray-50 dark:bg-gray-900"> <Navbar /> <main className="pt-4"> - <Routes> - <Route path="/signin" element={<SignIn />} /> - <Route path="/signup" element={<SignUp />} /> - <Route path="/" element={<Home />} /> - <Route path="/todolist" element={<Todolist />} /> - </Routes> + <Routes> + <Route path="/signin" element={<SignIn />} /> + <Route path="/signup" element={<SignUp />} /> + <Route path="/" element={<Home />} /> + <Route path="/todolist" element={<Todolist />} /> + </Routes> </main> - </div> - </Router> - </TasksProvider> + </div> + </Router> + </TasksProvider> + </ThemeProvider> ); } diff --git a/src/components/Calendar.jsx b/src/components/Calendar.jsx index d3cb662abf9ef553020978b99b3f3a88dc4cb528..a1dfae4aa06d1d78f26aa6305d5bf6b4b95eea9b 100644 --- a/src/components/Calendar.jsx +++ b/src/components/Calendar.jsx @@ -19,24 +19,24 @@ const Calendar = () => { return ( <div className="space-y-6"> - <div className="bg-violet-100 rounded-2xl p-6"> + <div className="bg-violet-100 dark:bg-gray-800 rounded-2xl p-6 shadow-lg transition-all"> <div className="flex items-center justify-between mb-4"> - <h2 className="text-lg font-semibold text-violet-900"> + <h2 className="text-lg font-semibold text-violet-900 dark:text-white"> {format(currentDate, 'MMMM yyyy', { locale: enUS })} </h2> <div className="flex gap-2"> - <button onClick={prevMonth} className="p-1 hover:bg-violet-200 rounded"> - <ChevronLeft className="w-5 h-5 text-violet-700" /> + <button onClick={prevMonth} className="p-1 hover:bg-violet-200 dark:hover:bg-gray-700 rounded transition"> + <ChevronLeft className="w-5 h-5 text-violet-700 dark:text-white" /> </button> - <button onClick={nextMonth} className="p-1 hover:bg-violet-200 rounded"> - <ChevronRight className="w-5 h-5 text-violet-700" /> + <button onClick={nextMonth} className="p-1 hover:bg-violet-200 dark:hover:bg-gray-700 rounded transition"> + <ChevronRight className="w-5 h-5 text-violet-700 dark:text-white" /> </button> </div> </div> <div className="grid grid-cols-7 gap-2 text-center mb-2"> {['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => ( - <div key={day} className="text-sm font-medium text-violet-800"> + <div key={day} className="text-sm font-medium text-violet-800 dark:text-gray-300"> {day} </div> ))} @@ -55,10 +55,10 @@ const Calendar = () => { key={day.toString()} onClick={() => setSelectedDate(day)} className={` - p-2 rounded-full text-sm + p-2 rounded-full text-sm transition ${isSelected - ? 'bg-violet-500 text-white' - : 'text-violet-800 hover:bg-violet-100' + ? 'bg-violet-500 text-white dark:bg-violet-400' + : 'text-violet-800 dark:text-gray-300 hover:bg-violet-100 dark:hover:bg-gray-700' } `} > diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index c5221de5db78b9e1bdb599b22378fdecfe328d2d..dbde8467cb8e4e6aa76891c8f7b50871a78a4e48 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,10 +1,12 @@ import React, { useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; -import { CheckCircle } from "lucide-react"; +import { CheckCircle, Sun, Moon } from "lucide-react"; +import { useTheme } from "@context/ThemeContext"; const Navbar = () => { const [isAuthenticated, setIsAuthenticated] = useState(!!localStorage.getItem("user")); const navigate = useNavigate(); + const { theme, toggleTheme } = useTheme(); useEffect(() => { // Écouter les changements sur `localStorage` @@ -28,29 +30,35 @@ const Navbar = () => { }; return ( - <nav className="bg-white shadow-md"> + <nav className="bg-white dark:bg-gray-900 shadow-md transition-colors"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="flex justify-between h-16"> <div className="flex items-center"> <Link to="/" className="flex items-center gap-2"> - <CheckCircle className="h-8 w-8 text-violet-600" /> - <span className="text-xl font-semibold text-gray-900">TaskMaster</span> + <CheckCircle className="h-8 w-8 text-violet-600 dark:text-violet-400" /> + <span className="text-xl font-semibold text-gray-900 dark:text-white">TaskMaster</span> </Link> </div> <div className="flex items-center gap-6"> <Link to="/" - className="text-gray-700 hover:text-violet-600 px-3 py-2 rounded-md text-sm font-medium" + className="text-gray-700 dark:text-white hover:text-violet-600 dark:hover:text-violet-400 px-3 py-2 rounded-md text-sm font-medium" > Dashboard </Link> <Link to="/todolist" - className="text-gray-700 hover:text-violet-600 px-3 py-2 rounded-md text-sm font-medium" + className="text-gray-700 dark:text-white hover:text-violet-600 dark:hover:text-violet-400 px-3 py-2 rounded-md text-sm font-medium" > Tasks List </Link> + + {/* Bouton Light/Dark Mode */} + <button onClick={toggleTheme} className="p-2 rounded-md bg-gray-200 dark:bg-gray-700"> + {theme === "light" ? <Moon className="h-5 w-5 text-gray-700" /> : <Sun className="h-5 w-5 text-yellow-400" />} + </button> + <div className="flex items-center gap-2"> {isAuthenticated ? ( // Bouton Logout si l'utilisateur est connecté @@ -65,7 +73,7 @@ const Navbar = () => { <> <Link to="/signin" - className="text-gray-700 hover:text-violet-600 px-3 py-2 rounded-md text-sm font-medium" + className="text-gray-700 dark:text-white hover:text-violet-600 dark:hover:text-violet-400 px-3 py-2 rounded-md text-sm font-medium" > Login </Link> diff --git a/src/components/dashboard/TaskCard.jsx b/src/components/dashboard/TaskCard.jsx index 8c700770dbb9857c657d71152b73ab696a8e0832..2135e0241aa7332ec799fe3c091a117bf843dc44 100644 --- a/src/components/dashboard/TaskCard.jsx +++ b/src/components/dashboard/TaskCard.jsx @@ -2,13 +2,13 @@ import React from "react"; const TaskCard = ({ title, count }) => { return ( - <div className="bg-white rounded-lg p-4 shadow-sm border border-gray-100 min-w-[200px]"> + <div className="bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-100 dark:border-gray-700 min-w-[200px]"> <div className="flex justify-between items-center mb-4"> - <h3 className="text-sm text-gray-600">{title}</h3> + <h3 className="text-sm text-gray-600 dark:text-gray-300">{title}</h3> </div> <div className="flex items-baseline gap-1"> - <span className="text-2xl font-semibold">{count}</span> - <span className="text-xs text-gray-500">Task count</span> + <span className="text-2xl font-semibold text-gray-900 dark:text-white">{count}</span> + <span className="text-xs text-gray-500 dark:text-gray-400">Task count</span> </div> </div> ); diff --git a/src/components/dashboard/TaskStats.jsx b/src/components/dashboard/TaskStats.jsx index 440bca0c4784450b9cada2a408e5aa34e37588c6..52f284de5a711e2fdec757c9a3bcf435a4ccceaf 100644 --- a/src/components/dashboard/TaskStats.jsx +++ b/src/components/dashboard/TaskStats.jsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React from "react"; import TaskCard from "@components/dashboard/TaskCard"; import { useTasks } from "@context/TasksContext"; diff --git a/src/components/dashboard/TasksStatPercent.jsx b/src/components/dashboard/TasksStatPercent.jsx index 56508a87aa885c421332d665173210ce0f6e0f88..fbd1998f29dd39a4f3246e1af2412736b6c83c7c 100644 --- a/src/components/dashboard/TasksStatPercent.jsx +++ b/src/components/dashboard/TasksStatPercent.jsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React from "react"; import { useTasks } from "@context/TasksContext"; const TasksStatPercent = () => { @@ -14,7 +14,7 @@ const TasksStatPercent = () => { return ( <div> - <h2 className="text-xl font-semibold mb-4"> All Tasks completion status</h2> + <h2 className="text-xl font-semibold mb-4 dark:text-white">All Tasks completion status</h2> <div className="flex items-center justify-between"> <div className="relative"> <svg width="120" height="120" className="transform -rotate-90"> @@ -35,24 +35,25 @@ const TasksStatPercent = () => { fill="none" strokeDasharray={circumference} strokeDashoffset={strokeDashoffset} + className="dark:stroke-purple-400" style={{ transition: 'stroke-dashoffset 0.5s ease' }} /> </svg> <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"> - <div className="text-2xl font-bold">{totalTasks}</div> - <div className="text-sm text-gray-500">Total</div> + <div className="text-2xl font-bold dark:text-white">{totalTasks}</div> + <div className="text-sm text-gray-500 dark:text-gray-400">Total</div> </div> </div> <div className="space-y-4"> - <div className="bg-white p-3 rounded-lg shadow-sm border border-gray-100"> - <div className="text-2xl font-bold text-violet-700">{completedPercentage}%</div> - <div className="text-sm text-gray-600">Completed</div> + <div className="bg-white dark:bg-gray-800 p-3 rounded-lg shadow-sm border border-gray-100 dark:border-gray-700"> + <div className="text-2xl font-bold text-violet-700 dark:text-violet-400">{completedPercentage}%</div> + <div className="text-sm text-gray-600 dark:text-gray-300">Completed</div> </div> - <div className="bg-white p-3 rounded-lg shadow-sm border border-gray-100"> + <div className="bg-white dark:bg-gray-800 p-3 rounded-lg shadow-sm border border-gray-100 dark:border-gray-700"> <div className="text-2xl font-bold text-violet-400">{100 - completedPercentage}%</div> - <div className="text-sm text-gray-600">Uncompleted</div> + <div className="text-sm text-gray-600 dark:text-gray-300">Uncompleted</div> </div> </div> </div> @@ -60,4 +61,4 @@ const TasksStatPercent = () => { ); }; -export default TasksStatPercent; \ No newline at end of file +export default TasksStatPercent; diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx new file mode 100644 index 0000000000000000000000000000000000000000..24cea23d1311e9751d8ccdb287a48f88d6948748 --- /dev/null +++ b/src/context/ThemeContext.jsx @@ -0,0 +1,28 @@ +import { createContext, useContext, useEffect, useState } from "react"; + +const ThemeContext = createContext(); + +const ThemeProvider = ({ children }) => { + const [theme, setTheme] = useState(() => { + return localStorage.getItem("theme") || "light"; + }); + + useEffect(() => { + document.documentElement.classList.toggle("dark", theme === "dark"); + localStorage.setItem("theme", theme); + }, [theme]); + + const toggleTheme = () => { + setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light")); + }; + + return ( + <ThemeContext.Provider value={{ theme, toggleTheme }}> + {children} + </ThemeContext.Provider> + ); +}; + +const useTheme = () => useContext(ThemeContext); + +export { ThemeProvider, useTheme }; diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index a879a324a5a76a4e07f76977d6057e20d4129cfd..e71f17a398fb7e28bf88696052c4d105af7960e4 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -7,28 +7,23 @@ const Home = () => { const navigate = useNavigate(); return ( <div> - <div className="p-6 bg-gray"> + <div className="p-6 bg-gray-50 dark:bg-gray-900"> <TaskStats /> </div> - <div className="min-h-screen bg-gray-100 p-8"> + <div className="min-h-screen bg-gray-100 dark:bg-gray-800 p-8"> <div className="grid grid-cols-2 gap-6"> - <div className="bg-white rounded-lg shadow-md p-6 min-h-[300px]"> - + <div className="bg-white dark:bg-gray-700 rounded-lg shadow-md p-6 min-h-[300px]"> </div> - - <div className="bg-white rounded-lg shadow-md p-6 min-h-[300px]"> + <div className="bg-white dark:bg-gray-700 rounded-lg shadow-md p-6 min-h-[300px]"> <Calendar /> </div> - - <div className="bg-white rounded-lg shadow-md p-6 min-h-[300px]"> - + <div className="bg-white dark:bg-gray-700 rounded-lg shadow-md p-6 min-h-[300px]"> </div> - - <div className="bg-white rounded-lg shadow-md p-6 min-h-[300px]"> + <div className="bg-white dark:bg-gray-700 rounded-lg shadow-md p-6 min-h-[300px]"> <TasksStatPercent/> </div> @@ -38,4 +33,4 @@ const Home = () => { ); }; -export default Home; +export default Home; \ No newline at end of file diff --git a/src/pages/SignIn.jsx b/src/pages/SignIn.jsx index a4731515ce0f8c08750e3f34c45471ad129bdb27..e32929eda97f9725089f2e53a2c8459073b1e66b 100644 --- a/src/pages/SignIn.jsx +++ b/src/pages/SignIn.jsx @@ -23,40 +23,50 @@ const SignIn = () => { }; return ( - <div className="flex min-h-screen items-center justify-center bg-gray-100"> - <div className="w-full max-w-md p-6 bg-white rounded-lg shadow-md"> - <h2 className="text-2xl font-semibold text-center mb-6">Login to your to-do list</h2> + <div className="flex min-h-screen items-center justify-center bg-gray-100 dark:bg-gray-900 transition-colors"> + <div className="w-full max-w-md p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md transition-all"> + <h2 className="text-2xl font-semibold text-center mb-6 text-gray-900 dark:text-white"> + Login to your to-do list + </h2> {error && <p className="text-red-500 text-sm text-center mb-4">{error}</p>} - <div className="border-b mb-4"></div> + <div className="border-b mb-4 border-gray-300 dark:border-gray-600"></div> <form onSubmit={handleSubmit}> <div className="mb-4"> - <label className="block text-gray-700">Email</label> + <label className="block text-gray-700 dark:text-gray-300">Email</label> <input type="email" - className="w-full px-4 py-2 border rounded-lg" + className="w-full px-4 py-2 border rounded-lg bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring focus:border-violet-500 dark:focus:border-violet-400" value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <div className="mb-4"> - <label className="block text-gray-700">Password</label> + <label className="block text-gray-700 dark:text-gray-300">Password</label> <input type="password" - className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300" + className="w-full px-4 py-2 border rounded-lg bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring focus:border-violet-500 dark:focus:border-violet-400" value={password} onChange={(e) => setPassword(e.target.value)} /> - <div className="text-right text-sm text-blue-600 cursor-pointer">Forgot?</div> + <div className="text-right text-sm text-blue-600 dark:text-blue-400 cursor-pointer"> + Forgot? + </div> </div> - <button type="submit" className="w-full bg-violet-600 text-white hover:bg-violet-700 py-2 rounded-lg">Login</button> + <button + type="submit" + className="w-full bg-violet-600 dark:bg-violet-700 text-white hover:bg-violet-700 dark:hover:bg-violet-600 py-2 rounded-lg transition"> + Login + </button> </form> - <p className="mt-4 text-center text-sm"> - Don't have an account? <Link to="/signup" className="text-blue-600">Sign up</Link> + + <p className="mt-4 text-center text-sm text-gray-600 dark:text-gray-300"> + Don't have an account? + <Link to="/signup" className="text-blue-600 dark:text-blue-400 ml-1">Sign up</Link> </p> </div> </div> diff --git a/src/pages/SignUp.jsx b/src/pages/SignUp.jsx index 635c7706d5c97f8a66937cc27a4b6e6a1c34a30f..9f28cb2f5812143a068a0d342c9a22d18c082c82 100644 --- a/src/pages/SignUp.jsx +++ b/src/pages/SignUp.jsx @@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom"; import { signUp } from "../services/auth"; import { Link } from "react-router-dom"; - const SignUp = () => { const [name, setName] = useState(""); const [email, setEmail] = useState(""); @@ -23,53 +22,61 @@ const SignUp = () => { }; return ( - <div className="flex min-h-screen items-center justify-center bg-gray-100"> - <div className="w-full max-w-md p-6 bg-white rounded-lg shadow-md"> - <h2 className="text-2xl font-semibold text-center mb-6">Sign up to your to-do list</h2> + <div className="flex min-h-screen items-center justify-center bg-gray-100 dark:bg-gray-900 transition-colors"> + <div className="w-full max-w-md p-6 bg-white dark:bg-gray-800 rounded-lg shadow-md transition-all"> + <h2 className="text-2xl font-semibold text-center mb-6 text-gray-900 dark:text-white"> + Sign up to your to-do list + </h2> {error && <p className="text-red-500 text-sm text-center mb-4">{error}</p>} - <div className="border-b mb-4"></div> + <div className="border-b mb-4 border-gray-300 dark:border-gray-600"></div> <form onSubmit={handleSubmit}> <div className="mb-4"> - <label className="block text-gray-700">Name</label> - <input + <label className="block text-gray-700 dark:text-gray-300">Name</label> + <input type="text" - className="w-full px-4 py-2 border rounded-lg" + className="w-full px-4 py-2 border rounded-lg bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring focus:border-violet-500 dark:focus:border-violet-400" value={name} onChange={(e) => setName(e.target.value)} /> </div> - + <div className="mb-4"> - <label className="block text-gray-700">Email</label> - <input + <label className="block text-gray-700 dark:text-gray-300">Email</label> + <input type="email" - className="w-full px-4 py-2 border rounded-lg" + className="w-full px-4 py-2 border rounded-lg bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring focus:border-violet-500 dark:focus:border-violet-400" value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <div className="mb-4"> - <label className="block text-gray-700">Password</label> - <input + <label className="block text-gray-700 dark:text-gray-300">Password</label> + <input type="password" - className="w-full px-4 py-2 border rounded-lg" + className="w-full px-4 py-2 border rounded-lg bg-gray-50 dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring focus:border-violet-500 dark:focus:border-violet-400" value={password} onChange={(e) => setPassword(e.target.value)} /> </div> - <button type="submit" className="w-full bg-violet-600 text-white hover:bg-violet-700 py-2 rounded-lg">Sign up</button> + <button + type="submit" + className="w-full bg-violet-600 dark:bg-violet-700 text-white hover:bg-violet-700 dark:hover:bg-violet-600 py-2 rounded-lg transition"> + Sign up + </button> </form> - <p className="mt-4 text-center text-sm"> - Already have an account? <Link to="/signin" className="text-blue-600">Sign in</Link> + + <p className="mt-4 text-center text-sm text-gray-600 dark:text-gray-300"> + Already have an account? + <Link to="/signin" className="text-blue-600 dark:text-blue-400 ml-1">Sign in</Link> </p> </div> </div> ); }; -export default SignUp; +export default SignUp; \ No newline at end of file