Skip to content

Commit 7a7fca3

Browse files
committed
Add plotting component
1 parent b855d35 commit 7a7fca3

5 files changed

Lines changed: 422 additions & 26 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"use client"
2+
3+
import {
4+
CartesianGrid,
5+
Legend,
6+
Line,
7+
LineChart,
8+
ResponsiveContainer,
9+
Tooltip,
10+
XAxis,
11+
YAxis,
12+
} from "recharts"
13+
14+
export type DailyStats = {
15+
day: number
16+
average: number
17+
max: number
18+
min: number
19+
}
20+
21+
const SERIES = [
22+
{ key: "max", name: "Max", color: "var(--chart-series-max)" },
23+
{ key: "average", name: "Average", color: "var(--chart-series-average)" },
24+
{ key: "min", name: "Min", color: "var(--chart-series-min)" },
25+
] as const
26+
27+
function endLabel(color: string, name: string, dataLength: number) {
28+
return (props: any) => {
29+
const { x, y, index } = props
30+
if (index !== dataLength - 1) {
31+
return <g key={`end-label-${name}`} />
32+
}
33+
return (
34+
<g key={`end-label-${name}`}>
35+
<circle cx={x} cy={y} r={4} fill={color} stroke="var(--background)" strokeWidth={2} />
36+
<text x={x + 8} y={y} dy={4} fontSize={12} fill="var(--foreground)">
37+
{name}
38+
</text>
39+
</g>
40+
)
41+
}
42+
}
43+
44+
export default function InflammationChart({ data }: { data: DailyStats[] }) {
45+
return (
46+
<div className="w-full h-100">
47+
<ResponsiveContainer width="100%" height="100%">
48+
<LineChart data={data} margin={{ top: 8, right: 56, bottom: 8, left: 0 }}>
49+
<CartesianGrid vertical={false} stroke="var(--chart-grid)" />
50+
<XAxis
51+
dataKey="day"
52+
stroke="var(--chart-axis)"
53+
tick={{ fill: "var(--chart-muted)", fontSize: 12 }}
54+
label={{ value: "Day", position: "insideBottom", offset: -4, fill: "var(--chart-muted)", fontSize: 12 }}
55+
/>
56+
<YAxis
57+
stroke="var(--chart-axis)"
58+
tick={{ fill: "var(--chart-muted)", fontSize: 12 }}
59+
label={{ value: "Inflammation", angle: -90, position: "insideLeft", fill: "var(--chart-muted)", fontSize: 12 }}
60+
/>
61+
<Tooltip
62+
cursor={{ stroke: "var(--chart-axis)", strokeWidth: 1 }}
63+
contentStyle={{ background: "var(--background)", border: "1px solid var(--chart-grid)" }}
64+
labelStyle={{ color: "var(--foreground)" }}
65+
/>
66+
<Legend wrapperStyle={{ color: "var(--foreground)", fontSize: 12 }} />
67+
{SERIES.map((series) => (
68+
<Line
69+
key={series.key}
70+
type="monotone"
71+
dataKey={series.key}
72+
name={series.name}
73+
stroke={series.color}
74+
strokeWidth={2}
75+
dot={false}
76+
activeDot={{ r: 4, fill: series.color, stroke: "var(--background)", strokeWidth: 2 }}
77+
label={endLabel(series.color, series.name, data.length)}
78+
isAnimationActive={false}
79+
/>
80+
))}
81+
</LineChart>
82+
</ResponsiveContainer>
83+
</div>
84+
)
85+
}

code/next-tutorial-application/app/data/[filename]/page.tsx

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1+
import InflammationChart, { DailyStats } from "./inflammation-chart"
2+
13
type Dataset = {
24
columns: number[]
35
index: number[]
46
data: number[][]
57
}
68

9+
function toDailyStats(dataset: Dataset): DailyStats[] {
10+
return dataset.columns.map((day, columnIndex) => {
11+
const values = dataset.data.map((row) => row[columnIndex])
12+
return {
13+
day,
14+
average: values.reduce((sum, value) => sum + value, 0) / values.length,
15+
max: Math.max(...values),
16+
min: Math.min(...values),
17+
}
18+
})
19+
}
20+
721
async function getDataset(filename: string): Promise<Dataset | null> {
822
const response = await fetch(`http://localhost:8000/data/${filename}`)
923

@@ -28,35 +42,12 @@ export default async function DataView({ params }: { params: Promise<{ filename:
2842
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
2943
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
3044
<h1 className="mb-4">Web Development Tutorial</h1>
31-
<div>
45+
<div className="w-full">
3246
<h2 className="mb-4">{filename}</h2>
3347
{dataset === null ? (
3448
<p>Data file not found.</p>
3549
) : (
36-
<div className="overflow-x-auto">
37-
<table className="border-collapse">
38-
<thead>
39-
<tr>
40-
{dataset.columns.map((column) => (
41-
<th key={column} className="border p-2 text-sm font-normal">
42-
{column}
43-
</th>
44-
))}
45-
</tr>
46-
</thead>
47-
<tbody>
48-
{dataset.data.map((row, rowIndex) => (
49-
<tr key={dataset.index[rowIndex]}>
50-
{row.map((value, columnIndex) => (
51-
<td key={columnIndex} className="border p-2 text-sm text-center">
52-
{value}
53-
</td>
54-
))}
55-
</tr>
56-
))}
57-
</tbody>
58-
</table>
59-
</div>
50+
<InflammationChart data={toDailyStats(dataset)} />
6051
)}
6152
</div>
6253
</main>

code/next-tutorial-application/app/globals.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
:root {
44
--background: #ffffff;
55
--foreground: #171717;
6+
--chart-grid: #e1e0d9;
7+
--chart-axis: #c3c2b7;
8+
--chart-muted: #898781;
9+
--chart-series-average: #2a78d6;
10+
--chart-series-max: #eb6834;
11+
--chart-series-min: #1baf7a;
612
}
713

814
@theme inline {
@@ -16,6 +22,11 @@
1622
:root {
1723
--background: #0a0a0a;
1824
--foreground: #ededed;
25+
--chart-grid: #2c2c2a;
26+
--chart-axis: #383835;
27+
--chart-series-average: #3987e5;
28+
--chart-series-max: #d95926;
29+
--chart-series-min: #199e70;
1930
}
2031
}
2132

code/next-tutorial-application/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"dependencies": {
1212
"next": "16.2.6",
1313
"react": "19.2.4",
14-
"react-dom": "19.2.4"
14+
"react-dom": "19.2.4",
15+
"recharts": "^3.10.1"
1516
},
1617
"devDependencies": {
1718
"@tailwindcss/postcss": "^4",

0 commit comments

Comments
 (0)