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

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
"@hookform/resolvers": "^3.3.4",
"axios": "^1.9.0",
"lucide-react": "^0.344.0",
"quill": "^2.0.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.50.1",
"react-quill": "^2.0.0",
"react-router-dom": "^6.22.1",
"zod": "^3.22.4"
},
Expand Down
Binary file modified server/notes.db
Binary file not shown.
6 changes: 3 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import ViewNotes from './pages/ViewNotes';
function App() {
return (
<Router>
<div className="min-h-screen bg-gray-50">
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors duration-300">
<Header />
<main className="container mx-auto px-4 py-8">
<main className="container mx-auto px-4 py-8 text-gray-900 dark:text-gray-100 transition-colors duration-300">
<Routes>
<Route path="/" element={<CreateNote />} />
<Route path="/notes" element={<ViewNotes />} />
Expand All @@ -19,4 +19,4 @@ function App() {
);
}

export default App;
export default App;
91 changes: 86 additions & 5 deletions src/components/CreateNoteForm.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,106 @@
// TODO: Import useForm, zodResolver, axios, useNavigate, useState, and noteSchema

import { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { z } from "zod";

import { Save } from "lucide-react";

const noteSchema = z.object({
title: z.string().min(3, "Title must be at least 3 characters"),
content: z.string().min(5, "Content must be at least 5 characters"),
});

const CreateNoteForm = () => {
// TODO: Setup isSubmitting state with useState
const [isSubmitting, setIsSubmitting] = useState(false);

// TODO: create navigate variable and set to useNavigate()

const navigate = useNavigate();



// TODO: Set up the form with useForm from react-hook-form and zodResolver from @hookform/resolvers/zod
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(noteSchema),
});

const sendToTheServer = async (data) => {
// TODO: Send the data to the server
// TODO: Use axios to create a new note in the server using the endpoint http://localhost:3001/api/notes

try {
setIsSubmitting(true);
await axios.post("http://localhost:3001/api/notes", data);
reset();
navigate("/notes");
} catch (error) {
console.error("Error creating note:", error);
alert("Failed to create note. Please try again.");
} finally {
setIsSubmitting(false);
}
};

return (
<>
<h1>Create Note</h1>
{/* TODO: Setup the form with TailwindCSS, create a form with the following fields: title, content, and submit button */}
</>
return (
<div className="max-w-lg mx-auto p-6 bg-white rounded-2xl shadow-md">
<h1 className="text-2xl font-bold mb-6 text-center text-gray-800">
Create Note
</h1>
{/* ✅ TailwindCSS */}
<form onSubmit={handleSubmit(sendToTheServer)} className="space-y-4">
{/* Title field */}
<div>
<label className="block text-sm font-medium text-gray-700">
Title
</label>
<input
type="text"
{...register("title")}
className="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-green-600 focus:ring-green-600"
placeholder="Enter note title"
/>
{errors.title && (
<p className="text-red-500 text-sm mt-1">{errors.title.message}</p>
)}
</div>

{/* Content field */}
<div>
<label className="block text-sm font-medium text-gray-700">
Content
</label>
<textarea
rows="4"
{...register("content")}
className="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-green-600 focus:ring-green-600"
placeholder="Write your note here..."
/>
{errors.content && (
<p className="text-red-500 text-sm mt-1">{errors.content.message}</p>
)}
</div>

{/* Submit button */}
<button
type="submit"
disabled={isSubmitting}
className="flex items-center justify-center w-full px-4 py-2 text-white bg-green-600 hover:bg-green-700 rounded-lg shadow-md transition disabled:opacity-50"
>
<Save className="mr-2 h-5 w-5" />
{isSubmitting ? "Saving..." : "Save Note"}
</button>
</form>
</div>
);
};

Expand Down
18 changes: 11 additions & 7 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { Sticker as Sticky, Plus, List } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
import ThemeToggle from './ThemeToggle'; // <--- Import ThemeToggle

const Header = () => {
const { pathname } = useLocation();

return (
<header className="bg-white shadow-sm">
<header className="bg-white dark:bg-gray-800 shadow-sm transition-colors duration-300">
<div className="container mx-auto px-4 py-4">
<nav className="flex items-center justify-between">
<Link to="/" className="flex items-center gap-2 text-yellow-600 font-bold text-xl">
<Sticky size={24} />
<span>Sticky Notes</span>
</Link>

<div className="flex gap-4">
<div className="flex items-center gap-4">
<Link
to="/"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
pathname === '/'
? 'bg-yellow-100 text-yellow-700'
: 'hover:bg-gray-100 text-gray-700'
? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-700 dark:text-yellow-100'
: 'hover:bg-gray-100 text-gray-700 dark:hover:bg-gray-700 dark:text-gray-200'
}`}
>
<Plus size={18} />
Expand All @@ -30,18 +31,21 @@ const Header = () => {
to="/notes"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
pathname === '/notes'
? 'bg-yellow-100 text-yellow-700'
: 'hover:bg-gray-100 text-gray-700'
? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-700 dark:text-yellow-100'
: 'hover:bg-gray-100 text-gray-700 dark:hover:bg-gray-700 dark:text-gray-200'
}`}
>
<List size={18} />
<span>View All</span>
</Link>

{/* Theme toggle button */}
<ThemeToggle />
</div>
</nav>
</div>
</header>
);
};

export default Header;
export default Header;
26 changes: 26 additions & 0 deletions src/components/ThemeToggle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useState } from "react";

export default function ThemeToggle() {
const [darkMode, setDarkMode] = useState(
localStorage.getItem("theme") === "dark"
);

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

return (
<button
onClick={() => setDarkMode(!darkMode)}
className="p-2 rounded-lg bg-yellow-500 text-white dark:bg-gray-700 dark:text-yellow-300"
>
{darkMode ? "☀️ Light Mode" : "🌙 Dark Mode"}
</button>
);
}
8 changes: 8 additions & 0 deletions src/schema/notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { z } from "zod";

export const noteSchema = z.object({
//TODO: create the title and content schema,
title: z
.string()
.min(3, "Title ka ugu yaraan 3 characters")
.max(50, "Title ka ugu badnaan 50 characters"),
// Make sure the title is required and the content is required
// Make sure the title is max 50 characters and the content is max 500 characters
content: z
.string()
.min(5, "Content ka ugu yaraan 5 characters")
.max(500, "Content ka ugu badnaan 500 characters"),

});
1 change: 1 addition & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
darkMode: "class",
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}'
Expand Down
1 change: 1 addition & 0 deletions tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"module": "ESNext",
"skipLibCheck": true,


/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
Expand Down