harshapitla/github-actions-analytics
0
1import React, { useMemo } from "react";2import {3 ComposedChart,4 Bar,5 XAxis,6 YAxis,7 CartesianGrid,8 Tooltip,9 ResponsiveContainer,10 Legend,11} from "recharts";12import ChartCard from "../components/charts/ChartCard";13 14function CustomTooltip({ active, payload, label }) {15 if (active && payload && payload.length) {16 return (17 <div18 style={{19 background: "rgba(5, 18, 35, 0.9)",20 padding: "8px 12px",21 borderRadius: 6,22 border: "1px solid #334155",23 }}24 >25 <div style={{ marginBottom: 4, fontWeight: "bold" }}>26 {label === 027 ? "No Issues/PRs"28 : `${label} ${label === 1 ? "item" : "items"}`}29 </div>30 {payload.map((entry, index) => (31 <div key={index} style={{ color: entry.color, marginTop: 2 }}>32 {entry.name}: {entry.value} actions33 </div>34 ))}35 </div>36 );37 }38 return null;39}40 41export default function IssuesVsPRsChart({ rows = [] }) {42 const distributionData = useMemo(() => {43 if (!Array.isArray(rows)) return [];44 45 // Create frequency distributions for issues and PRs46 const issuesCount = {};47 const prsCount = {};48 49 rows.forEach((row) => {50 const issues = parseInt(row.no_of_open_issues) || 0;51 const prs = parseInt(row.no_of_PRs) || 0;52 53 // Count frequency of each issue count54 issuesCount[issues] = (issuesCount[issues] || 0) + 1;55 56 // Count frequency of each PR count57 prsCount[prs] = (prsCount[prs] || 0) + 1;58 });59 60 // Find the maximum count to determine range61 const maxIssues = Math.max(...Object.keys(issuesCount).map(Number));62 const maxPRs = Math.max(...Object.keys(prsCount).map(Number));63 const maxCount = Math.max(maxIssues, maxPRs);64 65 // Create data points for each count from 0 to max66 const data = [];67 for (let i = 0; i <= Math.min(maxCount, 20); i++) {68 // Limit to 20 to keep chart readable69 data.push({70 count: i,71 issues: issuesCount[i] || 0,72 prs: prsCount[i] || 0,73 });74 }75 76 return data;77 }, [rows]);78 79 if (!distributionData.length) {80 return (81 <ChartCard82 title="Open Issues vs Pull Requests"83 subtitle="Distribution comparison of issues and pull requests"84 >85 <p className="small">No issue/PR data available to display.</p>86 </ChartCard>87 );88 }89 90 // Calculate summary statistics91 const totalActions = rows.length;92 const actionsWithIssues = rows.filter(93 (row) => parseInt(row.no_of_open_issues) > 094 ).length;95 const actionsWithPRs = rows.filter(96 (row) => parseInt(row.no_of_PRs) > 097 ).length;98 const avgIssues = (99 rows.reduce((sum, row) => sum + (parseInt(row.no_of_open_issues) || 0), 0) /100 totalActions101 ).toFixed(2);102 const avgPRs = (103 rows.reduce((sum, row) => sum + (parseInt(row.no_of_PRs) || 0), 0) /104 totalActions105 ).toFixed(2);106 107 return (108 <ChartCard109 title="Open Issues vs Pull Requests"110 subtitle="Distribution comparison showing development activity patterns"111 >112 <div style={{ height: 400 }}>113 <ResponsiveContainer>114 <ComposedChart115 data={distributionData}116 margin={{ top: 20, right: 30, left: 20, bottom: 60 }}117 >118 <CartesianGrid strokeDasharray="3 3" stroke="#334155" />119 <XAxis120 dataKey="count"121 stroke="#94a3b8"122 label={{123 value: "Number of Issues/PRs",124 position: "insideBottom",125 offset: -10,126 }}127 tickFormatter={(value) => value.toString()}128 />129 <YAxis130 stroke="#94a3b8"131 label={{132 value: "Number of Actions",133 angle: -90,134 position: "insideLeft",135 }}136 />137 <Tooltip content={<CustomTooltip />} />138 <Legend />139 <Bar140 dataKey="issues"141 name="Actions with X Issues"142 fill="#ef4444"143 fillOpacity={0.7}144 radius={[2, 2, 0, 0]}145 />146 <Bar147 dataKey="prs"148 name="Actions with X PRs"149 fill="#10b981"150 fillOpacity={0.7}151 radius={[2, 2, 0, 0]}152 />153 </ComposedChart>154 </ResponsiveContainer>155 </div>156 157 <div158 style={{159 marginTop: 16,160 fontSize: 12,161 color: "#94a3b8",162 textAlign: "center",163 }}164 >165 <div>166 📊 Total Actions: {totalActions.toLocaleString()} • Avg Issues:{" "}167 {avgIssues} • Avg PRs: {avgPRs}168 </div>169 <div style={{ marginTop: 4 }}>170 {actionsWithIssues} actions have issues • {actionsWithPRs} actions171 have PRs172 </div>173 <div style={{ marginTop: 4, fontSize: 11 }}>174 Red bars: Issue distribution • Green bars: PR distribution175 </div>176 </div>177 </ChartCard>178 );179}180 