Skip to content

Commit a404d5f

Browse files
committed
Scope<COVID>: Add CountryTrendChart component and integrate into Covid page
-> Fetch /dayone/country/{slug}.(But i think API is not working properly) -> Loading state while fetching. -> Cumulative confirmed over time line. -> Abort or guard against stale updates on country change. -> Placeholder swapped for chart component.
1 parent c71d944 commit a404d5f

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { useEffect, useState, useRef } from 'react';
2+
import Loading from './Loading.jsx';
3+
import ChartPlaceholder from './ChartPlaceholder.jsx';
4+
5+
// Tiny, dependency-free line chart that accepts array of {date, value}
6+
function SimpleLineChart({ data, width = 600, height = 200 }) {
7+
if (!data || data.length === 0) return <ChartPlaceholder label="No data" />;
8+
9+
const padding = 20;
10+
const dates = data.map(d => new Date(d.date));
11+
const values = data.map(d => d.value);
12+
const minY = Math.min(...values);
13+
const maxY = Math.max(...values);
14+
15+
const x = i => {
16+
const t = i / (data.length - 1 || 1);
17+
return padding + t * (width - padding * 2);
18+
};
19+
const y = v => {
20+
const range = maxY - minY || 1;
21+
const t = (v - minY) / range;
22+
return height - padding - t * (height - padding * 2);
23+
};
24+
25+
const path = data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${x(i)} ${y(d.value)}`).join(' ');
26+
27+
return (
28+
<svg width="100%" viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none">
29+
<defs>
30+
<linearGradient id="g" x1="0" x2="0" y1="0" y2="1">
31+
<stop offset="0%" stopColor="#60a5fa" stopOpacity="0.4" />
32+
<stop offset="100%" stopColor="#60a5fa" stopOpacity="0.05" />
33+
</linearGradient>
34+
</defs>
35+
<path d={`${path} L ${x(data.length - 1)} ${height - padding} L ${x(0)} ${height - padding} Z`} fill="url(#g)" />
36+
<path d={path} fill="none" stroke="#2563eb" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" />
37+
{data.map((d, i) => (
38+
<circle key={d.date} cx={x(i)} cy={y(d.value)} r={i === data.length - 1 ? 3.5 : 2} fill="#1e3a8a" />
39+
))}
40+
</svg>
41+
);
42+
}
43+
44+
export default function CountryTrendChart({ slug }) {
45+
const [loading, setLoading] = useState(false);
46+
const [error, setError] = useState(null);
47+
const [series, setSeries] = useState([]);
48+
const abortRef = useRef();
49+
const lastSlugRef = useRef(slug);
50+
51+
useEffect(() => {
52+
if (!slug) {
53+
lastSlugRef.current = slug;
54+
setSeries([]);
55+
setError(null);
56+
setLoading(false);
57+
if (abortRef.current) abortRef.current.abort();
58+
return;
59+
}
60+
61+
const controller = new AbortController();
62+
abortRef.current = controller;
63+
let mounted = true;
64+
65+
async function load() {
66+
setLoading(true);
67+
setError(null);
68+
setSeries([]);
69+
try {
70+
const res = await fetch(`https://api.covid19api.com/dayone/country/${slug}`, { signal: controller.signal });
71+
if (!res.ok) throw new Error('Failed to fetch country trends');
72+
const json = await res.json();
73+
74+
if (!mounted || lastSlugRef.current !== slug) return;
75+
76+
const seriesData = json.map(item => ({ date: item.Date, value: Number(item.Confirmed || 0) }));
77+
78+
seriesData.sort((a, b) => new Date(a.date) - new Date(b.date));
79+
80+
setSeries(seriesData);
81+
} catch (e) {
82+
if (e.name === 'AbortError') return;
83+
setError(e);
84+
} finally {
85+
if (mounted) setLoading(false);
86+
}
87+
}
88+
89+
lastSlugRef.current = slug;
90+
load();
91+
92+
return () => {
93+
mounted = false;
94+
controller.abort();
95+
};
96+
}, [slug]);
97+
98+
if (!slug) return <ChartPlaceholder label="Select a country to view trends" />;
99+
if (loading && series.length === 0) return <Loading />;
100+
if (error) return <ErrorMessage error={error} />;
101+
102+
return (
103+
<div className="country-trend-chart">
104+
<h3>Confirmed Cases — Cumulative</h3>
105+
<SimpleLineChart data={series} />
106+
</div>
107+
);
108+
}

src/pages/Covid.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { useEffect, useState, useCallback } from 'react';
2121
import Loading from '../components/Loading.jsx';
2222
import ErrorMessage from '../components/ErrorMessage.jsx';
2323
import Card from '../components/Card.jsx';
24+
import CountryTrendChart from '../components/CountryTrendChart.jsx';
2425

2526
export default function Covid() {
2627
const [summary, setSummary] = useState(null);
@@ -82,7 +83,7 @@ export default function Covid() {
8283
<p>Total Deaths: {selected.TotalDeaths.toLocaleString()}</p>
8384
</Card>
8485
)}
85-
{/* TODO: Add daily trends chart using ChartPlaceholder */}
86+
<CountryTrendChart slug={country} />
8687
</div>
8788
);
8889
}

0 commit comments

Comments
 (0)