Skip to content

Commit 694367c

Browse files
committed
resolve conflicts
2 parents 06fc36f + 94f37cc commit 694367c

2 files changed

Lines changed: 252 additions & 53 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { useEffect, useState } from "react";
2+
import Image from "next/image";
3+
import Link from "next/link";
4+
import FeedPostModal from "@/components/modals/FeedPostModal";
5+
import { Box } from "@/components/shared/dashboard";
6+
import { defaultAvatar } from "@/public/assets/images";
7+
import { useUserStore } from "@/store/codev-store";
8+
import { format } from "date-fns";
9+
import { ArrowBigUp } from "lucide-react";
10+
import toast from "react-hot-toast";
11+
12+
import { deletePost } from "../_services/action";
13+
import { PostType } from "../_services/query";
14+
import PostUpvote from "./PostUpvote";
15+
16+
interface PostProps {
17+
post: PostType;
18+
isAdmin?: boolean;
19+
onDelete?: (deletedPostId: string | number) => void;
20+
}
21+
22+
export default function Post({ post, isAdmin, onDelete }: PostProps) {
23+
const [isModalOpen, setIsModalOpen] = useState(false);
24+
const [isAuthor, setIsAuthor] = useState(false);
25+
const { user } = useUserStore();
26+
27+
useEffect(() => {
28+
const checkIfAuthor = async () => {
29+
if (!user) return;
30+
if (user.id === post.author_id?.id) {
31+
setIsAuthor(true);
32+
} else {
33+
setIsAuthor(false);
34+
}
35+
};
36+
37+
if (user) {
38+
checkIfAuthor();
39+
}
40+
}, [user]);
41+
42+
const handleDelete = async (e: React.MouseEvent) => {
43+
e.stopPropagation();
44+
e.preventDefault();
45+
try {
46+
await deletePost(post.id);
47+
toast.success("Post deleted successfully!");
48+
49+
if (onDelete) {
50+
onDelete(post.id);
51+
}
52+
} catch (error) {
53+
toast.error("Failed to delete post.");
54+
console.error("Failed to delete post:", error);
55+
}
56+
};
57+
58+
const openModal = () => setIsModalOpen(true);
59+
const closeModal = () => setIsModalOpen(false);
60+
return (
61+
<div>
62+
<Box
63+
className="group relative flex h-[400px] cursor-pointer flex-col justify-between rounded-xl p-3"
64+
onClick={openModal}
65+
>
66+
{/* Delete Button in top-right */}
67+
{(isAdmin || isAuthor) && (
68+
<button
69+
className="invisible absolute right-2 top-2 rounded bg-red-500 px-2 py-1 text-white hover:bg-red-600 group-hover:visible"
70+
onClick={handleDelete}
71+
aria-label="Delete post"
72+
>
73+
✕
74+
</button>
75+
)}
76+
77+
<div className="mx-4 mt-2 h-[200px]">
78+
<div className="flex items-center justify-between">
79+
<div className="mb-2 flex items-center">
80+
<Image
81+
src={post.author_id?.image_url || defaultAvatar}
82+
alt={`${post.author_id?.first_name}'s profile`}
83+
className="mr-4 h-12 w-12 rounded-full object-cover"
84+
width={40}
85+
height={40}
86+
/>
87+
<div className="flex-1">
88+
<h2 className="font-semibold text-gray-800 dark:text-gray-100">
89+
{post.author_id
90+
? `${post.author_id?.first_name} ${post.author_id?.last_name}`
91+
: "Codebility"}
92+
</h2>
93+
</div>
94+
</div>
95+
</div>
96+
97+
<div className="h-[60px] flex-grow">
98+
<p className="mt-2 line-clamp-2 text-xl font-semibold text-gray-700 dark:text-gray-300">
99+
{post.title}
100+
</p>
101+
</div>
102+
103+
<p className="text-sm text-gray-500 dark:text-gray-400">
104+
{format(new Date(post.created_at), "MMM d, yyyy")}
105+
</p>
106+
</div>
107+
108+
<div className="h-60 w-full overflow-hidden rounded-sm">
109+
<img
110+
src={post.image_url || "/assets/images/bg-certificate.png"}
111+
alt="Post image"
112+
className="h-full w-full object-cover"
113+
/>
114+
</div>
115+
116+
<div className="mx-4 mt-4 flex items-center space-x-4 text-gray-600 dark:text-gray-400">
117+
<PostUpvote post={post} />
118+
</div>
119+
</Box>
120+
<FeedPostModal
121+
isOpen={isModalOpen}
122+
onClose={closeModal}
123+
postId={post.id}
124+
/>
125+
</div>
126+
);
127+
}

‎apps/codebility/app/home/feeds/_services/action.ts‎

Lines changed: 125 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -207,93 +207,92 @@ export const AddPostUpvote = async (postId: string, userId: string) => {
207207
try {
208208
const supabase = await createClientServerComponent();
209209

210-
// Fetch current upvoters_id array
211-
const { data: post, error: fetchError } = await supabase
212-
.from("posts")
213-
.select("upvoters_id")
214-
.eq("id", postId)
215-
.single();
210+
const { data: postUpvote, error: fetchError } = await supabase
211+
.from("post_upvotes")
212+
.select("*")
213+
.eq("post_id", postId)
214+
.eq("upvoter_id", userId)
215+
.maybeSingle();
216216

217217
if (fetchError) throw fetchError;
218218

219-
const currentUpvoters = post.upvoters_id || [];
220-
221219
// Avoid duplicate upvotes
222-
if (currentUpvoters.includes(userId)) {
220+
if (postUpvote) {
223221
return { message: "User has already upvoted this post." };
224222
}
225223

226-
const updatedUpvoters = [...currentUpvoters, userId];
227-
228-
// Update the post with new upvoter
229-
const { data: updatedPost, error: updateError } = await supabase
230-
.from("posts")
231-
.update({ upvoters_id: updatedUpvoters })
232-
.eq("id", postId)
233-
.select();
224+
// Add post upvote
225+
const { data: newUpvote, error: insertError } = await supabase
226+
.from("post_upvotes")
227+
.insert([{ post_id: postId, upvoter_id: userId }])
228+
.select()
229+
.single();
234230

235-
if (updateError) throw updateError;
231+
if (insertError) throw insertError;
236232

237-
return updatedPost;
233+
return newUpvote;
238234
} catch (error) {
239-
console.error(error);
235+
console.error("Failed to add upvote:", error);
240236
throw error;
241237
}
242238
};
243239

244240
export const removePostUpvote = async (postId: string, userId: string) => {
245-
try {
241+
try {
246242
const supabase = await createClientServerComponent();
247243

248-
// Fetch current upvoters_id array
249-
const { data: post, error: fetchError } = await supabase
250-
.from("posts")
251-
.select("upvoters_id")
252-
.eq("id", postId)
253-
.single();
244+
const { error } = await supabase
245+
.from("post_upvotes")
246+
.delete()
247+
.eq("post_id", postId)
248+
.eq("upvoter_id", userId);
254249

255-
if (fetchError) throw fetchError;
250+
if (error) throw error;
256251

257-
const currentUpvoters = post.upvoters_id || [];
258252

259-
// If user hasn't upvoted, nothing to remove
260-
if (!currentUpvoters.includes(userId)) {
261-
return { message: "User hasn't upvoted this post." };
262-
}
253+
} catch (error) {
254+
console.error("Failed to remove upvote:", error);
255+
throw error;
256+
}
257+
};
263258

264-
const updatedUpvoters = currentUpvoters.filter(id => id !== userId);
259+
export const hasUserUpvoted = async (postId: string, userId: string): Promise<boolean> => {
260+
try {
261+
const supabase = await createClientServerComponent();
265262

266-
// Update the post with new upvoter array
267-
const { data: updatedPost, error: updateError } = await supabase
268-
.from("posts")
269-
.update({ upvoters_id: updatedUpvoters })
270-
.eq("id", postId)
271-
.select();
263+
const { data: postUpvote, error } = await supabase
264+
.from("post_upvotes")
265+
.select("*")
266+
.eq("post_id", postId)
267+
.eq("upvoter_id", userId)
268+
.maybeSingle();
272269

273-
if (updateError) throw updateError;
270+
if (error) throw error;
274271

275-
return updatedPost;
272+
if (postUpvote) {
273+
return true;
274+
} else {
275+
return false;
276+
}
276277
} catch (error) {
277278
console.error(error);
278279
throw error;
279280
}
280281
};
281282

282-
export const hasUserUpvoted = async (postId: string, userId: string): Promise<boolean> => {
283-
try {
283+
export const countUpvotes = async (postId: string): Promise<number> => {
284+
try {
284285
const supabase = await createClientServerComponent();
285286

286-
const { data: post, error } = await supabase
287-
.from("posts")
288-
.select("upvoters_id")
289-
.eq("id", postId)
290-
.single();
287+
const { count, error } = await supabase
288+
.from("post_upvotes")
289+
.select("*", { count: "exact", head: true })
290+
.eq("post_id", postId);
291291

292292
if (error) throw error;
293293

294-
const upvoters: string[] = post.upvoters_id || [];
295-
296-
return upvoters.includes(userId);
294+
if (count) return count
295+
else return 0;
297296
} catch (error) {
298297
console.error(error);
299298
throw error;
@@ -410,6 +409,7 @@ export async function deletePostContentImages(
410409
}
411410
}
412411

412+
<<<<<<< HEAD
413413
export const getSocialPoints = async (userId: string): Promise<number|null> => {
414414
const supabase = await createClientServerComponent();
415415

@@ -427,4 +427,76 @@ export const getSocialPoints = async (userId: string): Promise<number|null> => {
427427
console.error("Failed to fetch social points:", error);
428428
throw error;
429429
}
430-
};
430+
};
431+
=======
432+
433+
export async function createComment(
434+
post_id: string,
435+
commenter_id: string,
436+
content: string,
437+
) {
438+
const supabase = await createClientServerComponent();
439+
440+
try {
441+
const { data, error } = await supabase
442+
.from("post_comments")
443+
.insert([{ post_id, commenter_id, content }])
444+
.select()
445+
.single();
446+
447+
if (error) throw error;
448+
449+
return data;
450+
} catch (error) {
451+
console.error("Comment creation failed:", error);
452+
throw error;
453+
}
454+
}
455+
456+
export async function getPostComments(post_id: string) {
457+
const supabase = await createClientServerComponent();
458+
459+
try {
460+
const { data, error } = await supabase
461+
.from("post_comments")
462+
.select(`
463+
id,
464+
content,
465+
created_at,
466+
commenter: commenter_id!inner (
467+
id,
468+
first_name,
469+
last_name,
470+
image_url
471+
)
472+
`)
473+
.eq("post_id", post_id)
474+
.order("created_at", { ascending: true });
475+
476+
if (error) throw error;
477+
478+
return data;
479+
} catch (error) {
480+
console.error("Failed to fetch post comments:", error);
481+
throw error;
482+
}
483+
}
484+
485+
export async function deletePostComment(comment_id: string) {
486+
const supabase = await createClientServerComponent();
487+
488+
try {
489+
const { error } = await supabase
490+
.from("post_comments")
491+
.delete()
492+
.eq("id", comment_id);
493+
494+
if (error) throw error;
495+
496+
return { success: true };
497+
} catch (error) {
498+
console.error("Failed to delete post comment:", error);
499+
throw error;
500+
}
501+
}
502+
>>>>>>> dev

0 commit comments

Comments
 (0)