From 50a293aea51c4e421cd7e33253cc08e66f617823 Mon Sep 17 00:00:00 2001 From: sezallagwal Date: Tue, 29 Oct 2024 15:09:01 +0530 Subject: [PATCH 1/4] Refactor navigation bar for improved responsiveness --- src/app/diary/page.jsx | 472 ++++++++++++++++++++++++----------------- 1 file changed, 275 insertions(+), 197 deletions(-) diff --git a/src/app/diary/page.jsx b/src/app/diary/page.jsx index 7e3a77d..ae97fd9 100644 --- a/src/app/diary/page.jsx +++ b/src/app/diary/page.jsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; @@ -6,217 +6,295 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { useEffect, useState } from "react"; -// added form initial state const initialFormState = { - title: "", - content: "", + title: "", + content: "", + tags: [], // Add tags to the initial state }; export default function Diary() { - const [loading, setLoading] = useState(false); - const [diaries, setDiaries] = useState([]); - const [count, setCount] = useState(false); - const [form, setForm] = useState(initialFormState); // initializing with initial form state + const [loading, setLoading] = useState(false); + const [diaries, setDiaries] = useState([]); + const [count, setCount] = useState(false); + const [form, setForm] = useState(initialFormState); + const [showModal, setShowModal] = useState(false); + const [currentDiary, setCurrentDiary] = useState(null); - const [showModal, setShowModal] = useState(false); - const [currentDiary, setCurrentDiary] = useState(null); + const handleEdit = (diary) => { + setCurrentDiary(diary); + setShowModal(true); + }; - const handleEdit = (diary) => { - setCurrentDiary(diary); - setShowModal(true); - }; - - const handleModalSubmit = async (e) => { - e.preventDefault(); - setShowModal(false); - try { - const res = await fetch(`/api/diary/${currentDiary.id}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(currentDiary), - }); - const data = await res.json(); - } catch (err) { - console.log("error while sending the diary details", err); - } finally { - setCount((prev) => !prev); - setForm(initialFormState); // after making the request again setting the form state to initial state - } - }; + const handleModalSubmit = async (e) => { + e.preventDefault(); + setShowModal(false); + try { + const res = await fetch(`/api/diary/${currentDiary.id}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: currentDiary.title, + content: currentDiary.content, + tags: currentDiary.tags, // Include tags + }), + }); + const data = await res.json(); + } catch (err) { + console.log("error while sending the diary details", err); + } finally { + setCount((prev) => !prev); + setForm(initialFormState); + } + }; - useEffect(() => { - fetchDiaries(); - }, [count]); + useEffect(() => { + fetchDiaries(); + }, [count]); - const sendingData = async (data) => { - try { - let res = await fetch("/api/diary", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); - res = await res.json(); - } catch (err) { - console.log("error while sending the diary details", err); - } finally { - setLoading((prev) => false); - setCount((prev) => !prev); - } - } - const handleSubmit = (event) => { - event.preventDefault(); - setLoading((prev) => true); - sendingData(form); + const sendingData = async (data) => { + try { + let res = await fetch("/api/diary", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: data.title, + content: data.content, + tags: data.tags, // Include tags + }), + }); + res = await res.json(); + } catch (err) { + console.log("error while sending the diary details", err); + } finally { + setLoading(false); + setCount((prev) => !prev); } + }; - const handleDelete = async (id) => { - try { - let res = await fetch(`/api/diary/${id}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - }); - res = await res.json(); - } catch (err) { console.log("error while sending the diary details", err); } - finally { - setCount((prev) => !prev); - } + const handleSubmit = (event) => { + event.preventDefault(); + setLoading(true); + sendingData(form); + }; + + const handleDelete = async (id) => { + try { + let res = await fetch(`/api/diary/${id}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + res = await res.json(); + } catch (err) { + console.log("error while sending the diary details", err); + } finally { + setCount((prev) => !prev); } + }; - const fetchDiaries = async () => { - try { - let res = await fetch("/api/diary", { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - res = await res.json(); - setDiaries((prev) => res.diaries); - } catch (err) { - console.log("error while sending the diary details", err); - } + const fetchDiaries = async () => { + try { + let res = await fetch("/api/diary", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + res = await res.json(); + setDiaries(res.diaries); + } catch (err) { + console.log("error while sending the diary details", err); } + }; - return ( -
- {/* Add Diary Card */} - - - Add Diary - - -
-
-
- - setForm({ ...form, title: e.target.value })} - className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" - /> -
-
- - setForm({ ...form, content: e.target.value })} - className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" - /> -
-
- {loading ? ( - - ) : ( - - )} -
-
-
+ return ( +
+ {/* Add Diary Card */} + + + + Add Diary + + + +
+
+
+ + setForm({ ...form, title: e.target.value })} + className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> +
+
+ + + setForm({ ...form, content: e.target.value }) + } + className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> +
+
+ + + setForm({ + ...form, + tags: e.target.value.split(",").map((tag) => tag.trim()), + }) + } // Split and trim tags + className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> +
+
+ {loading ? ( + + ) : ( + + )} +
+
+
- {/* List of Diaries */} - - {diaries.map((diary, index) => ( - - - {diary.title} - - -

{diary.content}

-
-
- - -
-
- ))} -
+ {/* List of Diaries */} + + {diaries.map((diary, index) => ( + + + + {diary.title} + + + +

{diary.content}

+

+ Tags: {diary.tags.join(", ")} +

{" "} + {/* Display tags */} +
+
+ + +
+
+ ))} +
- {/* Modal for Editing */} - {showModal && ( -
-
- - Edit Diary - - -
-
-
- - setCurrentDiary({ ...currentDiary, title: e.target.value })} - className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" - /> -
-
- - setCurrentDiary({ ...currentDiary, content: e.target.value })} - className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" - /> -
-
-
- - -
-
-
-
+ {/* Modal for Editing */} + {showModal && ( +
+
+ + + Edit Diary + + + +
+
+
+ + + setCurrentDiary({ + ...currentDiary, + title: e.target.value, + }) + } + className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> +
+
+ + + setCurrentDiary({ + ...currentDiary, + content: e.target.value, + }) + } + className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> +
+
+ + + setCurrentDiary({ + ...currentDiary, + tags: e.target.value + .split(",") + .map((tag) => tag.trim()), + }) + } // Split and trim tags + className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> +
- )} +
+ + +
+
+
+
- - - ); -} \ No newline at end of file + )} +
+ ); +} From 592118572ed44cb757289a97e794432a3c7177d4 Mon Sep 17 00:00:00 2001 From: sezallagwal Date: Tue, 29 Oct 2024 15:57:22 +0530 Subject: [PATCH 2/4] added search and tag features and their respective backend logic --- .../migrations/20241029084744_/migration.sql | 13 ++ .../20241029092746_tags/migration.sql | 2 + prisma/schema.prisma | 2 + src/app/api/diary/[...id]/route.js | 10 +- src/app/api/diary/route.js | 24 ++- src/app/diary/page.jsx | 137 +++++++++-------- src/components/Footer.js | 140 ++++++++++++------ 7 files changed, 205 insertions(+), 123 deletions(-) create mode 100644 prisma/migrations/20241029084744_/migration.sql create mode 100644 prisma/migrations/20241029092746_tags/migration.sql diff --git a/prisma/migrations/20241029084744_/migration.sql b/prisma/migrations/20241029084744_/migration.sql new file mode 100644 index 0000000..fb66fdc --- /dev/null +++ b/prisma/migrations/20241029084744_/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "Notes" ( + "id" TEXT NOT NULL, + "content" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Notes_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Notes" ADD CONSTRAINT "Notes_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20241029092746_tags/migration.sql b/prisma/migrations/20241029092746_tags/migration.sql new file mode 100644 index 0000000..9184c67 --- /dev/null +++ b/prisma/migrations/20241029092746_tags/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Diary" ADD COLUMN "tags" TEXT[] DEFAULT ARRAY[]::TEXT[]; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b62d214..dd6b4a1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -37,10 +37,12 @@ model Diary { content String user User @relation(fields: [userId], references: [id]) userId String + tags String[] @default([]) // Ensure tags are stored as an array of strings createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } + model Notes { id String @id @default(uuid()) content String diff --git a/src/app/api/diary/[...id]/route.js b/src/app/api/diary/[...id]/route.js index a1f37e8..4a671bd 100644 --- a/src/app/api/diary/[...id]/route.js +++ b/src/app/api/diary/[...id]/route.js @@ -35,12 +35,13 @@ export async function PATCH(req, { params }) { } const { id } = params; const body = await req.json(); - const { title, content } = body; + const { title, content, tags } = body; // Include tags here try { const user = await prisma.user.findUnique({ where: { clerkId: userId, - }, select: { + }, + select: { id: true } }); @@ -51,7 +52,8 @@ export async function PATCH(req, { params }) { }, data: { title, - content + content, + tags: tags || [] // Update tags as well }, }); return Response.json({ message: "diary updated successfully" }); @@ -59,4 +61,4 @@ export async function PATCH(req, { params }) { console.log(err); return Response.json({ error: "Error updating diary" }); } -} \ No newline at end of file +} diff --git a/src/app/api/diary/route.js b/src/app/api/diary/route.js index b9c44bc..62969c5 100644 --- a/src/app/api/diary/route.js +++ b/src/app/api/diary/route.js @@ -8,19 +8,26 @@ export async function GET(req) { } let user = await prisma.user.findUnique({ where: { clerkId: userId }, - }) + }); let diaries = await prisma.diary.findMany({ where: { userId: user.id, }, + select: { // Optionally, select only the fields you want + id: true, + title: true, + content: true, + tags: true, // Ensure tags are selected + userId: true, + }, }); return Response.json({ diaries }); } + export async function POST(req) { const { userId } = getAuth(req); - const body = await req.json(); if (!userId) { @@ -28,28 +35,29 @@ export async function POST(req) { } try { - let clerkUser = await clerkClient.users.getUser(userId) + let clerkUser = await clerkClient.users.getUser(userId); clerkUser = { id: userId, name: clerkUser.firstName + " " + clerkUser.lastName, email: clerkUser.primaryEmailAddress.emailAddress, - } + }; let user = await prisma.user.findUnique({ where: { clerkId: clerkUser.id }, - }) + }); if (!user) { user = await prisma.user.create({ data: { clerkId: clerkUser.id, - name: clerkUser.name || '', // Use fullName if available - email: clerkUser.email || '', // Use emailAddress if available + name: clerkUser.name || '', + email: clerkUser.email || '', }, - }) + }); } const diary = await prisma.diary.create({ data: { title: body.title, content: body.content, + tags: body.tags || [], // Add this line to save tags userId: user.id, }, }); diff --git a/src/app/diary/page.jsx b/src/app/diary/page.jsx index ae97fd9..553bd99 100644 --- a/src/app/diary/page.jsx +++ b/src/app/diary/page.jsx @@ -9,7 +9,7 @@ import { useEffect, useState } from "react"; const initialFormState = { title: "", content: "", - tags: [], // Add tags to the initial state + tags: [], }; export default function Diary() { @@ -19,6 +19,8 @@ export default function Diary() { const [form, setForm] = useState(initialFormState); const [showModal, setShowModal] = useState(false); const [currentDiary, setCurrentDiary] = useState(null); + const [searchQuery, setSearchQuery] = useState(""); + const [uniqueTags, setUniqueTags] = useState([]); const handleEdit = (diary) => { setCurrentDiary(diary); @@ -37,14 +39,14 @@ export default function Diary() { body: JSON.stringify({ title: currentDiary.title, content: currentDiary.content, - tags: currentDiary.tags, // Include tags + tags: currentDiary.tags, }), }); - const data = await res.json(); + await res.json(); + fetchDiaries(); } catch (err) { - console.log("error while sending the diary details", err); + console.log("Error updating diary:", err); } finally { - setCount((prev) => !prev); setForm(initialFormState); } }; @@ -60,18 +62,15 @@ export default function Diary() { headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ - title: data.title, - content: data.content, - tags: data.tags, // Include tags - }), + body: JSON.stringify(data), }); - res = await res.json(); + await res.json(); + fetchDiaries(); } catch (err) { - console.log("error while sending the diary details", err); + console.log("Error sending diary:", err); } finally { setLoading(false); - setCount((prev) => !prev); + setForm(initialFormState); } }; @@ -83,17 +82,15 @@ export default function Diary() { const handleDelete = async (id) => { try { - let res = await fetch(`/api/diary/${id}`, { + await fetch(`/api/diary/${id}`, { method: "DELETE", headers: { "Content-Type": "application/json", }, }); - res = await res.json(); + fetchDiaries(); } catch (err) { - console.log("error while sending the diary details", err); - } finally { - setCount((prev) => !prev); + console.log("Error deleting diary:", err); } }; @@ -107,14 +104,23 @@ export default function Diary() { }); res = await res.json(); setDiaries(res.diaries); + setUniqueTags([...new Set(res.diaries.flatMap((diary) => diary.tags))]); // Populate unique tags } catch (err) { - console.log("error while sending the diary details", err); + console.log("Error fetching diaries:", err); } }; + const filteredDiaries = diaries.filter( + (diary) => + diary.title.toLowerCase().includes(searchQuery.toLowerCase()) || + diary.content.toLowerCase().includes(searchQuery.toLowerCase()) || + diary.tags.some((tag) => + tag.toLowerCase().includes(searchQuery.toLowerCase()) + ) + ); + return (
- {/* Add Diary Card */} @@ -153,51 +159,68 @@ export default function Diary() { setForm({ ...form, tags: e.target.value.split(",").map((tag) => tag.trim()), }) - } // Split and trim tags + } className="bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" />
- {loading ? ( - - ) : ( - - )} + - {/* List of Diaries */} - {diaries.map((diary, index) => ( + setSearchQuery(e.target.value)} + className="mb-4 bg-input text-foreground placeholder-muted-foreground border border-border rounded-md p-2 focus:ring focus:ring-primary" + /> + +
+ {uniqueTags.map((tag) => ( + + ))} +
+ + {filteredDiaries.map((diary, index) => ( - - + {diary.title} - -

{diary.content}

-

- Tags: {diary.tags.join(", ")} -

{" "} - {/* Display tags */} +

{diary.content}

+
+ {diary.tags.map((tag, index) => ( + + {tag} + + ))} +
- -
+
diff --git a/src/components/Footer.js b/src/components/Footer.js index dc87b59..e513068 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -1,33 +1,39 @@ -'use client' -import Link from 'next/link'; -import { Github, Link as FaLink } from 'lucide-react'; +"use client"; +import Link from "next/link"; +import { Github, Link as FaLink } from "lucide-react"; const Footer = () => { return ( - -