diff --git a/TODO b/TODO index 656d0b0..354bb00 100644 --- a/TODO +++ b/TODO @@ -1,3 +1 @@ marker legend - -Warning: validateDOMNesting(...): cannot appear as a child of . diff --git a/app/api/tournaments/route.ts b/app/api/tournaments/france/route.ts similarity index 81% rename from app/api/tournaments/route.ts rename to app/api/tournaments/france/route.ts index 0090407..f951478 100644 --- a/app/api/tournaments/route.ts +++ b/app/api/tournaments/france/route.ts @@ -1,8 +1,10 @@ import clientPromise from "@/lib/mongodb"; +// TODO collate only the country of France - redundant for now but will be needed when new countries are added +// probably do this by passing the country name as parameter /** * Tournament data API endpoint - * @route /api/tournaments + * @route /api/tournaments/france * @internal */ export async function GET() { diff --git a/app/tournois/page.tsx b/app/tournois/page.tsx index 97afa31..e325e7a 100644 --- a/app/tournois/page.tsx +++ b/app/tournois/page.tsx @@ -1,8 +1,11 @@ +import { Tournament } from "@/types"; + import dynamic from "next/dynamic"; import Layout from "@/components/Layout"; import TournamentTable from "@/components/TournamentTable"; -import { Tournament } from "@/types"; +import getTournaments from "@/utils/getTournamentData"; +// TODO can these functions be put into a custom hook? /** * Imports the tournament map component, ensuring CSR only. * @remarks SSR is not supported by react-leaflet @@ -16,23 +19,12 @@ const TournamentMap = dynamic(() => import("@/components/TournamentMap"), { ), }); -/** - * Retrieves tournament data from /api/tournaments - * @remarks The result is cached for the revalidation period in seconds - */ -async function getTournaments() { - const res = await fetch("http://localhost:3000/api/tournaments", { - next: { revalidate: 300 }, - }); - return await res.json(); -} - export default async function Tournaments() { - const tournamentData: Tournament[] = await getTournaments(); + const tournamentData: Tournament[] = await getTournaments("france"); return ( -
+
diff --git a/components/TournamentMap.tsx b/components/TournamentMap.tsx index 3ad37a7..15d6fdc 100644 --- a/components/TournamentMap.tsx +++ b/components/TournamentMap.tsx @@ -1,76 +1,56 @@ "use client"; +// Types +import { TournamentDataProps } from "@/types"; +import { LatLngLiteral } from "leaflet"; + +// Leaflet + icon fixes import "leaflet/dist/leaflet.css"; import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css"; import L from "leaflet"; import "leaflet-defaulticon-compatibility"; +import { MapContainer, TileLayer, LayersControl, useMap } from "react-leaflet"; -import { - MapContainer, - TileLayer, - Marker, - Popup, - LayersControl, - LayerGroup, -} from "react-leaflet"; - -import { LatLngLiteral } from "leaflet"; -import { TournamentDataProps } from "@/types"; +import { createLayerGroups } from "@/utils/layerGroups"; +import { useEffect } from "react"; export default function TournamentMap({ tournamentData }: TournamentDataProps) { const center: LatLngLiteral = { lat: 47.0844, lng: 2.3964 }; - // TODO consider putting in page.tsx so that it is SSR - // TODO move to own hook/util - // TODO wrap in useEffect on initial load [] - function layerGroups(timeControl: string, colour: string) { - const filteredTournaments = tournamentData.filter( - (t) => t.time_control === timeControl - ); + const classicalMarkers = createLayerGroups("Cadence Lente", "green", { + tournamentData, + }); + const rapidMarkers = createLayerGroups("Rapide", "blue", { tournamentData }); + const blitzMarkers = createLayerGroups("Blitz", "yellow", { tournamentData }); + const otherMarkers = createLayerGroups("1h KO", "red", { tournamentData }); - const iconOptions = new L.Icon({ - iconUrl: `images/leaflet/marker-icon-2x-${colour}.png`, - shadowUrl: "images/leaflet/marker-shadow.png", - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }); + // TODO move into its own hook + function Legend() { + const map = useMap(); - return ( - - - {filteredTournaments.map((t) => { - const coordinates = { - lat: t.coordinates[0] + Math.random() * (-0.01 - 0.01) + 0.01, - lng: t.coordinates[1] + Math.random() * (-0.01 - 0.01) + 0.01, - }; + useEffect(() => { + if (map) { + const legend = L.control({ position: "bottomleft" }); - return ( - - -

- {t.date} -
-
- {t.tournament} - -

- géolocalisation approximative - - - ); - })} - - - ); + legend.onAdd = () => { + const div = L.DomUtil.create("div", "map-legend"); + div.style = + "background: white; color: black; border: 2px solid grey; border-radius: 6px; padding: 10px;"; + div.innerHTML = ``; + return div; + }; + + legend.addTo(map); + } + }, [map]); + return null; } - const classicalMarkers = layerGroups("Cadence Lente", "green"); - const rapidMarkers = layerGroups("Rapide", "blue"); - const blitzMarkers = layerGroups("Blitz", "yellow"); - const otherMarkers = layerGroups("1h KO", "red"); - return (
+ {classicalMarkers} {rapidMarkers} diff --git a/utils/getTournamentData.tsx b/utils/getTournamentData.tsx new file mode 100644 index 0000000..7721817 --- /dev/null +++ b/utils/getTournamentData.tsx @@ -0,0 +1,10 @@ +/** + * Retrieves tournament data from /api/tournaments/:country + * @remarks The result is cached for the revalidation period in seconds + */ +export default async function getTournaments(country: string) { + const res = await fetch(`http://localhost:3000/api/tournaments/${country}`, { + next: { revalidate: 300 }, + }); + return await res.json(); +} diff --git a/utils/layerGroups.tsx b/utils/layerGroups.tsx new file mode 100644 index 0000000..2a333a6 --- /dev/null +++ b/utils/layerGroups.tsx @@ -0,0 +1,51 @@ +import { TournamentDataProps } from "@/types"; + +import L from "leaflet"; +import { LayerGroup, LayersControl, Marker, Popup } from "react-leaflet"; + +export const createLayerGroups = ( + timeControl: string, + colour: string, + { tournamentData }: TournamentDataProps +) => { + const filteredTournaments = tournamentData.filter( + (t) => t.time_control === timeControl + ); + + const iconOptions = new L.Icon({ + iconUrl: `images/leaflet/marker-icon-2x-${colour}.png`, + shadowUrl: "images/leaflet/marker-shadow.png", + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41], + }); + + return ( + + + {filteredTournaments.map((t) => { + const coordinates = { + lat: t.coordinates[0] + Math.random() * (-0.01 - 0.01) + 0.01, + lng: t.coordinates[1] + Math.random() * (-0.01 - 0.01) + 0.01, + }; + + return ( + + +

+ {t.date} +
+ + {t.tournament} + +

+ géolocalisation approximative +
+
+ ); + })} +
+
+ ); +};