Skip to content

Commit e7464fd

Browse files
committed
Merge branch 'origin/dev' into addmembermodal/juryyy and resolve conflicts in actions.ts
2 parents f7f85b9 + 5e38e51 commit e7464fd

6 files changed

Lines changed: 445 additions & 299 deletions

File tree

‎apps/codebility/app/home/feeds/_components/Feed.tsx‎

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,14 @@ export default function Feed({
4040
// Filter posts
4141
const filteredPosts = useMemo(() => {
4242
if (!searchQuery || searchQuery.trim() === "") return allPosts;
43-
43+
4444
const query = searchQuery.toLowerCase();
4545
return allPosts.filter((post) => {
46-
// Always include system post
4746
if (post.id === "00000000-0000-0000-0000-000000000001") return true;
48-
47+
4948
const titleMatch = post.title?.toLowerCase().includes(query);
50-
const firstNameMatch = post.author_id?.first_name
51-
?.toLowerCase()
52-
.includes(query);
53-
const lastNameMatch = post.author_id?.last_name
54-
?.toLowerCase()
55-
.includes(query);
49+
const firstNameMatch = post.author_id?.first_name?.toLowerCase().includes(query);
50+
const lastNameMatch = post.author_id?.last_name?.toLowerCase().includes(query);
5651
return titleMatch || firstNameMatch || lastNameMatch;
5752
});
5853
}, [allPosts, searchQuery]);
@@ -61,7 +56,7 @@ export default function Feed({
6156
const sortedPosts = useMemo(() => {
6257
const sorted = [...filteredPosts];
6358
sorted.sort((a, b) => {
64-
// Always keep system post at the top regardless of sorting
59+
// Always keep system post at the top
6560
if (a.id === "00000000-0000-0000-0000-000000000001") return -1;
6661
if (b.id === "00000000-0000-0000-0000-000000000001") return 1;
6762

@@ -85,6 +80,9 @@ export default function Feed({
8580
aVal = a.comment_count ?? 0;
8681
bVal = b.comment_count ?? 0;
8782
break;
83+
default:
84+
aVal = new Date(a.created_at).getTime();
85+
bVal = new Date(b.created_at).getTime();
8886
}
8987

9088
if (aVal < bVal) return sortOrder === "asc" ? -1 : 1;
@@ -96,6 +94,7 @@ export default function Feed({
9694

9795
const visiblePosts = sortedPosts.slice(0, visibleCount);
9896

97+
// Reset visible count when search changes
9998
useEffect(() => {
10099
setVisibleCount(POSTS_PER_PAGE);
101100
}, [searchQuery]);
@@ -122,18 +121,15 @@ export default function Feed({
122121
}, [sortedPosts.length]);
123122

124123
const handleDeletePost = (deletedPostId: string | number) => {
125-
// Prevent deletion of system post
126124
if (deletedPostId === "00000000-0000-0000-0000-000000000001") return;
127-
128125
useFeedsStore.setState((state) => ({
129126
posts: state.posts.filter((post) => post.id !== deletedPostId),
130127
}));
131128
};
132129

133130
return (
134131
<>
135-
{/* Posts grid */}
136-
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
132+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
137133
{isFetchingPosts
138134
? Array.from({ length: 6 }).map((_, i) => (
139135
<PostCardSkeleton key={i} />
@@ -148,7 +144,7 @@ export default function Feed({
148144
))}
149145
</div>
150146

151-
{/* Loader */}
147+
{/* Infinite scroll loader */}
152148
{!isFetchingPosts && visibleCount < sortedPosts.length && (
153149
<div
154150
ref={loaderRef}

‎apps/codebility/app/home/feeds/_components/PostCard.tsx‎

Lines changed: 51 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
import { useEffect, useState } from "react";
22
import Image from "next/image";
3-
import Link from "next/link";
43
import FeedPostModal from "@/components/modals/FeedPostModal";
54
import { Box } from "@/components/shared/dashboard";
65
import { defaultAvatar } from "@/public/assets/images";
76
import { useUserStore } from "@/store/codev-store";
87
import { format } from "date-fns";
9-
import { ArrowBigUp, MessageSquare } from "lucide-react";
108
import toast from "react-hot-toast";
119

1210
import { deletePost } from "../_services/action";
1311
import { PostType } from "../_services/query";
1412
import { DeleteDialog } from "./DeleteDialog";
1513
import PostCommentCount from "./PostCommentCount";
1614
import PostTags from "./PostTags";
17-
import PostUpvote from "./PostUpvote";
15+
import PostUpvote from "./PostUpvote";
1816

1917
interface PostProps {
2018
post: PostType;
@@ -30,21 +28,11 @@ export default function Post({ post, isAdmin, onDelete }: PostProps) {
3028
const { user } = useUserStore();
3129

3230
useEffect(() => {
33-
const checkIfAuthor = async () => {
34-
if (!user) return;
35-
if (user.id === post.author_id?.id) {
36-
setIsAuthor(true);
37-
} else {
38-
setIsAuthor(false);
39-
}
40-
};
41-
42-
if (user) {
43-
checkIfAuthor();
44-
}
31+
if (!user) return;
32+
setIsAuthor(user.id === post.author_id?.id);
4533
}, [user, post.author_id?.id]);
4634

47-
const openDeleteDialog = async (e: React.MouseEvent) => {
35+
const openDeleteDialog = (e: React.MouseEvent) => {
4836
e.stopPropagation();
4937
e.preventDefault();
5038
setShowDeleteDialog(true);
@@ -54,9 +42,7 @@ export default function Post({ post, isAdmin, onDelete }: PostProps) {
5442
try {
5543
setIsDeleting(true);
5644
await deletePost(post.id);
57-
if (onDelete) {
58-
onDelete(post.id);
59-
}
45+
if (onDelete) onDelete(post.id);
6046
toast.success("Post deleted successfully!");
6147
} catch (error) {
6248
toast.error("Failed to delete post.");
@@ -70,79 +56,75 @@ export default function Post({ post, isAdmin, onDelete }: PostProps) {
7056
const openModal = () => setIsModalOpen(true);
7157
const closeModal = () => setIsModalOpen(false);
7258

59+
const isSystemPost = post.id === "00000000-0000-0000-0000-000000000001";
60+
7361
return (
7462
<div>
75-
<Box
76-
className="group relative flex h-[440px] cursor-pointer flex-col justify-between rounded-xl p-3"
63+
{/* Card */}
64+
<div
7765
onClick={openModal}
66+
className="group relative flex h-[440px] cursor-pointer flex-col justify-between overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm transition-all duration-200 hover:border-cyan-400 hover:shadow-md dark:border-gray-700 dark:bg-gray-800 dark:hover:border-cyan-500/40 dark:hover:shadow-lg dark:hover:shadow-cyan-500/5"
7867
>
79-
{/* Delete Button in top-right */}
80-
{(isAdmin || isAuthor) && post.id !== "00000000-0000-0000-0000-000000000001" && (
68+
{/* Delete button */}
69+
{(isAdmin || isAuthor) && !isSystemPost && (
8170
<button
82-
className="invisible absolute right-2 top-2 rounded bg-red-500 px-2 py-1 text-white hover:bg-red-600 group-hover:visible"
71+
className="invisible absolute right-2 top-2 z-10 rounded-lg bg-red-500 px-2 py-1 text-xs font-medium text-white transition hover:bg-red-600 group-hover:visible"
8372
onClick={openDeleteDialog}
8473
aria-label="Delete post"
8574
>
8675
✕
8776
</button>
8877
)}
8978

90-
<div className="mx-4 mt-2 h-[200px]">
91-
<div className="flex items-center justify-between">
92-
<div className="mb-2 flex items-center">
93-
<Image
94-
src={
95-
post.author_id?.image_url ||
96-
(post.id === "00000000-0000-0000-0000-000000000001" ? "/favicon.ico" : defaultAvatar)
97-
}
98-
alt={`${post.author_id?.first_name || "Codebility"}'s profile`}
99-
className="mr-4 h-12 w-12 rounded-full object-cover"
100-
width={30}
101-
height={30}
102-
/>
103-
<div className="flex-1">
104-
<h2 className="font-semibold text-gray-800 dark:text-gray-100">
105-
{post.author_id?.first_name
106-
? `${post.author_id?.first_name} ${post.author_id?.last_name}`
107-
: "Codebility"}
108-
</h2>
109-
</div>
110-
</div>
111-
</div>
112-
113-
<div className="h-[60px] flex-grow">
114-
<p className="mt-2 line-clamp-2 text-xl font-semibold leading-snug text-gray-700 dark:text-gray-300">
115-
{post.title}
116-
</p>
117-
</div>
118-
<div className="mb-2">
119-
<PostTags post={post} minimal={true} />
120-
</div>
121-
122-
<p className="text-sm text-gray-500 dark:text-gray-400">
123-
{format(new Date(post.created_at), "MMM d, yyyy")}
79+
{/* Title */}
80+
<div className="px-4 pt-4">
81+
<p className="line-clamp-2 text-base font-semibold leading-snug text-gray-800 dark:text-gray-100">
82+
{post.title}
12483
</p>
12584
</div>
12685

127-
<div className="my-2 h-60 w-full overflow-hidden rounded-sm">
86+
{/* Thumbnail image — takes up most of the card */}
87+
<div className="mx-4 mt-2 flex-1 overflow-hidden rounded-xl">
12888
<img
12989
src={post.image_url || "/assets/images/bg-certificate.png"}
13090
alt="Post image"
131-
className="h-full w-full object-cover"
91+
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.02]"
13292
/>
13393
</div>
13494

135-
<div className="mx-4 flex items-center space-x-4 text-gray-600 dark:text-gray-400">
136-
<PostUpvote post={post} />
95+
{/* Footer: author + date on left, upvotes + comments on right */}
96+
<div className="flex items-center justify-between border-t border-gray-100 px-4 py-3 dark:border-gray-700">
97+
<div className="flex items-center gap-2">
98+
<Image
99+
src={
100+
post.author_id?.image_url ||
101+
(isSystemPost ? "/favicon.ico" : defaultAvatar)
102+
}
103+
alt={`${post.author_id?.first_name || "Codebility"}'s profile`}
104+
className="h-7 w-7 rounded-full object-cover ring-1 ring-gray-200 dark:ring-white/10"
105+
width={28}
106+
height={28}
107+
/>
108+
<div className="flex flex-col">
109+
<span className="text-xs font-semibold text-gray-700 dark:text-gray-200">
110+
{post.author_id?.first_name
111+
? `${post.author_id.first_name} ${post.author_id.last_name}`
112+
: "Codebility"}
113+
</span>
114+
<span className="text-[10px] text-gray-400 dark:text-gray-500">
115+
{format(new Date(post.created_at), "MMM d, yyyy")}
116+
</span>
117+
</div>
118+
</div>
137119

138-
<PostCommentCount post={post} />
120+
<div className="flex items-center gap-3 text-gray-500 dark:text-gray-400">
121+
<PostUpvote post={post} />
122+
<PostCommentCount post={post} />
123+
</div>
139124
</div>
140-
</Box>
141-
<FeedPostModal
142-
isOpen={isModalOpen}
143-
onClose={closeModal}
144-
postId={post.id}
145-
/>
125+
</div>
126+
127+
<FeedPostModal isOpen={isModalOpen} onClose={closeModal} postId={post.id} />
146128
<DeleteDialog
147129
isOpen={showDeleteDialog}
148130
onClose={() => setShowDeleteDialog(false)}

‎apps/codebility/app/home/feeds/_components/SocialPointsCard.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ export default function SocialPointsCard() {
5454
) : (
5555
<>
5656
<p className="bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-2xl font-bold text-transparent">
57-
{points ?? 0}
57+
{points ?? 0}
58+
<span className="text-xs text-gray-500">points</span>
5859
</p>
59-
<p className="text-xs text-gray-500">points</p>
6060
</>
6161
)}
6262
</div>

0 commit comments

Comments
 (0)