Skip to content

Commit d155ae3

Browse files
Merge pull request #510 from MeetDOD/issue-500
Feat: Added showing admin which user marked favorite to which post successfully issue 500
2 parents 7178ca3 + 095deae commit d155ae3

File tree

6 files changed

+153
-2
lines changed

6 files changed

+153
-2
lines changed

admin/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Graphs from "./pages/Graphs";
1515
import ContactMessages from "./pages/ContactMessages";
1616
import Comments from "./pages/Comments";
1717
import Layout from "./components/Layout";
18+
import Favorites from "./pages/Favorites";
1819
import Reactions from "./pages/Reactions";
1920
// import axios from "axios";
2021
// axios.defaults.baseURL = "http://localhost:3001/";
@@ -47,6 +48,7 @@ function App() {
4748
<Route path="statistics" element={<Graphs />} />
4849
<Route path="contactmessages" element={<ContactMessages />} />
4950
<Route path="comments" element={<Comments />} />
51+
<Route path="favorites" element={<Favorites />} />
5052
<Route path="reactions" element={<Reactions />} />
5153
<Route path="*" element={<PageNotFound />} />
5254
</Routes>

admin/src/components/SideBar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { VscGraphScatter } from "react-icons/vsc";
99
import { MdOutlineAttachEmail } from "react-icons/md";
1010
import { FaRegComments } from "react-icons/fa";
1111
import GoogleTranslate from './GoogleTranslate';
12+
import { RiHeartsLine } from "react-icons/ri";
1213
import { VscReactions } from "react-icons/vsc";
1314

1415
const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleSidebar: () => void }) => {
@@ -36,6 +37,7 @@ const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleS
3637
<Link to="/admin/posts" className={linkClasses('/admin/posts')}><IoNewspaperOutline size={23} className='mr-3'/>All Posts</Link>
3738
<Link to="/admin/contactmessages" className={linkClasses('/admin/contactmessages')}><MdOutlineAttachEmail size={23} className='mr-3'/>Messages</Link>
3839
<Link to="/admin/comments" className={linkClasses('/admin/comments')}><FaRegComments size={23} className='mr-3'/>Comments</Link>
40+
<Link to="/admin/favorites" className={linkClasses('/admin/favorites')}><RiHeartsLine size={23} className='mr-3'/>Favorites</Link>
3941
<Link to="/admin/reactions" className={linkClasses('/admin/reactions')}><VscReactions size={23} className='mr-3'/>Reactions</Link>
4042
<Link to="/admin/statistics" className={linkClasses('/admin/statistics')}><VscGraphScatter size={23} className='mr-3'/>Statistics</Link>
4143
</nav>

admin/src/pages/Favorites.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { useState, useEffect } from "react";
2+
import axios from "axios";
3+
import { useRecoilValue } from "recoil";
4+
import { tokenState } from "../store/atoms/auth";
5+
import { IFavoritePost } from "../types";
6+
import { ColorRing } from 'react-loader-spinner';
7+
import { RiHeartsFill } from "react-icons/ri";
8+
9+
const Favorites = () => {
10+
const [favoritePosts, setFavoritePosts] = useState<IFavoritePost[]>([]);
11+
const [loading, setLoading] = useState(true);
12+
const token = useRecoilValue(tokenState);
13+
14+
document.title = "Style Share Admin | Users Favorites 💓";
15+
16+
useEffect(() => {
17+
const fetchFavoritePosts = async () => {
18+
try {
19+
const response = await axios.get("/api/v1/admin/favorites", {
20+
headers: {
21+
Authorization: `Bearer ${token}`,
22+
},
23+
});
24+
setFavoritePosts(response.data.favorites);
25+
setLoading(false);
26+
} catch (error) {
27+
console.error("Error fetching favorite posts:", error);
28+
setLoading(true);
29+
}
30+
};
31+
32+
fetchFavoritePosts();
33+
}, [token]);
34+
35+
return (
36+
<div>
37+
<div className="flex-1 flex flex-col lg:ml-80">
38+
<div className="mx-5 mb-5">
39+
<span className="flex items-center text-xl font-bold decoration-sky-500 decoration-dotted underline">
40+
<div className='inline-block p-2 text-white bg-[#000435] rounded-lg mr-2'>
41+
<RiHeartsFill size={23} />
42+
</div>
43+
Users Favorite Posts
44+
</span>
45+
</div>
46+
{loading ?
47+
<div className="flex justify-center items-center h-80">
48+
<ColorRing
49+
visible={true}
50+
height="100"
51+
width="100"
52+
colors={['#000435', 'rgb(14 165 233)', 'rgb(243 244 246)', '#000435', 'rgb(14 165 233)']}
53+
/>
54+
</div>
55+
:
56+
<div className="mx-5 lg:mr-11 overflow-x-auto shadow-md rounded-xl mb-5">
57+
<table className="w-full rtl:text-right text-gray-500 dark:text-gray-400">
58+
<thead className="text-xs md:text-sm text-white uppercase bg-sky-500 text-center">
59+
<tr>
60+
<th scope="col" className="px-8 py-3 text-start">Photo</th>
61+
<th scope="col" className="px-8 py-3 text-start">Name</th>
62+
<th scope="col" className="px-6 py-3">Post Title</th>
63+
<th scope="col" className="px-6 py-3">Marked At</th>
64+
</tr>
65+
</thead>
66+
<tbody>
67+
{favoritePosts.map(favorite => (
68+
<tr key={favorite.id} className="text-xs md:text-sm text-center border-b bg-[#000435] border-sky-500 hover:bg-blue-950 hover:text-white">
69+
<td className="pl-7">
70+
<img className="h-10 w-10 rounded-full" src={`https://ui-avatars.com/api/?name=${favorite.user.username}&background=0ea5e9&color=fff&rounded=true&bold=true`} alt="profile-pic" />
71+
</td>
72+
<td className="px-8 py-4 font-semibold">
73+
<div className="flex flex-col items-start">
74+
<span className="font-bold">{favorite.user.username}</span>
75+
<span className="font-thin text-gray-300">{favorite.user.email}</span>
76+
</div>
77+
</td>
78+
<td className="px-8 py-4 font-semibold">{favorite.post.title}</td>
79+
<td className="px-8 py-4 font-semibold">{new Date(favorite.createdAt).toLocaleDateString()}</td>
80+
</tr>
81+
))}
82+
</tbody>
83+
</table>
84+
</div>
85+
}
86+
</div>
87+
</div>
88+
);
89+
};
90+
91+
export default Favorites;

admin/src/types.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ export interface IPost {
1414
},
1515
comments:IComment[]
1616
reactions:[];
17-
favoritePosts: [];
17+
favoritePosts: {
18+
user: IUser;
19+
}[];
1820
userReaction: 'Like' | 'Celebrate' | 'Support' | 'Love' | 'Insightful' | 'Funny' | null;
1921
}
2022

@@ -58,6 +60,21 @@ export interface IContactMessage{
5860
createdAt:number
5961
}
6062

63+
export interface IFavoritePost {
64+
id: string;
65+
createdAt: number;
66+
user: {
67+
id: string;
68+
username: string;
69+
email: string;
70+
};
71+
post: {
72+
id: string;
73+
title: string;
74+
description: string;
75+
};
76+
}
77+
6178
export interface IReaction {
6279
type: 'Like' | 'Celebrate' | 'Support' | 'Love' | 'Insightful' | 'Funny';
6380
createdAt: number;

backend/src/routes/admin/controller.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,4 +514,41 @@ export const getPostReactionsController = async (req: Request, res: Response) =>
514514
error: "An unexpected exception occurred!",
515515
});
516516
}
517+
};
518+
519+
export const getFavoritesController = async (req: Request, res: Response) => {
520+
try {
521+
const favorites = await prisma.favorite.findMany({
522+
select: {
523+
id: true,
524+
createdAt: true,
525+
user: {
526+
select: {
527+
id: true,
528+
username: true,
529+
email: true
530+
}
531+
},
532+
post: {
533+
select: {
534+
id: true,
535+
title: true,
536+
description: true
537+
}
538+
}
539+
},
540+
orderBy: {
541+
createdAt: 'desc'
542+
}
543+
});
544+
545+
res.status(200).json({
546+
message: "Successfully fetched favorite posts!",
547+
favorites,
548+
});
549+
} catch (error) {
550+
res.status(500).json({
551+
error: "An unexpected exception occurred!",
552+
});
553+
}
517554
};

backend/src/routes/admin/route.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Router} from 'express';
2-
import { getPostReactionsController,adminLoginController, adminProfileController, allUserForAdmin, blockUserController, unblockUserController, getAdminPostsController, getAdminTrendingPostsController, getAdminStatsController, getGraphsStatsController, updatePostController, deletePostController, getPostByIdController, getAllContactMessages, deleteCommentController } from './controller';
2+
import { getPostReactionsController,getFavoritesController,adminLoginController, adminProfileController, allUserForAdmin, blockUserController, unblockUserController, getAdminPostsController, getAdminTrendingPostsController, getAdminStatsController, getGraphsStatsController, updatePostController, deletePostController, getPostByIdController, getAllContactMessages, deleteCommentController } from './controller';
33
import { isAdmin } from '../../middleware/adminAuth';
44

55
const adminRouter = Router();
@@ -34,4 +34,6 @@ adminRouter.delete('/comments/delete/:commentId', isAdmin, deleteCommentControll
3434

3535
adminRouter.get('/getreactions', isAdmin, getPostReactionsController);
3636

37+
adminRouter.get('/favorites', isAdmin, getFavoritesController);
38+
3739
export default adminRouter;

0 commit comments

Comments
 (0)