Skip to content

Commit

Permalink
Merge branch 'main' of github.com:rohithaug/UCSD-CSE-210-Team-12 into…
Browse files Browse the repository at this point in the history
… tests-and-ci-setup
  • Loading branch information
Sharvari Deshmukh authored and Sharvari Deshmukh committed Mar 10, 2024
2 parents bc1d5fd + d012507 commit b8aaefb
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 7 deletions.
19 changes: 19 additions & 0 deletions client/src/assets/icons/sortButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// IMPORT LIBRARIES
import React from "react";

const SortButtonIcon = (props) => {
return (
<svg
class="w-3 h-3 ms-1.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
{...props}
>
<path d="M8.574 11.024h6.852a2.075 2.075 0 0 0 1.847-1.086 1.9 1.9 0 0 0-.11-1.986L13.736 2.9a2.122 2.122 0 0 0-3.472 0L6.837 7.952a1.9 1.9 0 0 0-.11 1.986 2.074 2.074 0 0 0 1.847 1.086Zm6.852 1.952H8.574a2.072 2.072 0 0 0-1.847 1.087 1.9 1.9 0 0 0 .11 1.985l3.426 5.05a2.123 2.123 0 0 0 3.472 0l3.427-5.05a1.9 1.9 0 0 0 .11-1.985 2.074 2.074 0 0 0-1.846-1.087Z"/>
</svg>
);
}

export default SortButtonIcon;
183 changes: 183 additions & 0 deletions client/src/pages/dashboard/components/blogPostMetrics.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// IMPORT LIBRARIES
import React, { useState, useEffect } from 'react';

// IMPORT ICONS
import SortButtonIcon from '@/assets/icons/sortButton';

const BlogPostMetrics = () => {
const [blogPostMetrics, setBlogPostMetrics] = useState([]);
const [sortDirection, setSortDirection] = useState({ key: null, ascending: true });

useEffect(() => {
const blogPostMetrics = [
{
id: "blog-post-id",
name: "Blog Post Title",
visits: 0,
likes: 0,
dislikes: 0,
source: {
email: 0,
direct: 0,
home: 0
}
},
{
id: "blog-post-id",
name: "Blog Post Title",
visits: 5,
likes: 0,
dislikes: 0,
source: {
email: 1,
direct: 4,
home: 0
}
},
{
id: "blog-post-id",
name: "Blog Post Title",
visits: 4,
likes: 0,
dislikes: 0,
source: {
email: 0,
direct: 7,
home: 0
}
}
];

setBlogPostMetrics(blogPostMetrics);
}, []);

const sortBlogPostMetrics = (key, subkey = null) => {
let tempBlogPostMetrics = [...blogPostMetrics];
let newSortDirection = { key, ascending: true };

if (key === "source") {
if (sortDirection.key === key && sortDirection.ascending) {
newSortDirection = { key, ascending: false };
tempBlogPostMetrics.sort((a, b) => a.source[subkey] - b.source[subkey]);
} else {
tempBlogPostMetrics.sort((a, b) => b.source[subkey] - a.source[subkey]);
}
} else {
if (sortDirection.key === key && sortDirection.ascending) {
newSortDirection = { key, ascending: false };
tempBlogPostMetrics.sort((a, b) => a[key] - b[key]);
} else {
tempBlogPostMetrics.sort((a, b) => b[key] - a[key]);
}
}

setBlogPostMetrics(tempBlogPostMetrics);
setSortDirection(newSortDirection);
}

return (
<div class="relative overflow-x-auto shadow-md sm:rounded-lg mb-8">
<table class="w-full text-sm text-left text-gray-500">
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3">
Blog Title
</th>
<th scope="col" class="px-6 py-3">
<div class="flex items-center">
Number of Views
<div
onClick={() => sortBlogPostMetrics("visits")}
>
<SortButtonIcon />
</div>
</div>
</th>
<th scope="col" class="px-6 py-3">
<div class="flex items-center">
Source - Email
<div
onClick={() => sortBlogPostMetrics("source", "email")}
>
<SortButtonIcon />
</div>
</div>
</th>
<th scope="col" class="px-6 py-3">
<div class="flex items-center">
Source - Direct
<div
onClick={() => sortBlogPostMetrics("source", "direct")}
>
<SortButtonIcon />
</div>
</div>
</th>
<th scope="col" class="px-6 py-3">
<div class="flex items-center">
Source - Home
<div
onClick={() => sortBlogPostMetrics("source", "home")}
>
<SortButtonIcon />
</div>
</div>
</th>
<th scope="col" class="px-6 py-3">
<div class="flex items-center">
Number of Likes
<div
onClick={() => sortBlogPostMetrics("likes")}
>
<SortButtonIcon />
</div>
</div>
</th>
<th scope="col" class="px-6 py-3">
<div class="flex items-center">
Number of dislikes
<div
onClick={() => sortBlogPostMetrics("dislikes")}
>
<SortButtonIcon />
</div>
</div>
</th>
</tr>
</thead>

<tbody>
{blogPostMetrics.map((item, idx) => {
return (
<tr key={idx} class="bg-white border-b">
<th scope="row" class="px-6 py-4">
<a href={`dashboard/${item.id}`} class="font-medium text-gray-900 whitespace-nowrap hover:text-blue-900 hover:underline">{item.name}</a>
</th>
<td class="px-6 py-4">
{item.visits}
</td>
<td class="px-6 py-4">
{item.source.email}
</td>
<td class="px-6 py-4">
{item.source.direct}
</td>
<td class="px-6 py-4">
{item.source.home}
</td>
<td class="px-6 py-4">
{item.likes}
</td>
<td class="px-6 py-4">
{item.dislikes}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
);
};

export default BlogPostMetrics;
61 changes: 58 additions & 3 deletions client/src/pages/dashboard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Cookie from 'js-cookie';
import Layout from './components/layout';
import BarChart from './components/charts/barChart';
import PieChart from './components/charts/pieChart';

import BlogPostMetrics from './components/blogPostMetrics';

export default function Page() {
const router = useRouter();

Expand All @@ -23,7 +24,7 @@ export default function Page() {
try {
setDashboardMetricsFetchLoading(true);
const response = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_ENDPOINT}/analytics/metrics/`, {
method: 'POST',
method: 'GET',
headers: {
'Authorization': `Bearer ${adminToken}`,
}
Expand Down Expand Up @@ -97,11 +98,57 @@ export default function Page() {
<div>
{dashboardMetrics && dashboardMetrics.uniqueVisit?.blog ?
<div>
<h1 className="text-3xl mb-4 font-normal tracking-tight text-gray-900">Blog Post Metrics</h1>
<BlogPostMetrics />

<h1 className="text-3xl mb-4 font-normal tracking-tight text-gray-900">Unique visits to each blog page</h1>
<BarChart
title="Unique visits to each blog page"
labels={dashboardMetrics?.uniqueVisit?.blog?.map(post => post?.blogId)}
datasets={[{ label: "Unique Visits", data: dashboardMetrics?.uniqueVisit?.blog?.map(post => post?.count) }]}
datasets={[
{
label: "Unique Visits", data: dashboardMetrics?.uniqueVisit?.blog?.map(post => post?.count),
backgroundColor: 'rgba(54, 162, 235, 0.3)',
borderColor: 'rgb(54, 162, 235)'
}
]}
/>
<BarChart
title="Likes and Dislikes for each blog page"
labels={dashboardMetrics?.likes?.map(post => post?.blogId)}
datasets={[
{
label: "Likes", data: dashboardMetrics?.likes?.map(post => post?.count),
backgroundColor: 'rgba(75, 192, 192, 0.3)',
borderColor: 'rgb(75, 192, 192)'
},
{
label: "Dislikes", data: dashboardMetrics?.dislikes?.map(post => post?.count),
backgroundColor: 'rgba(255, 99, 132, 0.3)',
borderColor: 'rgb(255, 99, 132)'
}
]}
/>
<BarChart
title="Source of visit for each blog page"
labels={dashboardMetrics?.source?.blog?.map(post => post?.blogId)}
datasets={[
{
label: "Home", data: dashboardMetrics?.source?.blog?.map(post => post?.count?.home),
backgroundColor: 'rgba(255, 206, 86, 0.3)',
borderColor: 'rgb(255, 206, 86)'
},
{
label: "Direct", data: dashboardMetrics?.source?.blog?.map(post => post?.count?.direct),
backgroundColor: 'rgba(153, 102, 255, 0.3)',
borderColor: 'rgb(153, 102, 255)'
},
{
label: "Email", data: dashboardMetrics?.source?.blog?.map(post => post?.count?.email),
backgroundColor: 'rgba(54, 162, 235, 0.3)',
borderColor: 'rgb(54, 162, 235)'
}
]}
/>
<div style={{display: 'flex'}}>
<div style={{display: 'block', flex: 1}}>
Expand All @@ -121,6 +168,14 @@ export default function Page() {
/>
</div>
</div>
<div style={{display: 'block', flex: 1}}>
<h1 className="text-3xl mb-4 font-normal tracking-tight text-gray-900">Category wise Views</h1>
<PieChart
title="Views for each category"
labels={dashboardMetrics?.uniqueVisit?.category?.map(cat => cat?.blogId)}
datasets={[{ label: "Views", data: dashboardMetrics?.uniqueVisit?.category?.map(cat => cat?.count) }]}
/>
</div>
</div>
:
<></>
Expand Down
69 changes: 68 additions & 1 deletion server/src/app/controllers/analytics.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,73 @@ const getMetrics = async (req, res) => {
}
};

/**
* Get SHS Dashboard Analytics Metrics for a specific blog.
*
* @function
* @async
* @param {Object} req - Express request object.
* @param {Object} res - Express response object.
* @returns {Promise<void>} Promise that resolves with metric details for a specific blog.
* @throws {Error} If there is an issue getting metric details or sending the response.
*/

const getBlogMetrics = async (req, res) => {
try {
isAuth(req, res, async () => {
const { blogId } = req.params;
const metrics = await analyticsService.getBlogMetrics(blogId);
if (!metrics) {
res.status(httpStatus.BAD_REQUEST).send({
message: 'Error fetching dashboard metrics',
description: 'Unexpected server error in getting metrics for dashboard. If the issue persists contact the system administrator'
});
} else {
res.status(httpStatus.OK).send(metrics);
}
});
} catch (error) {
res.status(httpStatus.BAD_REQUEST).send({
message: 'Error fetching dashboard metrics',
description: 'Unexpected server error in getting metrics for dashboard. If the issue persists contact the system administrator'
});
}
};

/**
* Get SHS Dashboard Analytics Metrics for bulk blogs.
*
* @function
* @async
* @param {Object} req - Express request object.
* @param {Object} res - Express response object.
* @returns {Promise<void>} Promise that resolves with metric details for bulk blogs.
* @throws {Error} If there is an issue getting metric details or sending the response.
*/

const getBulkMetrics = async (req, res) => {
try {
isAuth(req, res, async () => {
const metrics = await analyticsService.getBulkMetrics();
if (!metrics) {
res.status(httpStatus.BAD_REQUEST).send({
message: 'Error fetching dashboard metrics',
description: 'Unexpected server error in getting metrics for dashboard. If the issue persists contact the system administrator'
});
} else {
res.status(httpStatus.OK).send(metrics);
}
});
} catch (error) {
res.status(httpStatus.BAD_REQUEST).send({
message: 'Error fetching dashboard metrics',
description: 'Unexpected server error in getting metrics for dashboard. If the issue persists contact the system administrator'
});
}
}

module.exports = {
getMetrics
getMetrics,
getBlogMetrics,
getBulkMetrics
};
10 changes: 8 additions & 2 deletions server/src/app/routes/v1/analytics.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ const router = express.Router();
// REQUIRE CONTROLLERS
const { analyticsController } = require("../../controllers");

// POST REQUEST FOR ANALYTICS DATA
router.post("/metrics", analyticsController.getMetrics);
// GET REQUEST FOR ANALYTICS DATA
router.get("/metrics", analyticsController.getMetrics);

// GET REQUEST FOR ANALYTICS DATA FOR BULK BLOGS
router.get("/metrics/bulk", analyticsController.getBulkMetrics);

// GET REQUEST FOR ANALYTICS DATA FOR A SPECIFIC BLOG
router.get("/metrics/:blogId", analyticsController.getBlogMetrics);

module.exports = router;
Loading

0 comments on commit b8aaefb

Please sign in to comment.