Skip to content

Commit

Permalink
Merge pull request #556 from MeetDOD/issue-547
Browse files Browse the repository at this point in the history
Feat: Added Feedback/Testimonial  control in admin panel successfully issue 547
  • Loading branch information
VaibhavArora314 authored Aug 3, 2024
2 parents 3ba5b99 + a1c0a9c commit 8ca1686
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 2 deletions.
2 changes: 2 additions & 0 deletions admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Comments from "./pages/Comments";
import Layout from "./components/Layout";
import Favorites from "./pages/Favorites";
import Reactions from "./pages/Reactions";
import UsersFeedbacks from "./pages/UsersFeedbacks";
// import axios from "axios";
// axios.defaults.baseURL = "http://localhost:3001/";

Expand All @@ -43,6 +44,7 @@ function App() {
<Route path="" element={<Dashboard />} />
<Route path="profile" element={<Profile />} />
<Route path="users" element={<Users />} />
<Route path="userfeedbacks" element={<UsersFeedbacks />} />
<Route path="posts" element={<Posts />} />
<Route path="update-post/:postId" element={<UpdatePost />} />
<Route path="statistics" element={<Graphs />} />
Expand Down
2 changes: 2 additions & 0 deletions admin/src/components/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FaRegComments } from "react-icons/fa";
import GoogleTranslate from './GoogleTranslate';
import { RiHeartsLine } from "react-icons/ri";
import { VscReactions } from "react-icons/vsc";
import { VscFeedback } from "react-icons/vsc";

const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleSidebar: () => void }) => {
const location = useLocation();
Expand Down Expand Up @@ -39,6 +40,7 @@ const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleS
<Link to="/admin/comments" className={linkClasses('/admin/comments')}><FaRegComments size={23} className='mr-3'/>Comments</Link>
<Link to="/admin/favorites" className={linkClasses('/admin/favorites')}><RiHeartsLine size={23} className='mr-3'/>Favorites</Link>
<Link to="/admin/reactions" className={linkClasses('/admin/reactions')}><VscReactions size={23} className='mr-3'/>Reactions</Link>
<Link to="/admin/userfeedbacks" className={linkClasses('/admin/userfeedbacks')}><VscFeedback size={23} className='mr-3'/>Feedbacks</Link>
<Link to="/admin/statistics" className={linkClasses('/admin/statistics')}><VscGraphScatter size={23} className='mr-3'/>Statistics</Link>
</nav>
</div>
Expand Down
117 changes: 117 additions & 0 deletions admin/src/pages/UsersFeedbacks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { useState, useEffect } from "react";
import axios from "axios";
import { useRecoilValue } from "recoil";
import { tokenState } from "../store/atoms/auth";
import { IFeedback } from "../types";
import { ColorRing } from 'react-loader-spinner';
import { VscFeedback } from "react-icons/vsc";
import toast from "react-hot-toast";

const GetFeedbacks = () => {
const [feedbacks, setFeedbacks] = useState<IFeedback[]>([]);
const [loading, setLoading] = useState(true);
const token = useRecoilValue(tokenState);

document.title = "Style Share Admin | Manage Feedbacks 💬"

useEffect(() => {
const fetchFeedbacks = async () => {
try {
const response = await axios.get("/api/v1/admin/getfeedback", {
headers: {
Authorization: `Bearer ${token}`,
},
});
setFeedbacks(response.data);
setLoading(false);
} catch (error) {
console.error("Error fetching feedbacks:", error);
setLoading(true);
}
};

fetchFeedbacks();
}, [token]);

const handleToggleVisibility = async (feedbackId: string) => {
try {
await axios.patch(
`/api/v1/admin/toggleFeedbackVisibility/${feedbackId}`,
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
toast.success("Feedback visibility updated");
setFeedbacks(feedbacks.map(feedback =>
feedback.id === feedbackId ? { ...feedback, visible: !feedback.visible } : feedback
));
} catch (error) {
console.error("Error updating feedback visibility:", error);
toast.error("Error updating feedback visibility");
}
};

return (
<div>
<div className="flex-1 flex flex-col lg:ml-80">
<div className="mx-5 mb-5">
<span className="flex items-center text-xl font-bold decoration-sky-500 decoration-dotted underline">
<div className='inline-block p-2 text-white bg-[#000435] rounded-lg mr-2'>
<VscFeedback size={23} />
</div>
All Feedbacks
</span>
</div>
{loading ?
<div className="flex justify-center items-center h-80">
<ColorRing
visible={true}
height="100"
width="100"
colors={['#000435', 'rgb(14 165 233)', 'rgb(243 244 246)', '#000435', 'rgb(14 165 233)']}
/>
</div>
:
<div className="mx-5 lg:mr-11 overflow-x-auto shadow-md rounded-xl mb-5">
<table className="w-full rtl:text-right text-gray-500 dark:text-gray-400">
<thead className="text-xs md:text-sm text-white uppercase bg-sky-500 text-center">
<tr>
<th scope="col" className="px-6 py-3">User</th>
<th scope="col" className="px-8 py-3">Comment</th>
<th scope="col" className="px-6 py-3">Rating</th>
<th scope="col" className="px-6 py-3">Created At</th>
<th scope="col" className="px-16 py-3">Action</th>
</tr>
</thead>
<tbody>
{feedbacks.map(feedback => (
<tr key={feedback.id} className="text-xs md:text-sm text-center border-b bg-[#000435] border-sky-500 hover:bg-blue-950 hover:text-white">
<td className="px-8">
<span className="font-bold">{feedback.user.username}</span>
</td>
<td className="px-8 py-4 font-semibold">{feedback.comment}</td>
<td className="px-8 py-4 font-semibold">{feedback.rating}</td>
<td className="px-8 py-4 font-semibold">{new Date(feedback.createdAt).toLocaleDateString()}</td>
<td className="px-2 py-4 grid justify-center text-center">
<button
className={`font-semibold rounded-md p-2 text-white border-2 ${feedback.visible ? 'bg-red-500 hover:bg-red-600' : 'bg-sky-500 hover:bg-sky-600'}`}
onClick={() => handleToggleVisibility(feedback.id)}
>
{feedback.visible ? 'Hide from Testimonials' : 'Show on Testimonials'}
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
}
</div>
</div>
);
};

export default GetFeedbacks;
11 changes: 10 additions & 1 deletion admin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,13 @@ export interface IReaction {
createdAt: number;
user: IUser;
post: IPost;
}
}

export interface IFeedback {
id: string;
comment: string;
rating: number;
createdAt: string;
user: IUser;
visible: boolean;
}
1 change: 1 addition & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ model Feedback {
rating Int
comment String
userId String @db.ObjectId
visible Boolean @default(false)
user User @relation("userFeedbacks", fields: [userId], references: [id])
createdAt DateTime @default(now())
}
Expand Down
44 changes: 44 additions & 0 deletions backend/src/routes/admin/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,4 +644,48 @@ export const downloadReportController = async (req: UserAuthRequest, res: Respon
error: "An unexpected exception occurred!",
});
}
};

export const getFeedbacks = async (req: Request, res: Response) => {
try {
const feedbacks = await prisma.feedback.findMany({
include: {
user: {
select: {
id: true,
username: true,
avatar: true,
},
},
},
});

res.status(200).json(feedbacks);
} catch (error) {
console.error('Error fetching feedbacks:', error);
res.status(500).json({ error: 'An unexpected error occurred!' });
}
};

export const toggleFeedbackVisibility = async (req: Request, res: Response) => {
const { id } = req.params;
try {
const feedback = await prisma.feedback.findUnique({
where: { id },
});

if (!feedback) {
return res.status(404).json({ error: 'Feedback not found' });
}

const updatedFeedback = await prisma.feedback.update({
where: { id },
data: { visible: !feedback.visible },
});

res.status(200).json(updatedFeedback);
} catch (error) {
console.error('Error toggling feedback visibility:', error);
res.status(500).json({ error: 'An unexpected error occurred!' });
}
};
6 changes: 5 additions & 1 deletion backend/src/routes/admin/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Router} from 'express';
import { getPostReactionsController,getFavoritesController,adminLoginController, adminProfileController, allUserForAdmin, blockUserController, unblockUserController, getAdminPostsController, getAdminTrendingPostsController, getAdminStatsController, getGraphsStatsController, updatePostController, deletePostController, getPostByIdController, getAllContactMessages, deleteCommentController, downloadReportController } from './controller';
import { getPostReactionsController,getFavoritesController,adminLoginController, adminProfileController, allUserForAdmin, blockUserController, unblockUserController, getAdminPostsController, getAdminTrendingPostsController, getAdminStatsController, getGraphsStatsController, updatePostController, deletePostController, getPostByIdController, getAllContactMessages, deleteCommentController, downloadReportController, getFeedbacks, toggleFeedbackVisibility } from './controller';
import { isAdmin } from '../../middleware/adminAuth';

const adminRouter = Router();
Expand Down Expand Up @@ -38,4 +38,8 @@ adminRouter.get('/favorites', isAdmin, getFavoritesController);

adminRouter.get('/downloadReport',isAdmin, downloadReportController);

adminRouter.get('/getfeedback',isAdmin, getFeedbacks);

adminRouter.patch('/toggleFeedbackVisibility/:id', isAdmin, toggleFeedbackVisibility);

export default adminRouter;
3 changes: 3 additions & 0 deletions backend/src/routes/user/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,9 @@ export const createFeedback = async (req: UserAuthRequest, res: Response) => {
export const getFeedbacks = async (req: Request, res: Response) => {
try {
const feedbacks = await prisma.feedback.findMany({
where:{
visible:true
},
include: {
user: {
select: {
Expand Down

0 comments on commit 8ca1686

Please sign in to comment.