Team Ai
Apppublic

harshapitla/github-actions-analytics

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
IssuesVsPRsChart.jsx180 linesDownload Raw Back to charts
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