From 95bb0ba61bb194f7e06c651ecde1d063c499ab4c Mon Sep 17 00:00:00 2001 From: meet Date: Wed, 10 Jul 2024 17:11:56 +0530 Subject: [PATCH 1/3] Added graphs for admin --- admin/package-lock.json | 27 +++ admin/package.json | 2 + admin/src/App.tsx | 9 + admin/src/components/SideBar.tsx | 2 + admin/src/pages/Graphs.tsx | 261 +++++++++++++++++++++++++ backend/prisma/schema.prisma | 4 +- backend/src/routes/admin/controller.ts | 50 +++++ backend/src/routes/admin/route.ts | 4 +- backend/src/routes/post/controller.ts | 4 +- 9 files changed, 360 insertions(+), 3 deletions(-) create mode 100644 admin/src/pages/Graphs.tsx diff --git a/admin/package-lock.json b/admin/package-lock.json index f9c413e3..2378a070 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -9,7 +9,9 @@ "version": "0.0.0", "dependencies": { "axios": "^1.7.2", + "chart.js": "^4.4.3", "react": "^18.3.1", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", "react-hot-toast": "^2.4.1", "react-icons": "^5.2.1", @@ -1017,6 +1019,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1833,6 +1840,17 @@ "node": ">=4" } }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3532,6 +3550,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", diff --git a/admin/package.json b/admin/package.json index 8218ebc6..ed199ffc 100644 --- a/admin/package.json +++ b/admin/package.json @@ -11,7 +11,9 @@ }, "dependencies": { "axios": "^1.7.2", + "chart.js": "^4.4.3", "react": "^18.3.1", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.3.1", "react-hot-toast": "^2.4.1", "react-icons": "^5.2.1", diff --git a/admin/src/App.tsx b/admin/src/App.tsx index b3d569cc..a2fd5bf5 100644 --- a/admin/src/App.tsx +++ b/admin/src/App.tsx @@ -10,6 +10,7 @@ import Profile from "./pages/Profile"; import Users from "./pages/Users"; import Posts from "./pages/Posts"; import { Toaster } from "react-hot-toast"; +import Graphs from "./pages/Graphs"; // import axios from "axios"; // axios.defaults.baseURL = "http://localhost:3001/"; @@ -59,6 +60,14 @@ function App() { } /> + + + + } + /> } /> diff --git a/admin/src/components/SideBar.tsx b/admin/src/components/SideBar.tsx index 4241414e..e2d5ac39 100644 --- a/admin/src/components/SideBar.tsx +++ b/admin/src/components/SideBar.tsx @@ -5,6 +5,7 @@ import { CgProfile } from "react-icons/cg"; import { IoNewspaperOutline } from "react-icons/io5"; import logo from '../assets/favicon.png'; import { Link, useLocation } from 'react-router-dom'; +import { VscGraphScatter } from "react-icons/vsc"; const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleSidebar: () => void }) => { const location = useLocation(); @@ -25,6 +26,7 @@ const SideBar = ({ sidebarOpen, toggleSidebar }: { sidebarOpen: boolean, toggleS My Profile All Users All Posts + Statistics ); diff --git a/admin/src/pages/Graphs.tsx b/admin/src/pages/Graphs.tsx new file mode 100644 index 00000000..466d2b2b --- /dev/null +++ b/admin/src/pages/Graphs.tsx @@ -0,0 +1,261 @@ +import { useEffect, useState } from "react"; +import Navbar from "../components/Navbar"; +import SideBar from "../components/SideBar"; +import axios from "axios"; +import { Line, Bar, Bubble, Scatter } from "react-chartjs-2"; +import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend } from "chart.js"; +import { useRecoilValue } from "recoil"; +import { tokenState } from "../store/atoms/auth"; + +ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend); + +const Graphs = () => { + const [sidebarOpen, setSidebarOpen] = useState(false); + const [userStats, setUserStats] = useState([]); + const [postStats, setPostStats] = useState([]); + const [commentStats, setCommentStats] = useState([]); + const [favoritesStats, setFavoritesStats] = useState([]); + const [reactionsStats, setReactionsStats] = useState([]); + const [contactMessagesStats, setContactMessagesStats] = useState([]); + const token = useRecoilValue(tokenState); + + const toggleSidebar = () => { + setSidebarOpen(!sidebarOpen); + }; + + useEffect(() => { + const fetchStats = async () => { + try { + const response = await axios.get("/api/v1/admin/getgraphsstatus", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + setUserStats(response.data.users); + setPostStats(response.data.posts); + setCommentStats(response.data.comments); + setFavoritesStats(response.data.favorites); + setReactionsStats(response.data.reactions); + setContactMessagesStats(response.data.contacts); + } catch (error) { + console.error("Error fetching stats:", error); + } + }; + fetchStats(); + }, [token]); + + const formatData = (data: any[]) => { + const counts: { [key: string]: number } = {}; + data.forEach((item) => { + const date = new Date(item.createdAt); + const formattedDate = `${date.getMonth() + 1}/${date.getDate()}`; + if (!counts[formattedDate]) counts[formattedDate] = 0; + counts[formattedDate]++; + }); + const labels = Object.keys(counts); + const values = Object.values(counts); + return { labels, values }; + }; + + const createChartData = ({ data, label, borderColor }:any) => { + return { + labels: data.labels, + datasets: [ + { + label: label, + data: data.values, + fill: false, + borderColor: borderColor, + tension: 0.1, + }, + ], + }; + }; + + const createBarData = ({ data, label, backgroundColor }:any) => { + return { + labels: data.labels, + datasets: [ + { + label: label, + data: data.values, + backgroundColor: backgroundColor, + }, + ], + }; + }; + + const createBubbleData = ({ data, label, backgroundColor }:any) => { + return { + labels: data.labels, + datasets: [ + { + label: label, + data: data.values.map((value:any, index:any) => ({ + x: index, + y: value, + r: Math.sqrt(value) * 3, + })), + backgroundColor: backgroundColor, + }, + ], + }; + }; + + const createScatterData = ({ data, label, backgroundColor }:any) => { + return { + labels: data.labels, + datasets: [ + { + label: label, + data: data.values.map((value:any, index:any) => ({ + x: index, + y: value, + })), + backgroundColor: backgroundColor, + }, + ], + }; + }; + + const userData = formatData(userStats); + const postData = formatData(postStats); + const commentData = formatData(commentStats); + const favoriteData = formatData(favoritesStats); + const reactionData = formatData(reactionsStats); + const contactMessagesData = formatData(contactMessagesStats); + + const userChartData = createChartData({ data: userData, label: "Users registered", borderColor: "rgb(75, 192, 192)" }); + const postChartData = createChartData({ data: postData, label: "Posts uploads", borderColor: "rgb(75, 75, 192)" }); + const commentChartData = createChartData({ data: commentData, label: "Comments done", borderColor: "rgb(192, 75, 192)" }); + const favoriteChartData = createChartData({ data: favoriteData, label: "Favorites done", borderColor: "rgb(192, 75, 75)" }); + const reactionChartData = createChartData({ data: reactionData, label: "Reactions done", borderColor: "rgb(75, 192, 75)" }); + const contactMessageChartData = createChartData({ data: contactMessagesData, label: "Contact messages received", borderColor: "rgb(192, 192, 75)" }); + + const userBarData = createBarData({ data: userData, label: "Users registered", backgroundColor: "rgba(75, 192, 192, 0.5)" }); + const postBarData = createBarData({ data: postData, label: "Posts uploads", backgroundColor: "rgba(75, 75, 192, 0.5)" }); + const commentBarData = createBarData({ data: commentData, label: "Comments done", backgroundColor: "rgba(192, 75, 192, 0.5)" }); + const favoriteBarData = createBarData({ data: favoriteData, label: "Favorites done", backgroundColor: "rgba(192, 75, 75, 0.5)" }); + const reactionBarData = createBarData({ data: reactionData, label: "Reactions done", backgroundColor: "rgba(75, 192, 75, 0.5)" }); + const contactMessageBarData = createBarData({ data: contactMessagesData, label: "Contact messages received", backgroundColor: "rgba(192, 192, 75, 0.5)" }); + + const userBubbleData = createBubbleData({ data: userData, label: "Users registered", backgroundColor: "rgba(75, 192, 192, 0.5)" }); + const postBubbleData = createBubbleData({ data: postData, label: "Posts uploads", backgroundColor: "rgba(75, 75, 192, 0.5)" }); + const commentBubbleData = createBubbleData({ data: commentData, label: "Comments done", backgroundColor: "rgba(192, 75, 192, 0.5)" }); + const favoriteBubbleData = createBubbleData({ data: favoriteData, label: "Favorites done", backgroundColor: "rgba(192, 75, 75, 0.5)" }); + const reactionBubbleData = createBubbleData({ data: reactionData, label: "Reactions done", backgroundColor: "rgba(75, 192, 75, 0.5)" }); + const contactMessageBubbleData = createBubbleData({ data: contactMessagesData, label: "Contact messages received", backgroundColor: "rgba(192, 192, 75, 0.5)" }); + + const userScatterData = createScatterData({ data: userData, label: "Users registered", backgroundColor: "rgba(75, 192, 192, 0.5)" }); + const postScatterData = createScatterData({ data: postData, label: "Posts uploads", backgroundColor: "rgba(75, 75, 192, 0.5)" }); + const commentScatterData = createScatterData({ data: commentData, label: "Comments done", backgroundColor: "rgba(192, 75, 192, 0.5)" }); + const favoriteScatterData = createScatterData({ data: favoriteData, label: "Favorites done", backgroundColor: "rgba(192, 75, 75, 0.5)" }); + const reactionScatterData = createScatterData({ data: reactionData, label: "Reactions done", backgroundColor: "rgba(75, 192, 75, 0.5)" }); + const contactMessageScatterData = createScatterData({ data: contactMessagesData, label: "Contact messages received", backgroundColor: "rgba(192, 192, 75, 0.5)" }); + + const Y = new Date(); + let year = Y.getFullYear(); + + return ( +
+ +
+ +
+

Users Over Time {year}

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Posts Over Time {year}

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Comments Over Time {year}

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Favorites Over Time {year}

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Reactions Over Time {year}

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Contact Messages Over Time {year}

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ ); +}; + +export default Graphs; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index d73696b8..bbe53d80 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -66,7 +66,8 @@ model Reaction { postId String @db.ObjectId user User @relation(fields: [userId], references: [id]) post Post @relation(fields: [postId], references: [id]) - + createdAt DateTime @default(now()) + @@unique([userId, postId]) } @@ -87,6 +88,7 @@ model Favorite { postId String @db.ObjectId user User @relation("userFavorites", fields: [userId], references: [id]) post Post @relation("postFavorites", fields: [postId], references: [id]) + createdAt DateTime @default(now()) @@unique([userId, postId]) } diff --git a/backend/src/routes/admin/controller.ts b/backend/src/routes/admin/controller.ts index 9a714d98..58cfdf29 100644 --- a/backend/src/routes/admin/controller.ts +++ b/backend/src/routes/admin/controller.ts @@ -232,3 +232,53 @@ export const getAdminStatsController = async (req: Request, res: Response) => { }); } }; + +export const getGraphsStatsController = async (req: Request, res: Response) => { + try { + const users = await prisma.user.findMany({ + select: { + createdAt: true, + }, + }); + const posts = await prisma.post.findMany({ + select: { + createdAt: true, + }, + }); + const comments = await prisma.comment.findMany({ + select: { + createdAt: true, + }, + }); + const favorites = await prisma.favorite.findMany({ + select: { + createdAt:true + }, + }); + const contacts = await prisma.contactMessage.findMany({ + select: { + createdAt:true + }, + }); + const reactions = await prisma.reaction.findMany({ + select: { + createdAt:true + }, + }); + + res.status(200).json({ + users, + posts, + comments, + favorites, + contacts, + reactions + }); + } catch (error) { + console.log(error) + res.status(500).json({ + error: "An unexpected exception occurred!", + }); + } +}; + diff --git a/backend/src/routes/admin/route.ts b/backend/src/routes/admin/route.ts index b739b361..01678462 100644 --- a/backend/src/routes/admin/route.ts +++ b/backend/src/routes/admin/route.ts @@ -1,5 +1,5 @@ import {Router} from 'express'; -import { adminLoginController, adminProfileController, allUserForAdmin, blockUserController, unblockUserController, getAdminPostsController, getAdminTrendingPostsController, getAdminStatsController } from './controller'; +import { adminLoginController, adminProfileController, allUserForAdmin, blockUserController, unblockUserController, getAdminPostsController, getAdminTrendingPostsController, getAdminStatsController, getGraphsStatsController } from './controller'; import { isAdmin } from '../../middleware/adminAuth'; const adminRouter = Router(); @@ -20,4 +20,6 @@ adminRouter.get("/posts/trending", isAdmin,getAdminTrendingPostsController ); adminRouter.get("/getCardStatus", isAdmin,getAdminStatsController ); +adminRouter.get("/getgraphsstatus", isAdmin,getGraphsStatsController); + export default adminRouter; \ No newline at end of file diff --git a/backend/src/routes/post/controller.ts b/backend/src/routes/post/controller.ts index c03bdd4a..2cfd5159 100644 --- a/backend/src/routes/post/controller.ts +++ b/backend/src/routes/post/controller.ts @@ -465,7 +465,8 @@ export const favoritePostController = async (req: UserAuthRequest, res: Response await prisma.favorite.create({ data: { userId, - postId + postId, + createdAt: new Date() } }); @@ -756,6 +757,7 @@ export const reactToPostController = async (req: UserAuthRequest, res: Response) userId, postId, type, + createdAt: new Date() }, }); res.status(201).json({ message: "Reaction added", reaction: newReaction }); From 04d7f4513d420d8acb153a86c898c43a6362531f Mon Sep 17 00:00:00 2001 From: meet Date: Thu, 11 Jul 2024 18:38:50 +0530 Subject: [PATCH 2/3] Added label to x and y axis in very grphs --- admin/src/pages/Graphs.tsx | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/admin/src/pages/Graphs.tsx b/admin/src/pages/Graphs.tsx index 466d2b2b..860bb628 100644 --- a/admin/src/pages/Graphs.tsx +++ b/admin/src/pages/Graphs.tsx @@ -164,92 +164,92 @@ const Graphs = () => {

Users Over Time {year}

-
- +
+
- +
- +
- +

Posts Over Time {year}

- +
- +
- +
- +

Comments Over Time {year}

- +
- +
- +
- +

Favorites Over Time {year}

- +
- +
- +
- +

Reactions Over Time {year}

- +
- +
- +
- +

Contact Messages Over Time {year}

- +
- +
- +
- +
From 6481cc5a8f9c8c1c059cfb2884b1edf243de4366 Mon Sep 17 00:00:00 2001 From: meet Date: Fri, 12 Jul 2024 15:07:22 +0530 Subject: [PATCH 3/3] Removed the bubble and scatter graphs from grsphs.tsx --- admin/src/pages/Graphs.tsx | 85 +------------------------------------- 1 file changed, 1 insertion(+), 84 deletions(-) diff --git a/admin/src/pages/Graphs.tsx b/admin/src/pages/Graphs.tsx index 860bb628..88b6d142 100644 --- a/admin/src/pages/Graphs.tsx +++ b/admin/src/pages/Graphs.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import Navbar from "../components/Navbar"; import SideBar from "../components/SideBar"; import axios from "axios"; -import { Line, Bar, Bubble, Scatter } from "react-chartjs-2"; +import { Line, Bar } from "react-chartjs-2"; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, Title, Tooltip, Legend } from "chart.js"; import { useRecoilValue } from "recoil"; import { tokenState } from "../store/atoms/auth"; @@ -85,39 +85,6 @@ const Graphs = () => { }; }; - const createBubbleData = ({ data, label, backgroundColor }:any) => { - return { - labels: data.labels, - datasets: [ - { - label: label, - data: data.values.map((value:any, index:any) => ({ - x: index, - y: value, - r: Math.sqrt(value) * 3, - })), - backgroundColor: backgroundColor, - }, - ], - }; - }; - - const createScatterData = ({ data, label, backgroundColor }:any) => { - return { - labels: data.labels, - datasets: [ - { - label: label, - data: data.values.map((value:any, index:any) => ({ - x: index, - y: value, - })), - backgroundColor: backgroundColor, - }, - ], - }; - }; - const userData = formatData(userStats); const postData = formatData(postStats); const commentData = formatData(commentStats); @@ -139,20 +106,6 @@ const Graphs = () => { const reactionBarData = createBarData({ data: reactionData, label: "Reactions done", backgroundColor: "rgba(75, 192, 75, 0.5)" }); const contactMessageBarData = createBarData({ data: contactMessagesData, label: "Contact messages received", backgroundColor: "rgba(192, 192, 75, 0.5)" }); - const userBubbleData = createBubbleData({ data: userData, label: "Users registered", backgroundColor: "rgba(75, 192, 192, 0.5)" }); - const postBubbleData = createBubbleData({ data: postData, label: "Posts uploads", backgroundColor: "rgba(75, 75, 192, 0.5)" }); - const commentBubbleData = createBubbleData({ data: commentData, label: "Comments done", backgroundColor: "rgba(192, 75, 192, 0.5)" }); - const favoriteBubbleData = createBubbleData({ data: favoriteData, label: "Favorites done", backgroundColor: "rgba(192, 75, 75, 0.5)" }); - const reactionBubbleData = createBubbleData({ data: reactionData, label: "Reactions done", backgroundColor: "rgba(75, 192, 75, 0.5)" }); - const contactMessageBubbleData = createBubbleData({ data: contactMessagesData, label: "Contact messages received", backgroundColor: "rgba(192, 192, 75, 0.5)" }); - - const userScatterData = createScatterData({ data: userData, label: "Users registered", backgroundColor: "rgba(75, 192, 192, 0.5)" }); - const postScatterData = createScatterData({ data: postData, label: "Posts uploads", backgroundColor: "rgba(75, 75, 192, 0.5)" }); - const commentScatterData = createScatterData({ data: commentData, label: "Comments done", backgroundColor: "rgba(192, 75, 192, 0.5)" }); - const favoriteScatterData = createScatterData({ data: favoriteData, label: "Favorites done", backgroundColor: "rgba(192, 75, 75, 0.5)" }); - const reactionScatterData = createScatterData({ data: reactionData, label: "Reactions done", backgroundColor: "rgba(75, 192, 75, 0.5)" }); - const contactMessageScatterData = createScatterData({ data: contactMessagesData, label: "Contact messages received", backgroundColor: "rgba(192, 192, 75, 0.5)" }); - const Y = new Date(); let year = Y.getFullYear(); @@ -170,12 +123,6 @@ const Graphs = () => {
-
- -
-
- -

Posts Over Time {year}

@@ -185,12 +132,6 @@ const Graphs = () => {
-
- -
-
- -

Comments Over Time {year}

@@ -200,12 +141,6 @@ const Graphs = () => {
-
- -
-
- -

Favorites Over Time {year}

@@ -215,12 +150,6 @@ const Graphs = () => {
-
- -
-
- -

Reactions Over Time {year}

@@ -230,12 +159,6 @@ const Graphs = () => {
-
- -
-
- -

Contact Messages Over Time {year}

@@ -245,12 +168,6 @@ const Graphs = () => {
-
- -
-
- -