Skip to content

Commit

Permalink
added search and tag features and their respective backend logic
Browse files Browse the repository at this point in the history
  • Loading branch information
sezallagwal committed Oct 29, 2024
1 parent 50a293a commit 5921185
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 123 deletions.
13 changes: 13 additions & 0 deletions prisma/migrations/20241029084744_/migration.sql
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions prisma/migrations/20241029092746_tags/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Diary" ADD COLUMN "tags" TEXT[] DEFAULT ARRAY[]::TEXT[];
2 changes: 2 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions src/app/api/diary/[...id]/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
});
Expand All @@ -51,12 +52,13 @@ export async function PATCH(req, { params }) {
},
data: {
title,
content
content,
tags: tags || [] // Update tags as well
},
});
return Response.json({ message: "diary updated successfully" });
} catch (err) {
console.log(err);
return Response.json({ error: "Error updating diary" });
}
}
}
24 changes: 16 additions & 8 deletions src/app/api/diary/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,56 @@ 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) {
return new Response(JSON.stringify({ error: "User not authenticated" }), { status: 401 });
}

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,
},
});
Expand Down
137 changes: 74 additions & 63 deletions src/app/diary/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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);
Expand All @@ -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);
}
};
Expand All @@ -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);
}
};

Expand All @@ -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);
}
};

Expand All @@ -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 (
<div className="p-6 m-4 grid grid-cols-1 md:grid-cols-4 gap-6 h-auto md:h-5/6 bg-background">
{/* Add Diary Card */}
<Card className="col-span-1 bg-card shadow-lg rounded-lg border border-border">
<CardHeader>
<CardTitle className="text-lg font-semibold text-card-foreground">
Expand Down Expand Up @@ -153,51 +159,68 @@ export default function Diary() {
<Input
type="text"
placeholder="Tags"
value={form.tags.join(", ")} // Join tags for display
value={form.tags.join(", ")}
onChange={(e) =>
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"
/>
</div>
</div>
{loading ? (
<Button
className="my-4 w-full bg-muted text-muted-foreground rounded-md py-2"
disabled
>
Loading...
</Button>
) : (
<Button className="my-4 w-full bg-primary text-primary-foreground hover:bg-primary/90 rounded-md py-2">
Add
</Button>
)}
<Button
className="my-4 w-full bg-primary text-primary-foreground hover:bg-primary/90 rounded-md py-2"
disabled={loading}
>
{loading ? "Loading..." : "Add"}
</Button>
</form>
</CardContent>
</Card>

{/* List of Diaries */}
<Card className="col-span-1 md:col-span-3 p-4 gap-4 bg-card shadow-lg border border-border rounded-lg">
{diaries.map((diary, index) => (
<Input
type="text"
placeholder="Search diaries by title, content, or tags"
value={searchQuery}
onChange={(e) => 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"
/>

<div className="flex flex-wrap gap-2 mb-4">
{uniqueTags.map((tag) => (
<Button
key={tag}
onClick={() => setSearchQuery(tag)}
className="bg-secondary text-secondary-foreground hover:bg-secondary/90 rounded-md px-3 py-1"
>
{tag}
</Button>
))}
</div>

{filteredDiaries.map((diary, index) => (
<Card
key={index}
className="mb-4 bg-card shadow-md border border-border rounded-lg"
className="mb-4 bg-zinc-900 shadow-md border border-border rounded-lg"
>
<CardHeader>
<CardTitle className="text-xl font-semibold text-card-foreground">
<CardTitle className="text-xl font-semibold text-black bg-white p-2 rounded-lg w-fit ml-5 mt-3 mb-3">
{diary.title}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">{diary.content}</p>
<p className="text-muted-foreground mt-2">
Tags: {diary.tags.join(", ")}
</p>{" "}
{/* Display tags */}
<p className="text-muted-foreground border p-2">{diary.content}</p>
<div className="flex flex-wrap gap-2 mt-2">
{diary.tags.map((tag, index) => (
<span
key={index}
className="bg-zinc-600 text-secondary-foreground px-3 py-1 rounded-lg text-sm font-medium"
>
{tag}
</span>
))}
</div>
</CardContent>
<div className="flex justify-between px-6 pb-4">
<Button
Expand All @@ -207,9 +230,7 @@ export default function Diary() {
Edit
</Button>
<Button
onClick={() => {
handleDelete(diary.id);
}}
onClick={() => handleDelete(diary.id)}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90 rounded-md px-4 py-2"
>
Delete
Expand All @@ -219,7 +240,6 @@ export default function Diary() {
))}
</Card>

{/* Modal for Editing */}
{showModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50">
<div className="bg-card rounded-lg shadow-lg p-6 w-11/12 md:w-1/3">
Expand Down Expand Up @@ -265,31 +285,22 @@ export default function Diary() {
</Label>
<Input
type="text"
value={currentDiary?.tags.join(", ") || ""} // Join tags for display
value={currentDiary?.tags?.join(", ") || ""}
onChange={(e) =>
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"
/>
</div>
</div>
<div className="flex justify-end gap-4 mt-4">
<Button
type="button"
className="bg-muted text-muted-foreground hover:bg-muted/90 rounded-md px-4 py-2"
onClick={() => setShowModal(false)}
>
Cancel
</Button>
<Button className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-md px-4 py-2">
Save
</Button>
</div>
<Button className="my-4 w-full bg-primary text-primary-foreground hover:bg-primary/90 rounded-md py-2">
Update
</Button>
</form>
</CardContent>
</div>
Expand Down
Loading

0 comments on commit 5921185

Please sign in to comment.