diff --git a/backend/database/models/postModel.js b/backend/database/models/postModel.js
index e09c362..3d3f29c 100644
--- a/backend/database/models/postModel.js
+++ b/backend/database/models/postModel.js
@@ -2,19 +2,12 @@ import { Schema, model } from "mongoose";
const postModel = new Schema(
{
- postID: String,
- userID: { type: String, ref: "users" },
- postTitle: String,
- image_id: String,
- content: String,
- likes: {
- type: [String], // array of user ids
- default: [],
- },
- comments: {
- type: [String],
- default: [],
- },
+ createdBy: { type: String, ref: "users" },
+ title: { type: String, required: true },
+ content: { type: String, required: true },
+ upvote: { type: Number, default: 0 },
+ comments: [{ userId: String, comment: String }],
+ imageUrl: { type: String, default: "" },
},
{ timestamps: true }
);
@@ -32,7 +25,7 @@ export async function findAll() {
export async function findById(id) {
const data = await post.findOne({
- postID: { $eq: id },
+ _id: { $eq: id },
});
if (!data) return null;
return data;
@@ -40,16 +33,16 @@ export async function findById(id) {
export async function getPostbyTitle(postTitle) {
const data = await post.findOne({
- postTitle: { $eq: postTitle },
+ title: { $eq: postTitle },
});
if (!data) return null;
return data;
}
export async function update(id, data) {
- return await post.updateOne({ postID: id }, data);
+ return await post.updateOne({ _id: id }, data);
}
export async function findByIdAndDelete(id) {
- return await post.deleteOne({ postID: id });
+ return await post.deleteOne({ _id: id });
}
diff --git a/backend/routes/api.post.js b/backend/routes/api.post.js
index 43f925d..dded946 100644
--- a/backend/routes/api.post.js
+++ b/backend/routes/api.post.js
@@ -21,6 +21,16 @@ router.get('/', async (req, res) => {
}
})
+router.get("/:id", async (req, res) => {
+ try {
+ const id = req.params.id
+ const post = await findById(id)
+ res.status(200).json(post)
+ } catch (error) {
+ res.status(500).send({ message: error.message });
+ }
+})
+
router.get('/:title', async (req, res) => {
try {
const title = req.params.title
@@ -79,14 +89,18 @@ router.put('/:id/likePost', async (req, res) => {
router.put('/:id/commentPost', async (req, res) => {
try {
- const id = req.params
+ const id = req.params.id
const value = req.body
+ console.log("id", id)
+ console.log("value", value)
- const post = await findById(id)
+ let post = await findById(id)
+ if(!post) return res.status(404).json({ message: 'Post not found' });
+ console.log(value)
post.comments.push(value)
- const updatedPost = await update(id, post)
-
- res.json(updatedPost);
+ console.log(post)
+ post = await update(id, post)
+ res.json(post);
} catch (error) {
res.status(500).send({ message: error.message });
}
diff --git a/frontend/public/dashboard/post.svg b/frontend/public/dashboard/post.svg
new file mode 100644
index 0000000..880817a
--- /dev/null
+++ b/frontend/public/dashboard/post.svg
@@ -0,0 +1,38 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/actions/addFriend-action.ts b/frontend/src/app/actions/addFriend-action.ts
index ce6ee32..53fef20 100644
--- a/frontend/src/app/actions/addFriend-action.ts
+++ b/frontend/src/app/actions/addFriend-action.ts
@@ -7,7 +7,6 @@ export default async function addFriendAction(userId: string, friendId: string)
method: "POST",
headers: {
"Content-Type": "application/json",
- "Cache-Control": "max-age=3600",
},
body: JSON.stringify({ userId, friendId }),
}
diff --git a/frontend/src/app/actions/deleteRoom-action.ts b/frontend/src/app/actions/deleteRoom-action.ts
index 189898e..21888cb 100644
--- a/frontend/src/app/actions/deleteRoom-action.ts
+++ b/frontend/src/app/actions/deleteRoom-action.ts
@@ -7,7 +7,6 @@ export default async function deleteRoomAction(id: string) {
const url = `${process.env.API_URL}/api/rooms/${id}`
const response = await fetch(url, {
method: "DELETE",
- next: { tags: ["delete-rooms"] },
headers: {
"Content-Type": "application/json"
}
diff --git a/frontend/src/app/actions/getUserFriend-action.ts b/frontend/src/app/actions/getUserFriend-action.ts
index afb08de..9566b7c 100644
--- a/frontend/src/app/actions/getUserFriend-action.ts
+++ b/frontend/src/app/actions/getUserFriend-action.ts
@@ -4,7 +4,6 @@ export default async function getUserFriend(username: string) {
`${process.env.API_URL}/api/users/${username}/friends`,
{
method: "GET",
- cache: "force-cache",
next: { tags: ["friends"] },
headers: {
"Content-Type": "application/json",
diff --git a/frontend/src/app/actions/getUserRoom-action.ts b/frontend/src/app/actions/getUserRoom-action.ts
index 24238f4..5912d09 100644
--- a/frontend/src/app/actions/getUserRoom-action.ts
+++ b/frontend/src/app/actions/getUserRoom-action.ts
@@ -8,7 +8,6 @@ export async function getUserRoom(id: string): Promise {
{
method: "GET",
next: { tags: ["rooms"] },
- cache: "force-cache",
headers: {
"Content-Type": "application/json",
},
diff --git a/frontend/src/app/actions/post-action.ts b/frontend/src/app/actions/post-action.ts
new file mode 100644
index 0000000..5e3e090
--- /dev/null
+++ b/frontend/src/app/actions/post-action.ts
@@ -0,0 +1,97 @@
+"use server";
+
+export default async function getAllPostAction() {
+ const response = await fetch(`${process.env.API_URL}/api/post`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ if (!response.ok) {
+ throw new Error("An error occurred while fetching the post");
+ }
+ return await response.json();
+}
+
+interface PostData {
+ title: string;
+ imageUrl: string;
+ content: string;
+ createdBy: string;
+ createdAt: string;
+}
+
+export async function createPostAction(data: PostData) {
+ const response = await fetch(`${process.env.API_URL}/api/post`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ });
+ if (!response.ok) {
+ throw new Error("An error occurred while creating the post");
+ }
+ return await response.json();
+}
+
+export async function getPostByIdAction(id: string) {
+ const response = await fetch(`${process.env.API_URL}/api/post/${id}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ if (!response.ok) {
+ throw new Error("An error occurred while fetching the post");
+ }
+ return await response.json();
+}
+
+export async function deletePostAction(id: string) {
+ const response = await fetch(`${process.env.API_URL}/api/post/${id}`, {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ if (!response.ok) {
+ throw new Error("An error occurred while deleting the post");
+ }
+ return await response.json();
+}
+
+
+export async function updateVote({ id, vote }: { id: string; vote: number }) {
+ const response = await fetch(`${process.env.API_URL}/api/post/${id}`, {
+ method: "PUT",
+ body: JSON.stringify({ upvote: vote, id }),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ console.log("response", response);
+ if (!response.ok) {
+ throw new Error("An error occurred while updating the vote");
+ }
+ console.log(response);
+ return await response.json();
+}
+
+
+export async function addComment(userId: string, { comment, id }: { comment: string, id: string }) {
+ console.log(id, comment)
+ const response = await fetch(`${process.env.API_URL}/api/post/${id}/commentPost`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ comment, userId }),
+ });
+ console.log("response", response);
+ if (!response.ok) {
+ throw new Error("An error occurred while creating the comment");
+ }
+ return await response.json();
+
+}
\ No newline at end of file
diff --git a/frontend/src/app/context/getUserContext.tsx b/frontend/src/app/context/getUserContext.tsx
index 0269592..001cc10 100644
--- a/frontend/src/app/context/getUserContext.tsx
+++ b/frontend/src/app/context/getUserContext.tsx
@@ -54,7 +54,6 @@ export const UserContextProvider: React.FC = ({
`${process.env.NEXT_PUBLIC_API_URL}/api/users/${user.username}/friends`,
{
method: "GET",
- cache: "force-cache",
headers: {
"Content-Type": "application/json",
diff --git a/frontend/src/app/onboarding/actions.ts b/frontend/src/app/onboarding/actions.ts
index fadf9ea..d3b949e 100644
--- a/frontend/src/app/onboarding/actions.ts
+++ b/frontend/src/app/onboarding/actions.ts
@@ -42,7 +42,6 @@ async function createUserData(formData: { userId: string, petpreference: any, fa
method: "POST",
headers: {
"Content-Type": "application/json",
- 'Cache-Control': 'max-age=3600',
},
body: JSON.stringify(formData),
});
@@ -58,7 +57,6 @@ async function fetchAIOnboardingResult(
method: "POST",
headers: {
"Content-Type": "application/json",
- 'Cache-Control': 'max-age=3600',
},
body: JSON.stringify({ message: JSON.stringify(formData) }),
}
diff --git a/frontend/src/app/posts/[id]/page.tsx b/frontend/src/app/posts/[id]/page.tsx
new file mode 100644
index 0000000..1943440
--- /dev/null
+++ b/frontend/src/app/posts/[id]/page.tsx
@@ -0,0 +1,139 @@
+'use client'
+import { FormEvent, useContext, useEffect, useState } from "react";
+import Image from "next/image";
+import { redirect, useParams, useRouter } from "next/navigation";
+import { addComment, deletePostAction, getPostByIdAction, updateVote } from "@/app/actions/post-action";
+import { useUser } from "@clerk/nextjs";
+
+interface PostData {
+ id: string;
+ title: string;
+ content: string;
+ createdBy: string;
+ image_url: string;
+ comments: { comment: string, userId: string }[];
+ upvote: number;
+ createdAt: string;
+}
+
+export default function Post() {
+ const { id } = useParams();
+ const { user } = useUser();
+ const [data, setData] = useState({
+ id: "",
+ title: "",
+ createdBy: "",
+ comments: [],
+ content: "",
+ image_url: "",
+ upvote: 0,
+ createdAt: "",
+ });
+ const [comments, setComments] = useState<{ comment: string, userId: string }[]>([]);
+ const [comment, setComment] = useState("");
+ const router = useRouter();
+ const navigate = useRouter()
+ const [votes, setVotes] = useState(0);
+
+ useEffect(() => {
+ async function fetchPost() {
+ try {
+ const resolve = await getPostByIdAction(`${id}`) as PostData;
+ if(!resolve) {
+ return router.replace("/posts");
+ }
+ setData(resolve);
+ setVotes(resolve.upvote);
+ console.log(resolve)
+ setComments(resolve.comments);
+ console.log(resolve)
+ } catch (error) {
+ redirect("/posts");
+ console.log(error)
+ }
+ }
+ fetchPost();
+ }, [id]);
+ if(!user) {
+ return loading...
+ }
+ const date = new Date(data.createdAt);
+ console.log(comments)
+ const handleUpvote = async () => {
+ setVotes((prevVotes) => prevVotes + 1);
+ await updateVote({ id: id as string, vote: votes + 1 });
+ };
+
+ const handleSubmit = async (event: FormEvent) => {
+ event.preventDefault();
+ setComments((prevComments) => [...prevComments, { comment: comment, userId: user.id }]);
+ await addComment(user.id, { id: id as string, comment: comment });
+ setComment("");
+ };
+
+ return (
+
+
+ Back
+
+
+
+ Posted on{" "}
+ {`${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`}
+
+
{data.title}
+
{data.content || "No content"}
+ {data.image_url && (
+
+ )}
+
+
+
+
+ {comments.map((c, index) => (
+
+ {c.userId}: {c.comment}
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/posts/create/page.tsx b/frontend/src/app/posts/create/page.tsx
new file mode 100644
index 0000000..4db189d
--- /dev/null
+++ b/frontend/src/app/posts/create/page.tsx
@@ -0,0 +1,103 @@
+'use client';
+import { createPostAction } from '@/app/actions/post-action';
+import { useUser } from '@clerk/nextjs';
+import { useRouter } from 'next/navigation';
+import { ChangeEvent, FormEvent, useState } from 'react';
+
+export default function Create() {
+ const navigate = useRouter();
+ const { user } = useUser();
+ const [image, setImage] = useState(null);
+
+ if (!user) {
+ navigate.replace("/");
+ return null;
+ }
+
+ const handleSubmit = async (event: FormEvent) => {
+ event.preventDefault();
+
+ const form = event.target as HTMLFormElement;
+ const title = (form.elements.namedItem('title') as HTMLInputElement).value;
+ const content = (form.elements.namedItem('content') as HTMLTextAreaElement).value;
+ const imageUrl = (form.elements.namedItem('imageUrl') as HTMLInputElement).value;
+
+ const post = {
+ title,
+ createdBy: user.id,
+ content,
+ imageUrl
+ };
+
+ // Save post to the database
+ await createPostAction({
+ title: post.title,
+ content: post.content,
+ imageUrl: post.imageUrl,
+ createdBy: post.createdBy,
+ createdAt: new Date().toISOString(),
+ });
+
+ // Navigate back to the home page
+ navigate.replace("/posts");
+ };
+
+ return (
+
+ );
+}
diff --git a/frontend/src/app/posts/page.tsx b/frontend/src/app/posts/page.tsx
new file mode 100644
index 0000000..55c3032
--- /dev/null
+++ b/frontend/src/app/posts/page.tsx
@@ -0,0 +1,127 @@
+"use client";
+import { useEffect, useState } from "react";
+import getAllPostAction from "../actions/post-action";
+import { redirect, useRouter } from "next/navigation";
+
+interface Post {
+ _id: string;
+ title: string;
+ content: string;
+ image_url: string;
+ upvote: number;
+ createdAt: string;
+}
+
+export default function Posts() {
+ const [posts, setPosts] = useState([]);
+ const [search, setSearch] = useState("");
+ const navigate = useRouter();
+ useEffect(() => {
+ const getPosts = async () => {
+ const response = await getAllPostAction()
+ setPosts(response)
+ };
+ getPosts();
+ }, []);
+
+ async function handleForm(event: React.FormEvent) {
+ event.preventDefault();
+ console.log("searching for", search);
+ if (!search) {
+ setPosts([]);
+ return;
+ }
+ const response = posts.find((post) => post.title.includes(search));
+ setPosts(response ? [response] : []);
+ }
+
+ function handleOrderByTime() {
+ setPosts((prevPosts) =>
+ [...prevPosts].sort(
+ (a, b) =>
+ new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
+ )
+ );
+ }
+
+ function handleOrderByUpvotes() {
+ setPosts((prevPosts) => [...prevPosts].sort((a, b) => b.upvote - a.upvote));
+ }
+
+ return (
+
+ {/* Search Bar */}
+
+
+
+
+
+ {/* Order By Section */}
+
+ Order by:
+
+
+
+
+ {/* Posts List */}
+
+
+ );
+}
diff --git a/frontend/src/app/ui/chat/FriendList.tsx b/frontend/src/app/ui/chat/FriendList.tsx
index 725d530..83d5625 100644
--- a/frontend/src/app/ui/chat/FriendList.tsx
+++ b/frontend/src/app/ui/chat/FriendList.tsx
@@ -8,13 +8,13 @@ import { redirect, useRouter } from "next/navigation";
export default function FriendList({ friends, active, me, rooms }: { rooms: Room[], me: User, active: boolean, friends: User[] }) {
const router = useRouter();
+ console.log(friends)
const handleClick = (user: User) => {
const isValid = rooms.find((room) => room.recipients.some((r) => r.username === user.username));
if(isValid) return router.push(`/chat/${isValid.roomId}`);
const createRoom = async () => {
const fetchRoom = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/rooms`, {
method: "POST",
- cache: "force-cache",
headers: {
"Content-Type": "application/json",
},
diff --git a/frontend/src/app/ui/dashboard/Hero.tsx b/frontend/src/app/ui/dashboard/Hero.tsx
index 6046b29..64121e5 100644
--- a/frontend/src/app/ui/dashboard/Hero.tsx
+++ b/frontend/src/app/ui/dashboard/Hero.tsx
@@ -60,7 +60,6 @@ export default function Hero() {
Welcome back, {user.firstName}!
-
diff --git a/frontend/src/app/ui/navigation/Sidebar.tsx b/frontend/src/app/ui/navigation/Sidebar.tsx
index 326e5da..83602cd 100644
--- a/frontend/src/app/ui/navigation/Sidebar.tsx
+++ b/frontend/src/app/ui/navigation/Sidebar.tsx
@@ -15,6 +15,11 @@ const navigation = [
name: "Favorites",
href: "/dashboard/favorites",
},
+ {
+ name: "posts",
+ icon: "/dashboard/post.svg",
+ href: "/posts",
+ },
{
icon: "/dashboard/chat.svg",
name: "Chat",