diff --git a/src/components/Analytics/AnalyticsDashboard.jsx b/src/components/Analytics/AnalyticsDashboard.jsx
new file mode 100644
index 0000000000..d869ea4701
--- /dev/null
+++ b/src/components/Analytics/AnalyticsDashboard.jsx
@@ -0,0 +1,36 @@
+import React, { useState } from 'react';
+import ReviewersRequirementChart from './ReviewersRequirementChart';
+
+const durationOptions = [
+ { label: 'Last Week', value: 'lastWeek' },
+ { label: 'Last 2 weeks', value: 'last2weeks' },
+ { label: 'Last Month', value: 'lastMonth' },
+ { label: 'All Time', value: 'allTime' },
+];
+
+const AnalyticsDashboard = () => {
+ const [duration, setDuration] = useState('lastWeek');
+
+ return (
+
+
+
+
+
+
+
+
+
Reviewers Ranked by Requirement Satisfied
+
+
+
+ );
+};
+
+export default AnalyticsDashboard;
diff --git a/src/components/Analytics/PopularPRChart.jsx b/src/components/Analytics/PopularPRChart.jsx
new file mode 100644
index 0000000000..e037f3013d
--- /dev/null
+++ b/src/components/Analytics/PopularPRChart.jsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import { Bar, BarChart, LabelList, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
+
+const data = [
+ { prNumber: 'PR1234', reviews: 10 },
+ { prNumber: 'PR1235', reviews: 8 },
+ { prNumber: 'PR1236', reviews: 15 },
+ { prNumber: 'PR1237', reviews: 5 },
+ { prNumber: 'PR1238', reviews: 12 },
+ { prNumber: 'PR1231', reviews: 12 },
+ { prNumber: 'PR1256', reviews: 17 },
+ { prNumber: 'PR1255', reviews: 19 },
+ { prNumber: 'PR1254', reviews: 1 },
+ { prNumber: 'PR1253', reviews: 2 },
+ { prNumber: 'PR1252', reviews: 7 },
+ { prNumber: 'PR1251', reviews: 9 },
+ { prNumber: 'PR1250', reviews: 11 },
+ { prNumber: 'PR1249', reviews: 19 },
+ { prNumber: 'PR1248', reviews: 0 },
+ { prNumber: 'PR1242', reviews: 2 },
+ { prNumber: 'PR1241', reviews: 50 },
+];
+
+const PopularPRChart = () => {
+ const filteredData = data;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default PopularPRChart;
diff --git a/src/components/Analytics/ReviewersRequirementChart.jsx b/src/components/Analytics/ReviewersRequirementChart.jsx
new file mode 100644
index 0000000000..1b59f37f08
--- /dev/null
+++ b/src/components/Analytics/ReviewersRequirementChart.jsx
@@ -0,0 +1,121 @@
+import axios from 'axios';
+import React, { useEffect, useState } from 'react';
+import {
+ Bar,
+ BarChart,
+ LabelList,
+ Legend,
+ ResponsiveContainer,
+ Tooltip,
+ XAxis,
+ YAxis,
+} from 'recharts';
+import { ENDPOINTS } from '../../utils/URL';
+
+const getCount = (counts, key) => counts?.[key] || 0;
+
+const normalizeReviewCounts = item => {
+ const counts = item.counts || {};
+ const exceptional = getCount(counts, 'Exceptional');
+ const sufficient = getCount(counts, 'Sufficient');
+ const needsChanges = getCount(counts, 'Needs Changes');
+ const didNotReview = getCount(counts, 'Did Not Review');
+
+ return {
+ reviewer: item.reviewer,
+ exceptional,
+ sufficient,
+ needsChanges,
+ didNotReview,
+ total: exceptional + sufficient + needsChanges + didNotReview,
+ };
+};
+
+const ReviewersRequirementChart = ({ duration }) => {
+ const [data, setData] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ const [errorMessage, setErrorMessage] = useState('');
+
+ useEffect(() => {
+ const fetchAPIData = async () => {
+ setIsLoading(true);
+ setErrorMessage('');
+
+ try {
+ const token = window.localStorage.getItem('token');
+ const response = await axios.get(ENDPOINTS.GITHUB_REVIEW_SUMMARY(duration), {
+ headers: {
+ Authorization: token,
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ setData(Array.isArray(response.data) ? response.data : []);
+ } catch (err) {
+ setData([]);
+ setErrorMessage('Unable to load GitHub review data for the selected duration.');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchAPIData();
+ }, [duration]);
+
+ const sortedData = data
+ .map(normalizeReviewCounts)
+ .filter(item => item.reviewer)
+ .sort((a, b) => b.total - a.total);
+
+ if (isLoading) {
+ return Loading review data...
;
+ }
+
+ if (errorMessage) {
+ return {errorMessage}
;
+ }
+
+ if (!sortedData.length) {
+ return No review data found for this duration.
;
+ }
+
+ const rowHeight = 34;
+ const chartHeight = Math.max(sortedData.length * rowHeight, 360);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ReviewersRequirementChart;
diff --git a/src/routes.jsx b/src/routes.jsx
index f2fc47c514..5373942031 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -165,6 +165,7 @@ import RecipesLandingPage from './components/KitchenandInventory/Recipes';
import EPProtectedRoute from './components/common/EPDashboard/EPProtectedRoute';
import EPDashboard from './components/EductionPortal';
+import PopularPRChart from './components/Analytics/PopularPRChart';
import InsightWidget from './components/EductionPortal/AnalyticsDashboard/InsightsWidget';
import ReportDownloadButton from './components/EductionPortal/AnalyticsDashboard/ReportDownloadButton';
import AnnouncementsPage from './components/EductionPortal/Announcements/AnnouncementsPage';
@@ -190,6 +191,7 @@ import OptStatusPieChart from './components/OptStatusPieChart/OptStatusPieChart'
import SupportDashboard from './components/SupportPortal/SupportDashboard';
import SupportLogin from './components/SupportPortal/SupportLogin';
import SupportLogViewer from './components/SupportPortal/SupportLogViewer';
+import ReviewersRequirementChart from './components/Analytics/ReviewersRequirementChart';
import { UserRole } from './utils/enums';
import JobApplicationForm from './components/Collaboration/JobApplicationForm/JobApplicationForm';
import ReviewsInsight from './components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight';
@@ -785,6 +787,16 @@ export default (
fallback
allowedRoles={[UserRole.Owner]}
/>
+
+
+
+
+
`${APIEndpoint}/getTotalCountryCount`,
ANALYTICS_AVAILABLE_ROLES: () => `${APIEndpoint}/analytics/roles`,
+ GITHUB_REVIEW_SUMMARY: (duration, sort = 'desc') =>
+ `${APIEndpoint}/analytics/review-summary?duration=${encodeURIComponent(
+ duration,
+ )}&sort=${encodeURIComponent(sort)}`,
// Country Application Map Chart endpoints
COUNTRY_APPLICATION_DATA: (params = {}) => {
diff --git a/yarn.lock b/yarn.lock
index c053e522f7..de2c2b6d60 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2785,8 +2785,8 @@
"@parcel/watcher-darwin-arm64@2.5.1":
version "2.5.1"
- resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz"
- integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
+ resolved "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz"
+ integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
"@parcel/watcher@^2.4.1":
version "2.5.1"