harshapitla/github-actions-analytics
0
1import React, { useMemo } from "react";2import {3 BarChart,4 Bar,5 XAxis,6 YAxis,7 CartesianGrid,8 Tooltip,9 ResponsiveContainer,10 Cell,11 Label,12} from "recharts";13import ChartCard from "../components/charts/ChartCard";14 15function CustomTooltip({ active, payload }) {16 if (active && payload && payload.length) {17 const data = payload[0].payload;18 return (19 <div20 style={{21 background: "rgba(5, 18, 35, 0.9)",22 padding: "8px 12px",23 borderRadius: 6,24 border: "1px solid #334155",25 maxWidth: "350px",26 }}27 >28 <div style={{ marginBottom: 4, fontWeight: "bold" }}>29 {data.action_title}30 </div>31 <div style={{ color: data.verificationColor, fontWeight: "bold" }}>32 {data.isVerified ? "โ Verified" : "โ Non-Verified"}33 </div>34 <div style={{ color: "#8884d8" }}>35 Contributors: {data.no_of_contributors}36 </div>37 <div style={{ color: "#82ca9d" }}>PRs: {data.no_of_PRs}</div>38 <div style={{ color: "#fbbf24", marginTop: 4 }}>39 Efficiency Ratio: {data.efficiencyRatio}40 </div>41 <div style={{ color: "#f59e0b" }}>42 Stars: {data.no_of_stars.toLocaleString()}43 </div>44 <div style={{ fontSize: 12, color: "#94a3b8", marginTop: 4 }}>45 Publisher: {data.publisher}46 </div>47 {data.tags && (48 <div style={{ fontSize: 12, color: "#94a3b8" }}>49 Tags: {data.tags}50 </div>51 )}52 </div>53 );54 }55 return null;56}57 58export default function ContributorUtilizationChart({ rows = [] }) {59 const utilizationData = useMemo(() => {60 if (!Array.isArray(rows)) return [];61 62 // Filter for valid data63 const validRows = rows.filter((row) => {64 const contributors = parseInt(row.no_of_contributors) || 0;65 const prs = parseInt(row.no_of_PRs) || 0;66 const stars = parseInt(row.no_of_stars) || 0;67 return contributors >= 0 && prs >= 0 && stars >= 0;68 });69 70 const processedData = validRows.map((row) => {71 const contributors = parseInt(row.no_of_contributors) || 0;72 const prs = parseInt(row.no_of_PRs) || 0;73 const stars = parseInt(row.no_of_stars) || 0;74 const isVerified =75 String(row.is_action_verified).toLowerCase() === "true";76 77 // Calculate efficiency ratio: PRs per contributor78 const efficiencyRatio =79 contributors > 0 ? Math.round((prs / contributors) * 10) / 10 : 0;80 81 const verificationColor = isVerified ? "#10b981" : "#f59e0b";82 83 return {84 no_of_contributors: contributors,85 no_of_PRs: prs,86 no_of_stars: stars,87 efficiencyRatio,88 isVerified,89 verificationColor,90 action_title: row.action_title || row.name || "Unknown Action",91 publisher: row.publisher || "(unknown)",92 tags: row.tags || "",93 };94 });95 96 // Filter for actions with at least 1 contributor and sort by efficiency ratio descending97 const efficientActions = processedData98 .filter((action) => action.no_of_contributors >= 1)99 .sort((a, b) => b.efficiencyRatio - a.efficiencyRatio)100 .slice(0, 10); // Top 10 most efficient actions101 102 return efficientActions;103 }, [rows]);104 105 if (!utilizationData.length) {106 return (107 <ChartCard108 title="Top Contributor Utilization Efficiency"109 subtitle="Actions with highest PR-to-contributor ratios"110 >111 <p className="small">112 No valid data found for contributor utilization analysis.113 </p>114 </ChartCard>115 );116 }117 118 return (119 <ChartCard120 title="Top Contributor Utilization Efficiency"121 subtitle="Actions with highest PR-to-contributor ratios"122 >123 <div style={{ height: 500 }}>124 <ResponsiveContainer>125 <BarChart126 data={utilizationData}127 layout="horizontal"128 margin={{ top: 20, right: 30, left: 150, bottom: 20 }}129 >130 <CartesianGrid strokeDasharray="3 3" stroke="#334155" />131 <XAxis132 type="number"133 domain={[0, "dataMax"]}134 tick={{ fill: "#94a3b8", fontSize: 12 }}135 >136 <Label137 value="Efficiency Ratio (PRs per Contributor)"138 offset={-5}139 position="insideBottom"140 fill="#94a3b8"141 />142 </XAxis>143 <YAxis144 type="category"145 dataKey="action_title"146 tick={{ fill: "#94a3b8", fontSize: 11 }}147 width={140}148 />149 <Tooltip content={<CustomTooltip />} />150 151 <Bar dataKey="efficiencyRatio" radius={[0, 4, 4, 0]}>152 {utilizationData.map((entry, index) => (153 <Cell154 key={`cell-${index}`}155 fill={entry.verificationColor}156 fillOpacity={0.8}157 />158 ))}159 </Bar>160 </BarChart>161 </ResponsiveContainer>162 </div>163 164 <div style={{ marginTop: 20, fontSize: 14, color: "#94a3b8" }}>165 <div166 style={{167 display: "grid",168 gridTemplateColumns: "1fr 1fr",169 gap: "12px",170 marginBottom: 16,171 }}172 >173 <div174 style={{175 textAlign: "center",176 padding: "10px",177 backgroundColor: "rgba(16, 185, 129, 0.1)",178 borderRadius: "6px",179 }}180 >181 <div182 style={{183 width: 12,184 height: 12,185 backgroundColor: "#10b981",186 borderRadius: "50%",187 margin: "0 auto 6px",188 border: "2px solid white",189 }}190 ></div>191 <div style={{ fontWeight: "bold", color: "#10b981", fontSize: 12 }}>192 Verified Actions193 </div>194 <div style={{ fontSize: 11 }}>Official verification badge</div>195 </div>196 197 <div198 style={{199 textAlign: "center",200 padding: "10px",201 backgroundColor: "rgba(245, 158, 11, 0.1)",202 borderRadius: "6px",203 }}204 >205 <div206 style={{207 width: 12,208 height: 12,209 backgroundColor: "#f59e0b",210 borderRadius: "50%",211 margin: "0 auto 6px",212 border: "2px solid white",213 }}214 ></div>215 <div style={{ fontWeight: "bold", color: "#f59e0b", fontSize: 12 }}>216 Non-Verified Actions217 </div>218 <div style={{ fontSize: 11 }}>Community maintained</div>219 </div>220 </div>221 222 <div223 style={{224 display: "grid",225 gridTemplateColumns: "1fr 1fr",226 gap: "12px",227 marginBottom: 16,228 }}229 >230 <div231 style={{232 textAlign: "center",233 padding: "10px",234 backgroundColor: "rgba(16, 185, 129, 0.1)",235 borderRadius: "6px",236 }}237 >238 <div style={{ fontSize: 18, marginBottom: 8 }}>๐</div>239 <div style={{ fontWeight: "bold", color: "#10b981", fontSize: 12 }}>240 High Efficiency241 </div>242 <div style={{ fontSize: 11 }}>243 Contributors producing many PRs - healthy collaboration244 </div>245 </div>246 247 <div248 style={{249 textAlign: "center",250 padding: "10px",251 backgroundColor: "rgba(239, 68, 68, 0.1)",252 borderRadius: "6px",253 }}254 >255 <div style={{ fontSize: 18, marginBottom: 8 }}>๐</div>256 <div style={{ fontWeight: "bold", color: "#ef4444", fontSize: 12 }}>257 Low Efficiency258 </div>259 <div style={{ fontSize: 11 }}>260 Contributors producing few PRs - potential bottlenecks261 </div>262 </div>263 </div>264 265 <div266 style={{267 textAlign: "center",268 fontSize: 12,269 borderTop: "1px solid #334155",270 paddingTop: 12,271 }}272 >273 <p>274 <strong>Efficiency Ratio:</strong> PRs รท Contributors275 <br />276 <em>277 Showing top 10 most efficient actions โข Higher ratios indicate278 better contributor utilization279 </em>280 </p>281 </div>282 </div>283 </ChartCard>284 );285}286 