diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 74bdeef4..27c906b4 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -90,7 +90,7 @@ const App = () => { return ( -
+
} /> } /> diff --git a/frontend/src/components/Dashboard/DashboardTasks.jsx b/frontend/src/components/Dashboard/DashboardTasks.jsx index 9d39bace..ba2c5ec5 100644 --- a/frontend/src/components/Dashboard/DashboardTasks.jsx +++ b/frontend/src/components/Dashboard/DashboardTasks.jsx @@ -77,8 +77,8 @@ export default function DashboardTasks({ tasks, updateTask }) {

{task.title} @@ -94,20 +94,20 @@ export default function DashboardTasks({ tasks, updateTask }) { {task.status === "Completed" && ( - Completed + Completed )} {/* Hover affordance */} - + ))} ) : ( -

+
No tasks for today.
) : ( -

+

No upcoming tasks.

)} @@ -123,7 +123,7 @@ export default function TaskPreview({ tasks , updateTask}) {
diff --git a/frontend/src/components/EmptyState.jsx b/frontend/src/components/EmptyState.jsx index 1998695c..85b68e48 100644 --- a/frontend/src/components/EmptyState.jsx +++ b/frontend/src/components/EmptyState.jsx @@ -79,6 +79,3 @@ export default function EmptyState({ type = "tasks", onAction }) {
); } - - - diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index bdef2553..4c5b8ee5 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -8,6 +8,7 @@ import { ThemeContext } from "../context/ThemeContext"; import gsap from "gsap"; import { clsx } from "clsx"; import { twMerge } from "tailwind-merge"; +import ThemeToggle from "./ThemeToggle"; // Utility for merging tailwind classes safely function cn(...inputs) { @@ -261,7 +262,7 @@ const Navbar = () => { "px-4 py-2 rounded-xl text-sm font-medium transition-all duration-200 flex items-center gap-2", isActive ? "bg-[#d0f6e3] text-[#3b8ea0] shadow-sm" - : "text-[#4eb7b3] hover:bg-[#d0f6e3]/50 hover:text-[#3b8ea0]" + : "text-[#4eb7b3] hover:bg-[#d0f6e3]/50 hover:text-[#3b8ea0] dark:text-gray-300 dark:hover:bg-gray-800" ) } > @@ -293,7 +294,7 @@ const Navbar = () => { <> Login @@ -315,74 +316,100 @@ const Navbar = () => { )}
+ {/* Mobile Menu Toggle Button */} +
+ + + +
+ + {/* Mobile Navigation Dropdown */} + + {isOpen && ( + +
+ {user && navLinks.map((link) => ( + setIsOpen(false)} + className={({ isActive }) => + cn( + "px-4 py-3 rounded-xl text-base font-medium transition-colors flex items-center gap-3 w-full", + isActive + ? "bg-[#d0f6e3] text-[#3b8ea0]" + : "text-[#4eb7b3] dark:text-gray-300 hover:bg-[#d0f6e3]/50 dark:hover:bg-gray-800 hover:text-[#3b8ea0]" + ) + } + > + + {link.name} + + ))} + +
- {/* Mobile Navigation Dropdown */} - - {isOpen && ( - -
- {user && navLinks.map((link) => ( - setIsOpen(false)} - className={({ isActive }) => - cn( - "px-4 py-3 rounded-xl text-base font-medium transition-colors flex items-center gap-3 w-full", - isActive - ? "bg-[#d0f6e3] text-[#3b8ea0]" - : "text-[#4eb7b3] hover:bg-[#d0f6e3]/50 hover:text-[#3b8ea0]" - ) - } - > - - {link.name} - - ))} + {!user ? ( + <> + setIsOpen(false)} + className="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-[#3b8ea0] dark:text-gray-300 font-medium hover:bg-[#d0f6e3] dark:hover:bg-gray-800 dark:hover:text-white transition-colors" + > + + Login + -
- {!user ? ( - <> - setIsOpen(false)} - className="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-[#3b8ea0] font-medium hover:bg-[#d0f6e3] transition-colors" - > - - Login - - setIsOpen(false)} - className="w-full flex items-center justify-center gap-2 btn btn-primary py-3" - > - - Signup - - - ) : ( - - )} -
+ + Signup + + + ) : ( + + )} +
- + )} - + ); diff --git a/frontend/src/components/Routine/TaskLibrary.jsx b/frontend/src/components/Routine/TaskLibrary.jsx index 5ad23fe7..178996d9 100644 --- a/frontend/src/components/Routine/TaskLibrary.jsx +++ b/frontend/src/components/Routine/TaskLibrary.jsx @@ -29,9 +29,7 @@ function DraggableTask({ task }) { style={style} {...listeners} {...attributes} - className="group flex items-center gap-3 rounded-xl border border-soft/50 bg-[#f8fafc]/30 dark:bg-slate-800/40 p-3 - cursor-grab active:cursor-grabbing - hover:bg-white dark:hover:bg-slate-850 hover:shadow-md transition duration-200 hover-lift" + className="group flex items-center gap-3 rounded-xl border border-soft/50 dark:border-gray-700/60 bg-[#f8fafc]/30 dark:bg-slate-800/80 p-3 cursor-grab active:cursor-grabbing hover:bg-white dark:hover:bg-slate-850 hover:shadow-md transition duration-200 hover-lift" role="button" tabIndex={0} aria-label={`${task.title} - Drag to schedule or use arrow keys`} @@ -50,7 +48,7 @@ function DraggableTask({ task }) { /> {/* Title */} -

+

{task.title}

@@ -87,7 +85,7 @@ export default function TaskLibrary({ tasks, onAddTask }) { placeholder="Search tasks…" value={query} onChange={(e) => setQuery(e.target.value)} - className="mb-4 rounded-xl border border-soft/80 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#4eb7b3] bg-transparent text-main placeholder:text-muted" + className="mb-4 rounded-xl border border-soft/80 px-3 py-2 text-sm bg-transparent text-main placeholder:text-muted dark:bg-slate-800 dark:text-white dark:border-gray-700 dark:placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-[#4eb7b3]" /> {/* Task List */} diff --git a/frontend/src/components/ScrollToTop.jsx b/frontend/src/components/ScrollToTop.jsx index 47b28a89..e282c569 100644 --- a/frontend/src/components/ScrollToTop.jsx +++ b/frontend/src/components/ScrollToTop.jsx @@ -52,7 +52,7 @@ const ScrollToTop = () => { {shouldShow && ( @@ -110,7 +110,7 @@ export default function TaskItem({ task, onToggleComplete, onDelete, onUpdate, i {/* Delete Button - Fix : Ensure onDelete uses task._id*/} diff --git a/frontend/src/components/ThemeToggle.jsx b/frontend/src/components/ThemeToggle.jsx new file mode 100644 index 00000000..d7f3c5b4 --- /dev/null +++ b/frontend/src/components/ThemeToggle.jsx @@ -0,0 +1,36 @@ +import { Moon, Sun } from "lucide-react"; +import { motion } from "framer-motion"; +import { useTheme } from "../context/ThemeContext"; + +export default function ThemeToggle() { + const { theme, toggleTheme } = useTheme(); + + return ( + + {theme === "dark" ? ( + + ) : ( + + )} + + ); +} \ No newline at end of file diff --git a/frontend/src/context/ThemeContext.jsx b/frontend/src/context/ThemeContext.jsx index f283e77e..9583ab4d 100644 --- a/frontend/src/context/ThemeContext.jsx +++ b/frontend/src/context/ThemeContext.jsx @@ -1,5 +1,5 @@ /* eslint-disable react-refresh/only-export-components */ -import { createContext, useEffect, useState } from "react"; +import { createContext, useEffect, useState, useContext } from "react"; export const ThemeContext = createContext(null); @@ -34,3 +34,13 @@ export const ThemeProvider = ({ children }) => { ); }; + +export const useTheme = () => { + const context = useContext(ThemeContext); + + if (!context) { + throw new Error("useTheme must be used within ThemeProvider"); + } + + return context; +}; diff --git a/frontend/src/index.css b/frontend/src/index.css index 36b17b3e..5e9c3a2d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -81,7 +81,7 @@ html.dark body { } .card-muted { - @apply bg-white/80; + @apply bg-white/80 dark:bg-gray-800/80; } .card-primary { @@ -122,9 +122,15 @@ html.dark body { opacity: 0; transform: translateY(12px); } - to { opacity: 1; transform: translateY(0); } -} \ No newline at end of file +} + +* { + transition: + background-color 0.25s ease, + border-color 0.25s ease, + color 0.25s ease; + } diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 1c136329..a8ec2bf0 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -238,7 +238,7 @@ const handleDuplicateRoutine = async () => { icon={} /> -
+
{
@@ -280,7 +280,7 @@ export default function Tasks() { type="datetime-local" value={bulkDueDate} onChange={(e) => setBulkDueDate(e.target.value)} - className="p-2 border border-soft rounded-lg bg-transparent text-main" + className="p-2 border border-soft rounded-lg bg-transparent text-main dark:bg-slate-800" />