Skip to content
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"drizzle-zod": "^0.8.3",
"lucide-react": "^0.544.0",
"next": "15.4.6",
"next-themes": "^0.4.6",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-hook-form": "^7.62.0",
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

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

16 changes: 12 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Toaster } from "react-hot-toast";
import { ThemeProvider } from "@/components/theme-provider";

const geistSans = Geist({
variable: "--font-geist-sans",
Expand All @@ -27,12 +28,19 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<html lang="en" suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-gray-50 min-h-screen`}
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background min-h-screen`}
>
<main>{children}</main>
<Toaster position="bottom-right" />
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<main>{children}</main>
<Toaster position="bottom-right" />
</ThemeProvider>
</body>
</html>
);
Expand Down
44 changes: 44 additions & 0 deletions src/components/mode-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { Moon, Sun, Monitor } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";

export function ModeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);

// Avoid hydration mismatch by only rendering after mount
useEffect(() => {
setMounted(true);
}, []);

if (!mounted) {
return (
<Button variant="ghost" size="icon" disabled>
<Sun className="h-[1.2rem] w-[1.2rem]" />
<span className="sr-only">Loading theme</span>
</Button>
);
}

const cycleTheme = () => {
if (theme === "light") {
setTheme("dark");
} else if (theme === "dark") {
setTheme("system");
} else {
setTheme("light");
}
};

return (
<Button variant="ghost" size="icon" onClick={cycleTheme} title={`Current theme: ${theme}`}>
{theme === "light" && <Sun className="h-[1.2rem] w-[1.2rem]" />}
{theme === "dark" && <Moon className="h-[1.2rem] w-[1.2rem]" />}
{theme === "system" && <Monitor className="h-[1.2rem] w-[1.2rem]" />}
<span className="sr-only">Toggle theme (currently {theme})</span>
</Button>
);
}
10 changes: 7 additions & 3 deletions src/components/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { CheckSquare, Home } from "lucide-react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import LogoutButton from "../modules/auth/components/logout-button";
import { ModeToggle } from "@/components/mode-toggle";

export function Navigation() {
return (
<nav className="border-b bg-white sticky top-0 z-50">
<nav className="border-b bg-card sticky top-0 z-50">
<div className="container mx-auto px-4 py-3">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-6">
<Link
href="/"
className="text-xl font-bold text-gray-900"
className="text-xl font-bold"
>
TodoApp
</Link>
Expand All @@ -30,7 +31,10 @@ export function Navigation() {
</Link>
</div>
</div>
<LogoutButton />
<div className="flex items-center space-x-2">
<ModeToggle />
<LogoutButton />
</div>
</div>
</div>
</nav>
Expand Down
8 changes: 8 additions & 0 deletions src/components/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use client";

import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes";

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
24 changes: 12 additions & 12 deletions src/modules/dashboard/dashboard.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export default async function Dashboard() {
return (
<div className="container mx-auto py-12 px-4">
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
<h1 className="text-4xl font-bold mb-4">
Welcome to TodoApp
</h1>
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
A simple and elegant todo application built with Next.js 15,
TailwindCSS, and shadcn/ui components.
</p>
Expand Down Expand Up @@ -65,34 +65,34 @@ export default async function Dashboard() {
</div>

<div className="mt-16 text-center">
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
<h2 className="text-2xl font-semibold mb-4">
Features
</h2>
<div className="grid md:grid-cols-3 gap-6 max-w-3xl mx-auto">
<div className="text-center">
<div className="bg-blue-100 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3">
<CheckSquare className="h-6 w-6 text-blue-600" />
<div className="bg-primary/10 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3">
<CheckSquare className="h-6 w-6 text-primary" />
</div>
<h3 className="font-semibold mb-2">Task Management</h3>
<p className="text-gray-600 text-sm">
<p className="text-muted-foreground text-sm">
Create, edit, and delete todos with ease
</p>
</div>
<div className="text-center">
<div className="bg-green-100 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3">
<List className="h-6 w-6 text-green-600" />
<div className="bg-primary/10 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3">
<List className="h-6 w-6 text-primary" />
</div>
<h3 className="font-semibold mb-2">Categories</h3>
<p className="text-gray-600 text-sm">
<p className="text-muted-foreground text-sm">
Organize your todos with custom categories
</p>
</div>
<div className="text-center">
<div className="bg-purple-100 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3">
<Plus className="h-6 w-6 text-purple-600" />
<div className="bg-primary/10 rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-3">
<Plus className="h-6 w-6 text-primary" />
</div>
<h3 className="font-semibold mb-2">Rich Features</h3>
<p className="text-gray-600 text-sm">
<p className="text-muted-foreground text-sm">
Priorities, due dates, images, and more
</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/modules/todos/components/delete-todo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function DeleteTodo({ todoId }: DeleteTodoProps) {
<Button
variant="ghost"
size="sm"
className="text-red-600 hover:text-red-700 hover:bg-red-50"
className="text-destructive hover:text-destructive hover:bg-destructive/10"
>
<Trash2 className="h-4 w-4" />
</Button>
Expand All @@ -68,7 +68,7 @@ export function DeleteTodo({ todoId }: DeleteTodoProps) {
<AlertDialogAction
onClick={handleDelete}
disabled={isPending}
className="bg-red-600 hover:bg-red-700"
className="bg-destructive hover:bg-destructive/90"
>
{isPending ? "Deleting..." : "Delete"}
</AlertDialogAction>
Expand Down
10 changes: 5 additions & 5 deletions src/modules/todos/components/todo-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ export function TodoCard({ todo }: TodoCardProps) {
</div>
<div className="flex-1 min-w-0">
<h3
className={`font-semibold text-lg leading-tight ${todo.completed ? "line-through text-gray-500" : ""}`}
className={`font-semibold text-lg leading-tight ${todo.completed ? "line-through text-muted-foreground" : ""}`}
>
{todo.title}
</h3>
{todo.description && (
<p
className={`text-sm mt-1 ${todo.completed ? "text-gray-400" : "text-gray-600"}`}
className={`text-sm mt-1 ${todo.completed ? "text-muted-foreground/70" : "text-muted-foreground"}`}
>
{todo.description}
</p>
Expand Down Expand Up @@ -120,7 +120,7 @@ export function TodoCard({ todo }: TodoCardProps) {
<Badge variant="secondary">{todo.categoryName}</Badge>
)}
{todo.imageUrl && (
<Badge variant="outline" className="text-blue-600">
<Badge variant="outline" className="text-primary">
<ImageIcon className="h-3 w-3 mr-1" />
Image
</Badge>
Expand All @@ -129,12 +129,12 @@ export function TodoCard({ todo }: TodoCardProps) {

{todo.dueDate && (
<div
className={`flex items-center text-sm ${isOverdue ? "text-red-600" : "text-gray-500"}`}
className={`flex items-center text-sm ${isOverdue ? "text-destructive" : "text-muted-foreground"}`}
>
<Calendar className="h-4 w-4 mr-1" />
Due: {formatDate(todo.dueDate)}
{isOverdue && (
<span className="ml-2 text-red-600 font-semibold">
<span className="ml-2 text-destructive font-semibold">
(Overdue)
</span>
)}
Expand Down
8 changes: 4 additions & 4 deletions src/modules/todos/components/todo-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -443,12 +443,12 @@ export function TodoForm({
</Button>
</div>
) : (
<div className="border-2 border-dashed border-gray-300 rounded-md p-6 text-center">
<Upload className="mx-auto h-12 w-12 text-gray-400" />
<div className="border-2 border-dashed border-border rounded-md p-6 text-center">
<Upload className="mx-auto h-12 w-12 text-muted-foreground" />
<div className="mt-2">
<label
htmlFor="image-upload"
className="cursor-pointer text-blue-600 hover:text-blue-500"
className="cursor-pointer text-primary hover:text-primary/80"
>
Upload an image
</label>
Expand All @@ -460,7 +460,7 @@ export function TodoForm({
onChange={handleImageChange}
/>
</div>
<p className="text-xs text-gray-500 mt-1">
<p className="text-xs text-muted-foreground mt-1">
PNG, JPG up to 5MB
</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/modules/todos/edit-todo.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default async function EditTodoPage({ id }: EditTodoPageProps) {
</Button>
</Link>
<h1 className="text-3xl font-bold">Edit Todo</h1>
<p className="text-gray-600 mt-1">Update your task details</p>
<p className="text-muted-foreground mt-1">Update your task details</p>
</div>

<TodoForm user={user} categories={categories} initialData={todo} />
Expand Down
2 changes: 1 addition & 1 deletion src/modules/todos/new-todo.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default async function NewTodoPage() {
</Button>
</Link>
<h1 className="text-3xl font-bold">Create New Todo</h1>
<p className="text-gray-600 mt-1">
<p className="text-muted-foreground mt-1">
Add a new task to your todo list
</p>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/modules/todos/todo-list.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default async function TodoListPage() {
<div className="flex justify-between items-center mb-8 w-full">
<div>
<h1 className="text-3xl font-bold">Todos</h1>
<p className="text-gray-600 mt-1">
<p className="text-muted-foreground mt-1">
Manage your tasks and stay organized
</p>
</div>
Expand All @@ -27,11 +27,11 @@ export default async function TodoListPage() {

{todos.length === 0 ? (
<div className="text-center py-12 w-full">
<div className="text-gray-400 text-6xl mb-4">📝</div>
<h3 className="text-xl font-semibold text-gray-600 mb-2">
<div className="text-muted-foreground/40 text-6xl mb-4">📝</div>
<h3 className="text-xl font-semibold text-muted-foreground mb-2">
No todos yet
</h3>
<p className="text-gray-500 mb-6">
<p className="text-muted-foreground/80 mb-6">
Create your first todo to get started
</p>
<Link href={todosRoutes.new}>
Expand Down