diff --git a/src/components/graphs/DummyGraph.tsx b/src/components/graphs/DummyGraph.tsx deleted file mode 100644 index c7cd5be..0000000 --- a/src/components/graphs/DummyGraph.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as d3 from 'd3' -import { useRef, useEffect } from 'react' -import { Incident } from '@/types' -import { calculateBounds } from '@/utils' - -export default function DummyGraph({ incidents, bounds }: { incidents: [string, Incident][]; bounds: ReturnType }) { - const containerRef = useRef(null) - const d3Ref = useRef(null) - - useEffect(() => { - function render() { - if (d3Ref.current) { - const svg = d3.select(d3Ref.current) - // Clear previous contents - svg.selectAll('*').remove() - - // Set dimensions - const width = 400 - const height = 200 - const margin = { top: 20, right: 30, bottom: 30, left: 40 } - - svg.attr('preserveAspectRatio', 'xMinYMin meet').attr('viewBox', `0 0 ${width} ${height}`) - - // Create scales - const x = d3 - .scaleTime() - .domain([new Date(bounds.minYear, 0, 1), new Date(bounds.maxYear + 1, 0, 1)]) - .range([margin.left, width - margin.right]) - const y = d3 - .scaleLinear() - .domain([0, 7]) - .nice() - .range([height - margin.bottom, margin.top]) - - // Create axes - const xTicks = width / 100 - const yTicks = height / 50 - svg - .append('g') - .attr('transform', `translate(0, ${height - margin.bottom})`) - .call(d3.axisBottom(x).ticks(xTicks)) - svg.append('g').attr('transform', `translate(${margin.left}, 0)`).call(d3.axisLeft(y).ticks(yTicks)) - - // Create points - svg - .selectAll('.point') - .data(incidents) - .enter() - .append('circle') - .attr('class', 'point') - .attr('cx', (d) => x(new Date(d[1].dateString))) - .attr('cy', y(5)) - .attr('r', 5) - .attr('fill', 'steelblue') - } - } - addEventListener('resize', render) - render() - return () => removeEventListener('resize', render) - }, [incidents]) - - return ( -
- -
- ) -} \ No newline at end of file diff --git a/src/components/graphs/IncidentsStats.tsx b/src/components/graphs/IncidentsStats.tsx new file mode 100644 index 0000000..8ffbea5 --- /dev/null +++ b/src/components/graphs/IncidentsStats.tsx @@ -0,0 +1,94 @@ +import { DB, Incident } from '@/types' +import { calculateBounds } from '@/utils' +import { useMemo } from 'react' + +export default function IncidentsStats({ + data, + incidents, + bounds, +}: { + data: DB + incidents: [string, Incident][] + bounds: ReturnType +}) { + const stats = useMemo(() => { + const totalincidents = bounds.totalCount + + const countriesSet = new Set() + const departmentsSet = new Set() + const municipalitiesSet = new Set() + const dates: Date[] = [] + const typesSet = new Set() + + incidents.forEach(([_, incident]) => { + countriesSet.add(incident.country) + departmentsSet.add(incident.department) + municipalitiesSet.add(incident.municipality) + dates.push(new Date(incident.dateString)) + typesSet.add(incident.typeID as string) + }) + + const earliestDate = new Date(Math.min(...dates.map((date) => date.getTime()))) + const latestDate = new Date(Math.max(...dates.map((date) => date.getTime()))) + + const categoriesSet = new Set() + typesSet.forEach((typeID) => { + const categoryID = data.Types[typeID].categoryID + categoriesSet.add(categoryID as string) + }) + + return { + totalincidents, + countriesCount: countriesSet.size, + departmentsCount: departmentsSet.size, + municipalitiesCount: municipalitiesSet.size, + earliestDate, + latestDate, + categoriesCount: categoriesSet.size, + typesCount: typesSet.size, + } + }, [incidents, bounds]) + + return ( +
+

Estadísticas de incidentes

+

+ {bounds.totalCount} incidentes reportados +

+
+
+ + Países: {Object.keys(bounds.locations).length} + + + Departamentos: {Object.values(bounds.locations).reduce((acc, departments) => acc + Object.keys(departments).length, 0)} + + + Municipios:{' '} + {Object.values(bounds.locations).reduce( + (acc, departments) => acc + Object.values(departments).reduce((acc, municipalities) => acc + municipalities.length, 0), + 0 + )} + +
+
+
+ + Desde: {stats.earliestDate.toLocaleDateString('es-ES')} + + + Hasta: {stats.latestDate.toLocaleDateString('es-ES')} + +
+
+
+ + Categorías: {stats.categoriesCount} + + + Tipos: {stats.typesCount} + +
+
+ ) +} diff --git a/src/components/graphs/PieChart.tsx b/src/components/graphs/PieChart.tsx index d8cfe43..a732d38 100644 --- a/src/components/graphs/PieChart.tsx +++ b/src/components/graphs/PieChart.tsx @@ -52,7 +52,7 @@ export default function PieChart({ incidents, data }: { incidents: [string, Inci g.append('g').attr('class', 'labels') g.append('g').attr('class', 'lines') - g.attr('transform', 'translate(' + width / 4 + ',' + height / 2 + ')') + g.attr('transform', 'translate(' + width / 4 + ',' + (10 + height / 2) + ')') const radius = Math.min(width, height) / 2 @@ -137,6 +137,7 @@ export default function PieChart({ incidents, data }: { incidents: [string, Inci return (
+

Categorías de incidentes

) diff --git a/src/pages/StatsDashboard.tsx b/src/pages/StatsDashboard.tsx index 021299e..9c0c880 100644 --- a/src/pages/StatsDashboard.tsx +++ b/src/pages/StatsDashboard.tsx @@ -4,7 +4,7 @@ import React, { useMemo, useReducer, useState } from 'react' import IncidentTable from '@/components/IncidentTable' import StatisticsFilterBar from '@/components/StatisticsFilterBar' import { calculateBounds } from '@/utils' -import DummyGraph from '@/components/graphs/DummyGraph' +import IncidentsStats from '@/components/graphs/IncidentsStats' import LineGraph from '@/components/graphs/LineGraph' import PieChart from '@/components/graphs/PieChart' import StatisticsFilterMap from '@/components/StatisticsFilterMap' @@ -88,7 +88,7 @@ const StatsDashboard: React.FC = ({ data }) => {
- +