[frontend]: Add ObjectTypeProportionPieChart

This commit is contained in:
2025-03-11 19:48:35 +00:00
parent 885a54a333
commit 77f20dac7c
6 changed files with 445 additions and 6 deletions

View File

@ -36,7 +36,7 @@ function App() {
useEffect(() => {
const handler = setTimeout(() => {
setSearchTerm(searchInput);
}, 300); // Adjust this delay as desired
}, 300);
return () => {
clearTimeout(handler);

View File

@ -1,10 +1,44 @@
import React from "react";
import React, { useState, useEffect, useMemo } from "react";
import ObjectTypeProportionPieChart from "./charts/ObjectTypeProportionPieChart";
import LoadingOverlay from "./LoadingOverlay.jsx";
const Statistics = () => {
const [transientTypes, setTransientTypes] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("https://281bc6mcm5.execute-api.us-east-1.amazonaws.com/transient_data");
if (!response.ok) throw new Error("Network response was not ok");
const rawData = await response.json();
const transientTypes = rawData
.map((item) => item.objectType)
.map((type) => type.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2'));
setTransientTypes(transientTypes);
} catch (err) {
setError("Failed to fetch data");
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <LoadingOverlay message={"Fetching data..."} />;
if (error) return <p className="text-center text-red-500">{error}</p>;
return (
<div style={{height: "100vh", width: "100vw", display: "flex", position: "relative", paddingTop: "5vh"}}>
<h1 className="text-2xl font-bold mb-4">About This Application</h1>
<p>This application provides real-time and permanent data on Irish Rail, Luas, and bus services.</p>
<div
style={{ height: "100vh", width: "100vw", display: "flex", position: "relative", paddingTop: "5vh" }}
className="min-h-screen flex flex-col items-center justify-center bg-gray-100 p-4"
>
<ObjectTypeProportionPieChart label="Current Transport Type Proportion" dataList={transientTypes} />
</div>
);
};

View File

@ -0,0 +1,45 @@
import { PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer } from "recharts";
const COLORS = ["#8884d8", "#82ca9d", "#FFBB28", "#FF8042"];
const ObjectTypeProportionPieChart = ({ label, dataList }) => {
// Count occurrences of each unique string
const typeCounts = dataList.reduce((acc, type) => {
acc[type] = (acc[type] || 0) + 1;
return acc;
}, {});
// Convert to array format for Recharts
const chartData = Object.keys(typeCounts).map((key) => ({
name: key,
value: typeCounts[key],
}));
return (
<div className="flex flex-col items-center p-4 bg-white shadow-lg rounded-lg w-full max-w-md">
<h2 className="text-xl font-semibold text-center mb-4">{label}</h2>
<ResponsiveContainer width={300} height={300}>
<PieChart>
<Pie
data={chartData}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
outerRadius={100}
fill="#8884d8"
label
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Tooltip />
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
);
};
export default ObjectTypeProportionPieChart;

View File

@ -25,6 +25,7 @@ a:hover {
}
body {
color: black;
margin: 0;
display: flex;
place-items: center;
@ -94,4 +95,8 @@ body, html, #root {
padding: 0;
box-sizing: border-box;
overflow-x: hidden;
}
.leaflet-marker-icon {
color: white;
}