Skip to content

Commit

Permalink
improve charts
Browse files Browse the repository at this point in the history
  • Loading branch information
Alberto Gutierrez committed Oct 23, 2024
1 parent cdb9ea7 commit 892b4be
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 61 deletions.
101 changes: 101 additions & 0 deletions website/src/components/MetricChartLine/MetricChartLine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from "react";
import { Stats } from "../Repo/Repo";
import { LineChart } from "@mui/x-charts";

const getLineXChart = (data: string[]) => {
return data.map(d => new Date(d))
}

const yearFormatter = (date: Date) => `${date.getMonth()+1}-${date.getDate()}-${date.getFullYear()}`;



export const MetricChartLine = (props: {data: {[key: string]: Stats}}) => {
const [forks, setForks] = React.useState<boolean>(false);
const [issues, setIssues] = React.useState<boolean>(false);
const [stars, setStars] = React.useState<boolean>(true);
const [size, setSize] = React.useState<boolean>(false);
const [all, setAll] = React.useState<boolean>(false);

const setEverything = () => {
setAll(!all)
setForks(!all)
setIssues(!all)
setStars(!all)
setSize(!all)
}

const getSeriesLine = (data: {[key: string]: Stats}) => {
Object.values(data).map(v => v.forks)
const series = []
forks && series.push(
{
label: 'Forks',
data: Object.values(data).map(v => v.forks),
showMark: false,
}
)
stars && series.push(
{
label: 'Stars',
data: Object.values(data).map(v => v.stars),
showMark: false,
}
)
issues && series.push(
{
label: 'Issues',
data: Object.values(data).map(v => v.issues),
showMark: false,
}
)
size && series.push(
{
label: 'Size',
data: Object.values(data).map(v => v.size/1024),
showMark: false,
}
)
return {
series: series
};
}

return (
<>
<div style={{display: "flex"}}>
<div style={{marginLeft: "10px"}}>
<input type="checkbox" className="form-check-input" checked={stars} onClick={() => setStars(!stars)}/>
<label className="form-check-label" style={{marginLeft: "10px"}}>Starts</label>
</div>
<div style={{marginLeft: "10px"}}>
<input type="checkbox" className="form-check-input" checked={forks} onClick={() => setForks(!forks)}/>
<label className="form-check-label" style={{marginLeft: "10px"}}>Forks</label>
</div>
<div style={{marginLeft: "10px"}}>
<input type="checkbox" className="form-check-input" checked={issues} onClick={() => setIssues(!issues)}/>
<label className="form-check-label" style={{marginLeft: "10px"}}>Issues</label>
</div>
<div style={{marginLeft: "10px"}}>
<input type="checkbox" className="form-check-input" checked={size} onClick={() => setSize(!size)}/>
<label className="form-check-label" style={{marginLeft: "10px"}}>Size (MB)</label>
</div>
<div style={{marginLeft: "10px"}}>
<input type="checkbox" className="form-check-input" checked={all} onClick={() => setEverything()}/>
<label className="form-check-label" style={{marginLeft: "10px"}}>{!all? "All" : "Remove all"}</label>
</div>
</div>
<div className="ct-chart" id="chartHours">
<LineChart
xAxis={[{ data: getLineXChart(Object.keys(props.data)), scaleType: 'time', valueFormatter: yearFormatter }]}
series={getSeriesLine(props.data).series.map((series) => ({
...series
}))}
width={800}
height={300}
/>
</div>
</>
)

}
41 changes: 32 additions & 9 deletions website/src/components/Repo/Repo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,41 @@ export interface Repository {
language: {[key: string]: number}
}

const getLastDate = (stats: string[]) => {
const getLastDate = (stats: string[]): [Date, Date] => {
let arrayDates = stats.map((da) => new Date(da) );
return arrayDates.reduce((a,b) => a > b ? a : b)
const latest = arrayDates.reduce((a,b) => a > b ? a : b)
let nearest = Infinity
let winner = -1
let latestMonth = new Date(latest.getTime());
if (latestMonth.getMonth() === 0) {
// It's January
latestMonth.setFullYear(latestMonth.getFullYear() - 1 )
latestMonth.setMonth(11)
}else {
latestMonth.setMonth(latestMonth.getMonth() - 1)
}
const latestTime = latestMonth.getTime()
arrayDates.forEach((date, index) => {
let distance = Math.abs(date.getTime() - latestTime)
if (distance < nearest) {
nearest = distance
winner = index
}
})
const closest = arrayDates[winner]
return [latest,closest]
}


const formatDate = (d: Date) => {
return `${d.getFullYear()}-${('0' + (d.getMonth()+1)).slice(-2)}-${('0' + d.getDate()).slice(-2)}`
}

export const Repo = (props: {repo: Repository}) => {
const date = getLastDate(Object.keys(props.repo.metrics));
const met = props.repo.metrics[formatDate(date)]
const [latest, closest] = getLastDate(Object.keys(props.repo.metrics));
const met = props.repo.metrics[formatDate(latest)];
const formatClosest = formatDate(closest);
const metLastMonth = props.repo.metrics[formatClosest];
return (
<Card>
<Card.Body>
Expand Down Expand Up @@ -70,17 +93,17 @@ export const Repo = (props: {repo: Repository}) => {
</Row>
<Row><Form.Label column sm={1} style={{fontWeight: "bold"}}>Stats</Form.Label></Row>
<Row>
<Col lg="3" sm="6"> <Stat name={"forks"} value={met.forks} /> </Col>
<Col lg="3" sm="6"> <Stat name={"issues"} value={met.issues} /> </Col>
<Col lg="3" sm="6"> <Stat name={"starts"} value={met.stars} /> </Col>
<Col lg="3" sm="6"> <Stat name={"size"} value={met.size} /> </Col>
<Col lg="3" sm="6"> <Stat name={"forks"} value={met.forks} previous={metLastMonth.forks} previousDate={formatClosest} /> </Col>
<Col lg="3" sm="6"> <Stat name={"issues"} value={met.issues} previous={metLastMonth.issues} previousDate={formatClosest}/> </Col>
<Col lg="3" sm="6"> <Stat name={"starts"} value={met.stars} previous={metLastMonth.stars} previousDate={formatClosest}/> </Col>
<Col lg="3" sm="6"> <Stat name={"size"} value={met.size} previous={metLastMonth.size} previousDate={formatClosest}/></Col>
</Row>
</Card.Body>
<Card.Footer>
<hr></hr>
<div className="stats">
<i className="fas fa-clock mr-1"></i>
Last update: {date.toLocaleDateString()}
Last update: {latest.toLocaleDateString()}
</div>
</Card.Footer>
</Card>
Expand Down
47 changes: 41 additions & 6 deletions website/src/components/Stat/Stat.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from "react";
import { Card, Col, Row } from "react-bootstrap";

import { Badge, Card, Col, OverlayTrigger, Row, Tooltip } from "react-bootstrap";
export const convertBytes = (bytes: number, options: { useBinaryUnits?: boolean; decimals?: number } = {}): string => {
const { useBinaryUnits = false, decimals = 2} = options;

Expand All @@ -18,7 +17,11 @@ export const convertBytes = (bytes: number, options: { useBinaryUnits?: boolean;
return `${(bytes / Math.pow(base, i)).toFixed(decimals)} ${units[i]}`;
}

export const Stat = (props: {name: string, value: number}) => {
const iconDown = "fas fa-arrow-down"
const iconUp = "fas fa-arrow-up"
const equals = "fas fa-equals"

export const Stat = (props: {name: string, value: number, previous: number, previousDate: string}) => {
var icon = <i className="fas fa-code text-info"/>

switch(props.name) {
Expand All @@ -39,19 +42,51 @@ export const Stat = (props: {name: string, value: number}) => {
break;
}

const getContentBadge = () => {
const value = props.value - props.previous;
let icon = iconUp
icon = value < 0 ? iconDown : value === 0 ? equals : iconUp
if (props.name === "issues") {
icon += value > 0 ? " text-danger" : value < 0 ? " text-success" : " text"
} else {
icon += value > 0 ? " text-success" : value < 0 ? " text-danger" : " text"
}

return (
<>
<i className={icon}></i> {props.name === "size" ? convertBytes(value*1024): value}
</>
)
}

return (
<Card className="card-stats">
<Card.Body>
<Row>
<Col xs="5">
<Col xs="4">
<div className="icon-big text-center icon-warning">
{icon}
</div>
</Col>
<Col xs="7">
<Col xs="8">
<div className="numbers">
<p className="card-category">{props.name}</p>
<Card.Title as="h4">{props.name === "size" ? convertBytes(props.value*1024): props.value}</Card.Title>
<Card.Title as="h4">
<OverlayTrigger
placement={"bottom"}
overlay={
<Tooltip id={`tooltip-${props.name}`}>
Metric from {props.previousDate}
</Tooltip>
}
>

<Badge pill bg="light" text="dark" style={{fontSize: "12px", marginRight: "5px"}}>
{getContentBadge()}
</Badge>
</OverlayTrigger>
{props.name === "size" ? convertBytes(props.value*1024): props.value}
</Card.Title>
</div>
</Col>
</Row>
Expand Down
50 changes: 4 additions & 46 deletions website/src/views/Metrics.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from "react";
import { Accordion, Badge, Card, Col, Container, Row, Stack } from "react-bootstrap";
import { convertBytes } from "../components/Stat/Stat";
import { Repo, Repository, Stats } from "../components/Repo/Repo";
import { Repo, Repository } from "../components/Repo/Repo";
import metrics from "../data/metrics.json";
import { LineChart, PieChart } from "@mui/x-charts";
import { PieChart } from "@mui/x-charts";
import { Box, Slider } from "@mui/material";
import { MetricChartLine } from "../components/MetricChartLine/MetricChartLine";

export interface MetricsT {
repositories: {[key: string] : Repository};
Expand All @@ -14,40 +15,7 @@ const getPieDataChart = (data: {[key:string]: number}) => {
return Object.keys(data).map((key, index) => { return {"id": 0, "value": data[key], "label": key}})
}

const getLineXChart = (data: string[]) => {
return data.map(d => new Date(d))
}

const getSeriesLine = (data: {[key: string]: Stats}) => {
Object.values(data).map(v => v.forks)

return {
series: [
{
label: 'Forks',
data: Object.values(data).map(v => v.forks),
showMark: false,
},
{
label: 'Stars',
data: Object.values(data).map(v => v.stars),
showMark: false,
},
{
label: 'Issues',
data: Object.values(data).map(v => v.issues),
showMark: false,
},
{
label: 'Size',
data: Object.values(data).map(v => v.size/1024),
showMark: false,
},
]
};
}

const yearFormatter = (date: Date) => `${date.getMonth()+1}-${date.getDate()}-${date.getFullYear()}`;

const Metrics = () => {
const [pieLangSlice, setPieLangSlice] = React.useState(5);
Expand Down Expand Up @@ -78,19 +46,9 @@ const Metrics = () => {
<Card>
<Card.Header>
<Card.Title as="h4">Community grow</Card.Title>
<p className="card-category">Forks / Starts / Issues / Size (KB)</p>
</Card.Header>
<Card.Body>
<div className="ct-chart" id="chartHours">
<LineChart
xAxis={[{ data: getLineXChart(Object.keys(data.repositories[repo].metrics)), scaleType: 'time', valueFormatter: yearFormatter }]}
series={getSeriesLine(data.repositories[repo].metrics).series.map((series) => ({
...series
}))}
width={800}
height={300}
/>
</div>
<MetricChartLine data={data.repositories[repo].metrics} />
</Card.Body>

</Card>
Expand Down

0 comments on commit 892b4be

Please sign in to comment.