mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-23 04:26:57 +00:00
Global filters (#40)
This commit is contained in:
@@ -2,9 +2,10 @@ import { useMap } from "react-leaflet";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
|
import { TimeControl } from "@/types";
|
||||||
|
|
||||||
const Legend = () => {
|
const Legend = () => {
|
||||||
const t = useTranslations("App");
|
const t = useTranslations("Tournaments");
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -20,16 +21,20 @@ const Legend = () => {
|
|||||||
);
|
);
|
||||||
div.innerHTML = `<ul>
|
div.innerHTML = `<ul>
|
||||||
<li><span style='background:#00ac39; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
<li><span style='background:#00ac39; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
||||||
"tcClassic"
|
"timeControlEnum",
|
||||||
|
{ tc: TimeControl.Classic }
|
||||||
)}</li>
|
)}</li>
|
||||||
<li><span style='background:#0086c7; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
<li><span style='background:#0086c7; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
||||||
"tcRapid"
|
"timeControlEnum",
|
||||||
|
{ tc: TimeControl.Rapid }
|
||||||
)}</li>
|
)}</li>
|
||||||
<li><span style='background:#cec348; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
<li><span style='background:#cec348; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
||||||
"tcBlitz"
|
"timeControlEnum",
|
||||||
|
{ tc: TimeControl.Blitz }
|
||||||
)}</li>
|
)}</li>
|
||||||
<li><span style='background:#d10c3e; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
<li><span style='background:#d10c3e; display: block; height: 16px; width: 30px; border: 1px solid #999; float: left; margin-right: 5px'></span>${t(
|
||||||
"tcKO"
|
"timeControlEnum",
|
||||||
|
{ tc: TimeControl.KO }
|
||||||
)}</li>
|
)}</li>
|
||||||
</ul>`;
|
</ul>`;
|
||||||
return div;
|
return div;
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { useAtom } from "jotai";
|
||||||
|
|
||||||
|
import { classicAtom, rapidAtom, blitzAtom, oneHourKOAtom } from "@/app/atoms";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { TimeControl } from "@/types";
|
||||||
|
|
||||||
|
const TimeControlFilters = () => {
|
||||||
|
const t = useTranslations("Tournaments");
|
||||||
|
const classic = useAtom(classicAtom);
|
||||||
|
const rapid = useAtom(rapidAtom);
|
||||||
|
const blitz = useAtom(blitzAtom);
|
||||||
|
const oneHourKO = useAtom(oneHourKOAtom);
|
||||||
|
|
||||||
|
const checkboxes = [
|
||||||
|
{ title: t("timeControlEnum", { tc: TimeControl.Classic }), atom: classic },
|
||||||
|
{ title: t("timeControlEnum", { tc: TimeControl.Rapid }), atom: rapid },
|
||||||
|
{ title: t("timeControlEnum", { tc: TimeControl.Blitz }), atom: blitz },
|
||||||
|
{ title: t("timeControlEnum", { tc: TimeControl.KO }), atom: oneHourKO },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{checkboxes.map((cb, i) => (
|
||||||
|
<div key={i} className="text-gray-900 dark:text-white">
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="mr-2"
|
||||||
|
checked={cb.atom[0]}
|
||||||
|
onChange={() => cb.atom[1](!cb.atom[0])}
|
||||||
|
/>
|
||||||
|
{cb.title}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TimeControlFilters;
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Tournament } from "@/types";
|
import { TimeControl, Tournament } from "@/types";
|
||||||
import { LatLngLiteral } from "leaflet";
|
import { LatLngLiteral } from "leaflet";
|
||||||
import {
|
import {
|
||||||
MapContainer,
|
MapContainer,
|
||||||
TileLayer,
|
TileLayer,
|
||||||
LayersControl,
|
|
||||||
LayerGroup,
|
LayerGroup,
|
||||||
useMapEvent,
|
useMapEvent,
|
||||||
} from "react-leaflet";
|
} from "react-leaflet";
|
||||||
@@ -15,10 +14,14 @@ import "leaflet/dist/leaflet.css";
|
|||||||
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
|
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
|
||||||
import "leaflet-defaulticon-compatibility";
|
import "leaflet-defaulticon-compatibility";
|
||||||
|
|
||||||
import { tournamentsAtom, mapBoundsAtom } from "@/app/atoms";
|
import {
|
||||||
|
filteredTournamentsByTimeControlAtom,
|
||||||
|
mapBoundsAtom,
|
||||||
|
} from "@/app/atoms";
|
||||||
|
|
||||||
import Legend from "./Legend";
|
import Legend from "./Legend";
|
||||||
import { TournamentMarker } from "./TournamentMarker";
|
import { TournamentMarker } from "./TournamentMarker";
|
||||||
|
import TimeControlFilters from "./TimeControlFilters";
|
||||||
|
|
||||||
const MapEvents = () => {
|
const MapEvents = () => {
|
||||||
const setMapBounds = useSetAtom(mapBoundsAtom);
|
const setMapBounds = useSetAtom(mapBoundsAtom);
|
||||||
@@ -30,17 +33,17 @@ const MapEvents = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function TournamentMap() {
|
export default function TournamentMap() {
|
||||||
const tournaments = useAtomValue(tournamentsAtom);
|
const tournaments = useAtomValue(filteredTournamentsByTimeControlAtom);
|
||||||
const setMapBounds = useSetAtom(mapBoundsAtom);
|
const setMapBounds = useSetAtom(mapBoundsAtom);
|
||||||
const center: LatLngLiteral = { lat: 47.0844, lng: 2.3964 };
|
const center: LatLngLiteral = { lat: 47.0844, lng: 2.3964 };
|
||||||
|
|
||||||
const createLayerGroups = (
|
const createLayerGroups = (
|
||||||
timeControl: string,
|
timeControl: TimeControl,
|
||||||
colour: string,
|
colour: string,
|
||||||
tournaments: Tournament[]
|
tournaments: Tournament[]
|
||||||
) => {
|
) => {
|
||||||
const filteredTournaments = tournaments.filter(
|
const filteredTournaments = tournaments.filter(
|
||||||
(t) => t.time_control === timeControl
|
(t) => t.timeControl === timeControl
|
||||||
);
|
);
|
||||||
|
|
||||||
const layerGroup = filteredTournaments.map((tournament) => {
|
const layerGroup = filteredTournaments.map((tournament) => {
|
||||||
@@ -53,31 +56,41 @@ export default function TournamentMap() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return <LayerGroup>{layerGroup}</LayerGroup>;
|
||||||
<LayersControl.Overlay checked name={timeControl}>
|
|
||||||
<LayerGroup>{layerGroup}</LayerGroup>
|
|
||||||
</LayersControl.Overlay>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const classicalMarkers = createLayerGroups(
|
const classicalMarkers = createLayerGroups(
|
||||||
"Cadence Lente",
|
TimeControl.Classic,
|
||||||
"green",
|
"green",
|
||||||
tournaments
|
tournaments
|
||||||
);
|
);
|
||||||
|
const rapidMarkers = createLayerGroups(
|
||||||
const rapidMarkers = createLayerGroups("Rapide", "blue", tournaments);
|
TimeControl.Rapid,
|
||||||
const blitzMarkers = createLayerGroups("Blitz", "yellow", tournaments);
|
"blue",
|
||||||
const otherMarkers = createLayerGroups("1h KO", "red", tournaments);
|
tournaments
|
||||||
|
);
|
||||||
|
const blitzMarkers = createLayerGroups(
|
||||||
|
TimeControl.Blitz,
|
||||||
|
"yellow",
|
||||||
|
tournaments
|
||||||
|
);
|
||||||
|
const otherMarkers = createLayerGroups(TimeControl.KO, "red", tournaments);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="tournament-map" className="grid h-[calc(100vh-144px)]">
|
<section
|
||||||
|
id="tournament-map"
|
||||||
|
className="flex h-[calc(100vh-144px)] flex-col"
|
||||||
|
>
|
||||||
|
<div className="p-3 lg:hidden">
|
||||||
|
<TimeControlFilters />
|
||||||
|
</div>
|
||||||
|
|
||||||
<MapContainer
|
<MapContainer
|
||||||
center={center}
|
center={center}
|
||||||
zoom={5}
|
zoom={5}
|
||||||
scrollWheelZoom={false}
|
scrollWheelZoom={false}
|
||||||
style={{
|
style={{
|
||||||
height: "100%",
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
ref={(map) => {
|
ref={(map) => {
|
||||||
if (map) {
|
if (map) {
|
||||||
@@ -91,12 +104,11 @@ export default function TournamentMap() {
|
|||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
/>
|
/>
|
||||||
<Legend />
|
<Legend />
|
||||||
<LayersControl>
|
|
||||||
{classicalMarkers}
|
{classicalMarkers}
|
||||||
{rapidMarkers}
|
{rapidMarkers}
|
||||||
{blitzMarkers}
|
{blitzMarkers}
|
||||||
{otherMarkers}
|
{otherMarkers}
|
||||||
</LayersControl>
|
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import {
|
|||||||
debouncedHoveredMapTournamentIdAtom,
|
debouncedHoveredMapTournamentIdAtom,
|
||||||
} from "@/app/atoms";
|
} from "@/app/atoms";
|
||||||
|
|
||||||
const coordinateRandomisation = (lat: number, lng: number): LatLngLiteral => {
|
const coordinateRandomisation = (latLng: LatLngLiteral): LatLngLiteral => {
|
||||||
const randomisation = () => Math.random() * (-0.01 - 0.01) + 0.01;
|
const randomisation = () => Math.random() * (-0.01 - 0.01) + 0.01;
|
||||||
return {
|
return {
|
||||||
lat: lat + randomisation(),
|
lat: latLng.lat + randomisation(),
|
||||||
lng: lng + randomisation(),
|
lng: latLng.lng + randomisation(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,12 +33,7 @@ export const TournamentMarker = ({
|
|||||||
}: TournamentMarkerProps) => {
|
}: TournamentMarkerProps) => {
|
||||||
const t = useTranslations("Tournaments");
|
const t = useTranslations("Tournaments");
|
||||||
const markerRef = useRef<L.Marker<any> | null>(null);
|
const markerRef = useRef<L.Marker<any> | null>(null);
|
||||||
const position = useRef(
|
const position = useRef(coordinateRandomisation(tournament.latLng));
|
||||||
coordinateRandomisation(
|
|
||||||
tournament.coordinates[0],
|
|
||||||
tournament.coordinates[1]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const hoveredListTournamentId = useAtomValue(
|
const hoveredListTournamentId = useAtomValue(
|
||||||
debouncedHoveredListTournamentIdAtom
|
debouncedHoveredListTournamentIdAtom
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useAtomValue, useSetAtom, useAtom } from "jotai";
|
import { useAtomValue, useSetAtom, useAtom } from "jotai";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filteredTournamentsAtom,
|
filteredTournamentsListAtom,
|
||||||
syncVisibleAtom,
|
syncVisibleAtom,
|
||||||
hoveredMapTournamentIdAtom,
|
hoveredMapTournamentIdAtom,
|
||||||
debouncedHoveredMapTournamentIdAtom,
|
debouncedHoveredMapTournamentIdAtom,
|
||||||
@@ -13,13 +14,13 @@ import {
|
|||||||
} from "@/app/atoms";
|
} from "@/app/atoms";
|
||||||
|
|
||||||
import SearchBar from "./SearchBar";
|
import SearchBar from "./SearchBar";
|
||||||
|
import TimeControlFilters from "./TimeControlFilters";
|
||||||
import ScrollToTopButton from "./ScrollToTopButton";
|
import ScrollToTopButton from "./ScrollToTopButton";
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
export default function TournamentTable() {
|
export default function TournamentTable() {
|
||||||
const t = useTranslations("Tournaments");
|
const t = useTranslations("Tournaments");
|
||||||
|
|
||||||
const filteredTournaments = useAtomValue(filteredTournamentsAtom);
|
const filteredTournaments = useAtomValue(filteredTournamentsListAtom);
|
||||||
const [syncVisible, setSyncVisible] = useAtom(syncVisibleAtom);
|
const [syncVisible, setSyncVisible] = useAtom(syncVisibleAtom);
|
||||||
const hoveredMapTournamentId = useAtomValue(hoveredMapTournamentIdAtom);
|
const hoveredMapTournamentId = useAtomValue(hoveredMapTournamentIdAtom);
|
||||||
const debouncedHoveredMapTournamentId = useAtomValue(
|
const debouncedHoveredMapTournamentId = useAtomValue(
|
||||||
@@ -50,12 +51,17 @@ export default function TournamentTable() {
|
|||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
className="mr-2"
|
||||||
checked={syncVisible}
|
checked={syncVisible}
|
||||||
onChange={() => setSyncVisible(!syncVisible)}
|
onChange={() => setSyncVisible(!syncVisible)}
|
||||||
/>{" "}
|
/>
|
||||||
{t("syncWithMapCheckbox")}
|
{t("syncWithMapCheckbox")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden lg:block">
|
||||||
|
<TimeControlFilters />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ScrollToTopButton />
|
<ScrollToTopButton />
|
||||||
@@ -117,7 +123,7 @@ export default function TournamentTable() {
|
|||||||
</td>
|
</td>
|
||||||
<td className="p-3">
|
<td className="p-3">
|
||||||
<a href={tournament.url} target="_blank">
|
<a href={tournament.url} target="_blank">
|
||||||
{tournament.time_control}
|
{tournament.timeControl}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function TournamentsDisplay({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="relative grid h-full w-full lg:grid-cols-2">
|
<main className="relative grid h-full w-full lg:grid-cols-2">
|
||||||
<div className="">
|
<div>
|
||||||
<TournamentMap />
|
<TournamentMap />
|
||||||
</div>
|
</div>
|
||||||
<div className="relative bg-white dark:bg-gray-800 lg:overflow-y-auto">
|
<div className="relative bg-white dark:bg-gray-800 lg:overflow-y-auto">
|
||||||
|
|||||||
@@ -1,19 +1,38 @@
|
|||||||
import clientPromise from "@/lib/mongodb";
|
import clientPromise from "@/lib/mongodb";
|
||||||
|
|
||||||
import { errorLog } from "@/utils/logger";
|
import { errorLog } from "@/utils/logger";
|
||||||
import { Tournament } from "@/types";
|
import { Tournament, TimeControl } from "@/types";
|
||||||
|
|
||||||
import TournamentsDisplay from "./TournamentsDisplay";
|
import TournamentsDisplay from "./TournamentsDisplay";
|
||||||
|
import { ObjectId } from "mongodb";
|
||||||
|
|
||||||
export const revalidate = 3600; // Revalidate cache every 6 hours
|
export const revalidate = 3600; // Revalidate cache every 6 hours
|
||||||
|
|
||||||
|
export interface TournamentData {
|
||||||
|
_id: ObjectId;
|
||||||
|
town: string;
|
||||||
|
department: string;
|
||||||
|
tournament: string;
|
||||||
|
url: string;
|
||||||
|
time_control: "Cadence Lente" | "Rapide" | "Blitz" | "1h KO";
|
||||||
|
date: string;
|
||||||
|
coordinates: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tcMap: Record<TournamentData["time_control"], TimeControl> = {
|
||||||
|
"Cadence Lente": TimeControl.Classic,
|
||||||
|
Rapide: TimeControl.Rapid,
|
||||||
|
Blitz: TimeControl.Blitz,
|
||||||
|
"1h KO": TimeControl.KO,
|
||||||
|
};
|
||||||
|
|
||||||
const getTournaments = async () => {
|
const getTournaments = async () => {
|
||||||
try {
|
try {
|
||||||
const client = await clientPromise;
|
const client = await clientPromise;
|
||||||
const db = client.db("tournamentsFranceDB");
|
const db = client.db("tournamentsFranceDB");
|
||||||
const data = await db
|
const data = await db
|
||||||
.collection("tournaments")
|
.collection("tournaments")
|
||||||
.aggregate<Tournament>([
|
.aggregate<TournamentData>([
|
||||||
{
|
{
|
||||||
$addFields: {
|
$addFields: {
|
||||||
dateParts: {
|
dateParts: {
|
||||||
@@ -35,10 +54,11 @@ const getTournaments = async () => {
|
|||||||
])
|
])
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
// We map the ObjectId to a string so that it can be serialized and sent to a client component
|
return data.map<Tournament>((t) => ({
|
||||||
return data.map((t) => ({
|
|
||||||
...t,
|
...t,
|
||||||
_id: t._id.toString(),
|
_id: t._id.toString(),
|
||||||
|
timeControl: tcMap[t.time_control],
|
||||||
|
latLng: { lat: t.coordinates[0], lng: t.coordinates[1] },
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorLog(error);
|
errorLog(error);
|
||||||
|
|||||||
+27
-5
@@ -1,14 +1,19 @@
|
|||||||
import { Tournament } from '@/types';
|
import { TimeControl, Tournament } from '@/types';
|
||||||
import { atom } from 'jotai';
|
import { atom } from 'jotai';
|
||||||
import { LatLngBounds } from 'leaflet';
|
import { LatLngBounds } from 'leaflet';
|
||||||
|
|
||||||
import atomWithDebounce from "@/utils/atomWithDebounce";
|
import atomWithDebounce from "@/utils/atomWithDebounce";
|
||||||
|
|
||||||
export const tournamentsAtom = atom<Tournament[]>([]);
|
export const tournamentsAtom = atom<Tournament[]>([]);
|
||||||
export const searchStringAtom = atom('');
|
|
||||||
export const mapBoundsAtom = atom<LatLngBounds | null>(null);
|
export const mapBoundsAtom = atom<LatLngBounds | null>(null);
|
||||||
export const syncVisibleAtom = atom(true);
|
export const syncVisibleAtom = atom(true);
|
||||||
|
|
||||||
|
export const searchStringAtom = atom('');
|
||||||
|
export const classicAtom = atom(true);
|
||||||
|
export const rapidAtom = atom(true);
|
||||||
|
export const blitzAtom = atom(true);
|
||||||
|
export const oneHourKOAtom = atom(true);
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
currentValueAtom: hoveredMapTournamentIdAtom,
|
currentValueAtom: hoveredMapTournamentIdAtom,
|
||||||
debouncedValueAtom: debouncedHoveredMapTournamentIdAtom,
|
debouncedValueAtom: debouncedHoveredMapTournamentIdAtom,
|
||||||
@@ -18,12 +23,29 @@ export const {
|
|||||||
debouncedValueAtom: debouncedHoveredListTournamentIdAtom,
|
debouncedValueAtom: debouncedHoveredListTournamentIdAtom,
|
||||||
} = atomWithDebounce<string | null>(null);
|
} = atomWithDebounce<string | null>(null);
|
||||||
|
|
||||||
export const filteredTournamentsAtom = atom((get) => {
|
export const filteredTournamentsByTimeControlAtom = atom((get) => {
|
||||||
const tournaments = get(tournamentsAtom);
|
const tournaments = get(tournamentsAtom);
|
||||||
const searchString = get(searchStringAtom).trim();
|
|
||||||
|
const classic = get(classicAtom);
|
||||||
|
const rapid = get(rapidAtom);
|
||||||
|
const blitz = get(blitzAtom);
|
||||||
|
const oneHourKO = get(oneHourKOAtom);
|
||||||
|
|
||||||
|
return tournaments.filter(tournament =>
|
||||||
|
(tournament.timeControl === TimeControl.Classic && classic) ||
|
||||||
|
(tournament.timeControl === TimeControl.Rapid && rapid) ||
|
||||||
|
(tournament.timeControl === TimeControl.Blitz && blitz) ||
|
||||||
|
(tournament.timeControl === TimeControl.KO && oneHourKO));
|
||||||
|
});
|
||||||
|
|
||||||
|
export const filteredTournamentsListAtom = atom((get) => {
|
||||||
|
const tournaments = get(filteredTournamentsByTimeControlAtom);
|
||||||
const mapBounds = get(mapBoundsAtom);
|
const mapBounds = get(mapBoundsAtom);
|
||||||
const syncVisible = get(syncVisibleAtom);
|
const syncVisible = get(syncVisibleAtom);
|
||||||
|
|
||||||
|
const searchString = get(searchStringAtom).trim();
|
||||||
|
|
||||||
|
|
||||||
// When searching, we search all the tournament, regardless of the map display
|
// When searching, we search all the tournament, regardless of the map display
|
||||||
if (searchString !== '')
|
if (searchString !== '')
|
||||||
return tournaments.filter((t) => t.town.includes(searchString.toUpperCase()))
|
return tournaments.filter((t) => t.town.includes(searchString.toUpperCase()))
|
||||||
@@ -32,5 +54,5 @@ export const filteredTournamentsAtom = atom((get) => {
|
|||||||
if (mapBounds === null || !syncVisible) return tournaments;
|
if (mapBounds === null || !syncVisible) return tournaments;
|
||||||
|
|
||||||
// Filter by those in the current map bounds
|
// Filter by those in the current map bounds
|
||||||
return tournaments.filter(tournament => mapBounds.contains({ lat: tournament.coordinates[0], lng: tournament.coordinates[1]}))
|
return tournaments.filter(tournament => mapBounds.contains(tournament.latLng))
|
||||||
})
|
})
|
||||||
|
|||||||
+2
-8
@@ -17,13 +17,6 @@
|
|||||||
"contactAria": "Contact Us"
|
"contactAria": "Contact Us"
|
||||||
},
|
},
|
||||||
|
|
||||||
"App": {
|
|
||||||
"tcClassic": "Classic",
|
|
||||||
"tcRapid": "Rapid",
|
|
||||||
"tcBlitz": "Blitz",
|
|
||||||
"tcKO": "1h KO"
|
|
||||||
},
|
|
||||||
|
|
||||||
"Home": {
|
"Home": {
|
||||||
"title": "France Echecs",
|
"title": "France Echecs",
|
||||||
"purpose": "Find chess tournaments, in France, on a map",
|
"purpose": "Find chess tournaments, in France, on a map",
|
||||||
@@ -41,7 +34,8 @@
|
|||||||
"town": "Town",
|
"town": "Town",
|
||||||
"tournament": "Tournament",
|
"tournament": "Tournament",
|
||||||
"timeControl": "Time Control",
|
"timeControl": "Time Control",
|
||||||
"approx": "Géo-localisation is approximative"
|
"approx": "Géo-localisation is approximative",
|
||||||
|
"timeControlEnum": "{tc, select, Classic {Classic} Rapid {Rapid} Blitz {Blitz} KO {1h KO} other {{tc}}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"About": {
|
"About": {
|
||||||
|
|||||||
+2
-8
@@ -17,13 +17,6 @@
|
|||||||
"contactAria": "Contactez-nous"
|
"contactAria": "Contactez-nous"
|
||||||
},
|
},
|
||||||
|
|
||||||
"App": {
|
|
||||||
"tcClassic": "Cadence Lente",
|
|
||||||
"tcRapid": "Rapide",
|
|
||||||
"tcBlitz": "Blitz",
|
|
||||||
"tcKO": "1h KO"
|
|
||||||
},
|
|
||||||
|
|
||||||
"Home": {
|
"Home": {
|
||||||
"title": "Échecs France",
|
"title": "Échecs France",
|
||||||
"purpose": "Trouvez Vos Tournois d'Échecs en France Sur Une Carte",
|
"purpose": "Trouvez Vos Tournois d'Échecs en France Sur Une Carte",
|
||||||
@@ -41,7 +34,8 @@
|
|||||||
"town": "Ville",
|
"town": "Ville",
|
||||||
"tournament": "Tournois",
|
"tournament": "Tournois",
|
||||||
"timeControl": "Cadence",
|
"timeControl": "Cadence",
|
||||||
"approx": "Géolocalisation approximative"
|
"approx": "Géolocalisation approximative",
|
||||||
|
"timeControlEnum": "{tc, select, Classic {Cadence Lente} Rapid {Rapide} Blitz {Blitz} KO {1h KO} other {{tc}}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"About": {
|
"About": {
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
|
import { LatLngLiteral } from "leaflet";
|
||||||
|
|
||||||
|
export enum TimeControl {
|
||||||
|
Classic = "Classic",
|
||||||
|
Rapid = "Rapid",
|
||||||
|
Blitz = "Blitz",
|
||||||
|
KO = "KO",
|
||||||
|
};
|
||||||
|
|
||||||
export interface Tournament {
|
export interface Tournament {
|
||||||
_id: string;
|
_id: string;
|
||||||
town: string;
|
town: string;
|
||||||
department: string;
|
department: string;
|
||||||
tournament: string;
|
tournament: string;
|
||||||
url: string;
|
url: string;
|
||||||
time_control: string;
|
timeControl: TimeControl;
|
||||||
date: string;
|
date: string;
|
||||||
coordinates: [number, number];
|
latLng: LatLngLiteral;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ScrollableElement = Window | HTMLElement;
|
export type ScrollableElement = Window | HTMLElement;
|
||||||
|
|||||||
Reference in New Issue
Block a user