Team Ai
Apppublic

harshapitla/github-actions-analytics

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
EvolutionScatter.jsx111 linesDownload Raw Back to charts
1import React, { useMemo } from "react";2import {3  ScatterChart,4  Scatter,5  XAxis,6  YAxis,7  ZAxis,8  CartesianGrid,9  Tooltip,10  ResponsiveContainer,11  Label,12} from "recharts";13import ChartCard from "../components/charts/ChartCard";14 15const toNumber = (value) => {16  const num = Number(value);17  return Number.isFinite(num) ? num : 0;18};19 20const toTimestamp = (value) => {21  if (!value) return 0;22  const date = new Date(value);23  return Number.isNaN(date.getTime()) ? 0 : date.getTime();24};25 26function TooltipContent({ payload }) {27  if (!payload || !payload.length) return null;28  const p = payload[0].payload;29  return (30    <div31      style={{32        background: "rgba(255,255,255,0.95)",33        color: "#0f172a",34        padding: "8px 12px",35        borderRadius: 6,36        border: "1px solid #e2e8f0",37        boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1)",38      }}39    >40      <div>41        <strong>{p.name}</strong>42      </div>43      <div className="small">Publisher: {p.publisher}</div>44      <div className="small">Downloads: {Math.round(p.downloads).toLocaleString()}</div>45      <div className="small">Rating: {p.rating?.toFixed?.(1) ?? p.rating}</div>46      <div className="small">47        Last update: {p.lastUpdated ? new Date(p.lastUpdated).toLocaleDateString() : "Unknown"}48      </div>49    </div>50  );51}52 53export default function EvolutionScatter({ rows = [] }) {54  const data = useMemo(() => {55    if (!Array.isArray(rows)) return [];56    return rows57      .map((record) => {58        const downloads = Math.max(1, toNumber(record.downloads_count));59        const rating = toNumber(record.rating);60        const lastUpdated = record.last_updated || record.last_update || record.published_at;61        return {62          x: downloads,63          y: rating,64          z: toTimestamp(lastUpdated),65          name: record.name,66          publisher: record.publisher,67          downloads,68          rating,69          lastUpdated,70        };71      })72      .filter((point) => point.y > 0);73  }, [rows]);74 75  if (!data.length) {76    return (77      <ChartCard title="Popularity vs. Quality" subtitle="Downloads vs. rating (log scale).">78        <p className="small">Not enough rating information to compare extensions.</p>79      </ChartCard>80    );81  }82 83  return (84    <ChartCard title="Popularity vs. Quality" subtitle="Downloads vs. rating, bubble size = update recency.">85      <div style={{ height: 360 }}>86        <ResponsiveContainer>87          <ScatterChart margin={{ top: 20, right: 30, left: 20, bottom: 40 }}>88            <CartesianGrid strokeDasharray="3 3" strokeOpacity={0.3} />89            <XAxis90              type="number"91              dataKey="x"92              name="downloads"93              scale="log"94              domain={["dataMin", "dataMax"]}95              tickFormatter={(value) => (value >= 1_000_000 ? `${value / 1_000_000}M` : value >= 1_000 ? `${value / 1_000}k` : value)}96            >97              <Label value="Downloads (Log Scale)" offset={-25} position="insideBottom" />98            </XAxis>99            <YAxis type="number" dataKey="y" name="rating" domain={[1, 5]} ticks={[1, 2, 3, 4, 5]}>100              <Label value="Rating" angle={-90} position="insideLeft" style={{ textAnchor: "middle" }} />101            </YAxis>102            <ZAxis type="number" dataKey="z" range={[10, 200]} />103            <Tooltip content={<TooltipContent />} />104            <Scatter name="extensions" data={data} fill="#7c3aed" fillOpacity={0.6} />105          </ScatterChart>106        </ResponsiveContainer>107      </div>108    </ChartCard>109  );110}111