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"