Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into code_editor_js
Browse files Browse the repository at this point in the history
  • Loading branch information
Parth18Shah committed Aug 7, 2024
2 parents f2d7308 + 02b35f9 commit ce3761d
Show file tree
Hide file tree
Showing 66 changed files with 3,225 additions and 699 deletions.
2 changes: 1 addition & 1 deletion admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./src/assets/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Style Share Admin</title>
</head>
<body>
<div id="root"></div>
Expand Down
6 changes: 6 additions & 0 deletions admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import Graphs from "./pages/Graphs";
import ContactMessages from "./pages/ContactMessages";
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 @@ -41,11 +44,14 @@ 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 />} />
<Route path="contactmessages" element={<ContactMessages />} />
<Route path="comments" element={<Comments />} />
<Route path="favorites" element={<Favorites />} />
<Route path="reactions" element={<Reactions />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</AuthenticatedRoute>
Expand Down
Binary file added admin/src/assets/avatars/avatar1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added admin/src/assets/avatars/avatar2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added admin/src/assets/avatars/avatar3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added admin/src/assets/avatars/avatar4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added admin/src/assets/avatars/avatar5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added admin/src/assets/captcha.webp
Binary file not shown.
3 changes: 2 additions & 1 deletion admin/src/components/CaptchaAdmin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { IoMdRefresh } from "react-icons/io";
import "../styles/captcha.css"

const CaptchaAdmin = ({ onChange }: { onChange: (isValid: boolean) => void }) => {
const [captchaText, setCaptchaText] = useState('');
Expand Down Expand Up @@ -33,7 +34,7 @@ const CaptchaAdmin = ({ onChange }: { onChange: (isValid: boolean) => void }) =>
return (
<div className="mb-4 rounded-lg">
<div className="flex items-center justify-between mb-4 text-[#000435] bg-gray-100 dark:text-white dark:bg-blue-950 p-2 rounded-lg border border-[#5f67de] dark:border-white">
<span className="text-xl font-semibold italic line-through">{captchaText}</span>
<span className="captcha w-1/2 p-2.5 text-2xl font-bold border-2 border-black rounded-md select-none border-solid italic line-through">{captchaText}</span>
<button
type="button"
onClick={generateCaptcha}
Expand Down
27 changes: 27 additions & 0 deletions admin/src/components/NewPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useRecoilValue } from "recoil";
import { tokenState } from "../store/atoms/auth";
import { Link } from "react-router-dom";
import { ColorRing } from 'react-loader-spinner';
import { TbReportAnalytics } from "react-icons/tb";

const NewPosts = () => {
const [posts, setposts] = useState<IPost[]>([]);
Expand All @@ -27,6 +28,27 @@ const NewPosts = () => {
}
};

const downloadReport = async () => {
try {
const response = await axios.get('/api/v1/admin/downloadReport', {
headers: {
Authorization: `Bearer ${token}`,
},
responseType: 'blob',
});

const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'StyleShare_Report.pdf');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('Error downloading the report:', error);
}
};

useEffect(() => {
fetchPost();
}, []);
Expand Down Expand Up @@ -89,6 +111,11 @@ const NewPosts = () => {
</tbody>
</table>
</div>
<div className="lg:mx-24 mx-10 lg:mr-11 mt-5 overflow-x-auto rounded-xl mb-5">
<button onClick={downloadReport} className="flex items-center py-2.5 px-4 rounded-lg transition duration-200 bg-yellow-500 hover:bg-yellow-600 text-gray-100"><TbReportAnalytics size={23} className='mr-3'/>
Download Report
</button>
</div>
</>
}
</div>
Expand Down
6 changes: 6 additions & 0 deletions admin/src/components/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { VscGraphScatter } from "react-icons/vsc";
import { MdOutlineAttachEmail } from "react-icons/md";
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 All @@ -35,6 +38,9 @@ const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleS
<Link to="/admin/posts" className={linkClasses('/admin/posts')}><IoNewspaperOutline size={23} className='mr-3'/>All Posts</Link>
<Link to="/admin/contactmessages" className={linkClasses('/admin/contactmessages')}><MdOutlineAttachEmail size={23} className='mr-3'/>Messages</Link>
<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
12 changes: 6 additions & 6 deletions admin/src/components/StatusCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ const StatusCard = () => {
<Link to="/admin/posts" className="col-span-1">
{renderCard(<LiaNewspaperSolid size={40} />, stats?.totalPosts ?? null, 'Total Posts')}
</Link>
<div className="col-span-1">
<Link to="/admin/comments" className="col-span-1">
{renderCard(<FaComments size={40} />, stats?.totalComments ?? null, 'Total Comments')}
</div>
<div className="col-span-1">
</Link>
<Link to="/admin/reactions" className="col-span-1">
{renderCard(<MdAddReaction size={40} />, stats?.totalReactions ?? null, 'Total Reactions')}
</div>
</Link>
<Link to="/admin/contactmessages" className="col-span-1">
{renderCard(<SiGooglemessages size={40} />, stats?.contactMessages ?? null, 'Contact Messages')}
</Link>
<div className="col-span-1">
<Link to="/admin/favorites" className="col-span-1">
{renderCard(<RiHeartsFill size={40} />, stats?.favoritesPosts ?? null, 'Most Favorites')}
</div>
</Link>
</div>
</div>
</SkeletonTheme>
Expand Down
35 changes: 32 additions & 3 deletions admin/src/pages/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { tokenState } from "../store/atoms/auth";
import toast from "react-hot-toast";
import { ColorRing } from 'react-loader-spinner';
import { FaComments } from "react-icons/fa";
import { TbReportAnalytics } from "react-icons/tb";

const Comments = () => {
const [posts, setPosts] = useState<IPost[]>([]);
Expand Down Expand Up @@ -47,10 +48,31 @@ const Comments = () => {
}
};

const downloadUsersCommentsReport = async () => {
try {
const response = await axios.get('/api/v1/admin/downloaduserscommentsreport', {
headers: {
Authorization: `Bearer ${token}`,
},
responseType: 'blob',
});

const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'StyleShare_Comments_Report.pdf');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('Error downloading users comments report:', error);
}
};

return (
<div>
<div className="lg:ml-80">
<div className="mx-5 mb-5">
<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'>
<FaComments size={23} />
Expand All @@ -68,6 +90,7 @@ const Comments = () => {
/>
</div>
:
<>
<div className="mx-5 lg:mr-11 overflow-x-auto shadow-md rounded-xl mb-5">
<table className="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<thead className="text-xs text-white uppercase bg-sky-500">
Expand Down Expand Up @@ -97,7 +120,7 @@ const Comments = () => {
<td colSpan={4} className="px-8 py-4">
<div className="flex items-start space-x-4">
<div className="flex-shrink-0">
<img className="h-10 w-10 rounded-full" src={`https://ui-avatars.com/api/?name=${comment.user.username}&background=0ea5e9&color=fff&rounded=true&bold=true`} alt="profile-pic" />
<img className="h-10 w-10 rounded-full" src={comment.user?.avatar?.replace('/app', '/admin') || `https://ui-avatars.com/api/?name=${comment.user?.username}&background=0ea5e9&color=fff&rounded=true&bold=true`} alt="profile-pic" />
</div>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium text-white">
Expand All @@ -123,6 +146,12 @@ const Comments = () => {
</tbody>
</table>
</div>
<div className="mx-5 overflow-x-auto rounded-xl mb-5">
<button onClick={downloadUsersCommentsReport} className="flex items-center py-2.5 px-4 rounded-lg transition duration-200 bg-yellow-500 hover:bg-yellow-600 text-gray-100"><TbReportAnalytics size={23} className='mr-3'/>
Download Comments Info
</button>
</div>
</>
}
</div>
</div>
Expand Down
29 changes: 29 additions & 0 deletions admin/src/pages/ContactMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'react-responsive-modal/styles.css';
import '../styles/Model.css'
import { ColorRing } from 'react-loader-spinner';
import { MdMessage } from "react-icons/md";
import { TbReportAnalytics } from "react-icons/tb";

const ContactMessages = () => {
const [contactMessages, setContactMessages] = useState<IContactMessage[]>([]);
Expand Down Expand Up @@ -37,6 +38,27 @@ const ContactMessages = () => {
fetchMessages();
}, [token]);

const downloadContactMessagesReport = async () => {
try {
const response = await axios.get('/api/v1/admin/downloadcontactmessagereport', {
headers: {
Authorization: `Bearer ${token}`,
},
responseType: 'blob',
});

const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'StyleShare_Contact_Messages_Report.pdf');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('Error downloading the Contact Messages report:', error);
}
};

const handleOpenModal = (message: IContactMessage) => {
setSelectedMessage(message);
setOpen(true);
Expand Down Expand Up @@ -68,6 +90,7 @@ const ContactMessages = () => {
/>
</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">
Expand Down Expand Up @@ -112,6 +135,12 @@ const ContactMessages = () => {
</tbody>
</table>
</div>
<div className="mx-5 overflow-x-auto rounded-xl mb-5">
<button onClick={downloadContactMessagesReport} className="flex items-center py-2.5 px-4 rounded-lg transition duration-200 bg-yellow-500 hover:bg-yellow-600 text-gray-100"><TbReportAnalytics size={23} className='mr-3'/>
Download Messages
</button>
</div>
</>
}
</div>
<Modal open={open} onClose={handleCloseModal} center classNames={{ modal: 'customModal',overlay: 'customOverlay'}}>
Expand Down
120 changes: 120 additions & 0 deletions admin/src/pages/Favorites.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { useState, useEffect } from "react";
import axios from "axios";
import { useRecoilValue } from "recoil";
import { tokenState } from "../store/atoms/auth";
import { IFavoritePost } from "../types";
import { ColorRing } from 'react-loader-spinner';
import { RiHeartsFill } from "react-icons/ri";
import { TbReportAnalytics } from "react-icons/tb";

const Favorites = () => {
const [favoritePosts, setFavoritePosts] = useState<IFavoritePost[]>([]);
const [loading, setLoading] = useState(true);
const token = useRecoilValue(tokenState);

document.title = "Style Share Admin | Users Favorites 💓";

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

fetchFavoritePosts();
}, [token]);

const downloadUsersFavoritesReport = async () => {
try {
const response = await axios.get('/api/v1/admin/downloadusersfavoritesreport', {
headers: {
Authorization: `Bearer ${token}`,
},
responseType: 'blob',
});

const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'StyleShare_Favorites_Report.pdf');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('Error downloading users favorites report:', error);
}
};

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'>
<RiHeartsFill size={23} />
</div>
Users Favorite Posts
</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-8 py-3 text-start">Photo</th>
<th scope="col" className="px-8 py-3 text-start">Name</th>
<th scope="col" className="px-6 py-3">Post Title</th>
<th scope="col" className="px-6 py-3">Marked At</th>
</tr>
</thead>
<tbody>
{favoritePosts.map(favorite => (
<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">
<td className="pl-7">
<img className="h-10 w-10 rounded-full" src={favorite.user?.avatar?.replace('/app', '/admin') || `https://ui-avatars.com/api/?name=${favorite.user?.username}&background=0ea5e9&color=fff&rounded=true&bold=true`} alt="profile-pic" />
</td>
<td className="px-8 py-4 font-semibold">
<div className="flex flex-col items-start">
<span className="font-bold">{favorite.user.username}</span>
<span className="font-thin text-gray-300">{favorite.user.email}</span>
</div>
</td>
<td className="px-8 py-4 font-semibold">{favorite.post.title}</td>
<td className="px-8 py-4 font-semibold">{new Date(favorite.createdAt).toLocaleDateString()}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="mx-5 overflow-x-auto rounded-xl mb-5">
<button onClick={downloadUsersFavoritesReport} className="flex items-center py-2.5 px-4 rounded-lg transition duration-200 bg-yellow-500 hover:bg-yellow-600 text-gray-100"><TbReportAnalytics size={23} className='mr-3'/>
Download Favorites Info
</button>
</div>
</>
}
</div>
</div>
);
};

export default Favorites;
Loading

0 comments on commit ce3761d

Please sign in to comment.