mirror of
https://github.com/TheRealOwenRees/echecsfrance.git
synced 2026-07-23 04:26:57 +00:00
Feature/date picker (#238)
- Date range picker for tournaments. - Resets date when closing picker - tRPC and Jotai update
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
"use client";
|
||||
|
||||
import { useAtom, useAtomValue } from "jotai";
|
||||
import { DateRange } from "react-date-range";
|
||||
import "react-date-range/dist/styles.css";
|
||||
import "react-date-range/dist/theme/default.css";
|
||||
|
||||
import { dateRangeAtom, maxDateAtom } from "@/atoms";
|
||||
import { DatePickerDirection } from "@/types";
|
||||
|
||||
const DatePicker = ({
|
||||
datePickerDirection,
|
||||
}: {
|
||||
datePickerDirection: DatePickerDirection;
|
||||
}) => {
|
||||
const [dateRange, setDateRange] = useAtom(dateRangeAtom);
|
||||
const maxDate = useAtomValue(maxDateAtom);
|
||||
|
||||
return (
|
||||
<DateRange
|
||||
onChange={(item) => setDateRange([item.selection])}
|
||||
minDate={new Date()}
|
||||
maxDate={maxDate}
|
||||
months={2}
|
||||
moveRangeOnFirstSelection={true}
|
||||
preventSnapRefocus={true}
|
||||
ranges={dateRange}
|
||||
direction={datePickerDirection}
|
||||
showDateDisplay={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePicker;
|
||||
@@ -1,15 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { FaExternalLinkAlt } from "react-icons/fa";
|
||||
import { FaTrophy } from "react-icons/fa";
|
||||
import { BsCalendar2Date } from "react-icons/bs";
|
||||
import { FaExternalLinkAlt, FaTrophy } from "react-icons/fa";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import DatePicker from "@/app/[locale]/tournaments/DatePicker";
|
||||
import {
|
||||
datePickerIsOpenAtom,
|
||||
dateRangeAtom,
|
||||
debouncedHoveredListIdAtom,
|
||||
debouncedHoveredMapIdAtom,
|
||||
filteredTournamentsListAtom,
|
||||
@@ -20,6 +23,8 @@ import {
|
||||
import ScrollToTopButton from "@/components/ScrollToTopButton";
|
||||
import SearchBar from "@/components/SearchBar";
|
||||
import { useBreakpoint } from "@/hooks/tailwind";
|
||||
import useDatePickerWidth from "@/hooks/useDatePickerWidth";
|
||||
import { DatePickerDirection } from "@/types";
|
||||
|
||||
import TimeControlFilters from "./TimeControlFilters";
|
||||
|
||||
@@ -27,14 +32,26 @@ const TournamentTable = () => {
|
||||
const t = useTranslations("Tournaments");
|
||||
const at = useTranslations("App");
|
||||
|
||||
const isLg = useBreakpoint("lg");
|
||||
const datePickerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const filteredTournaments = useAtomValue(filteredTournamentsListAtom);
|
||||
|
||||
const [syncVisible, setSyncVisible] = useAtom(syncVisibleAtom);
|
||||
const [normsOnly, setNormsOnly] = useAtom(normsOnlyAtom);
|
||||
const hoveredMapId = useAtomValue(hoveredMapIdAtom);
|
||||
const debouncedHoveredMapId = useAtomValue(debouncedHoveredMapIdAtom);
|
||||
const setHoveredListId = useSetAtom(debouncedHoveredListIdAtom);
|
||||
|
||||
const isLg = useBreakpoint("lg");
|
||||
const setDateRange = useSetAtom(dateRangeAtom);
|
||||
const [datePickerIsOpen, setDatePickerIsOpen] = useAtom(datePickerIsOpenAtom);
|
||||
const [dateDirectionState, setDateDirectionState] =
|
||||
useState<DatePickerDirection>("horizontal");
|
||||
const datePickerColour = datePickerIsOpen
|
||||
? "text-primary-600"
|
||||
: "text-gray-500";
|
||||
|
||||
useDatePickerWidth({ datePickerRef, setDateDirectionState });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLg || debouncedHoveredMapId === null) return;
|
||||
@@ -45,14 +62,31 @@ const TournamentTable = () => {
|
||||
tournamentRow?.scrollIntoView({ behavior: "smooth" });
|
||||
}, [debouncedHoveredMapId, isLg]);
|
||||
|
||||
const handleDatePickerClick = () => {
|
||||
// reset date range today -> max date
|
||||
setDateRange([
|
||||
{
|
||||
startDate: new Date(),
|
||||
endDate: undefined,
|
||||
key: "selection",
|
||||
},
|
||||
]);
|
||||
setDatePickerIsOpen(!datePickerIsOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
className="grid w-full auto-rows-max pb-20 lg:col-start-2 lg:col-end-3 lg:h-content lg:overflow-y-scroll lg:pb-0"
|
||||
id="listing"
|
||||
>
|
||||
<div className="z-10 flex w-full flex-wrap items-center justify-between gap-3 p-3">
|
||||
<div className="z-10 flex w-full flex-wrap items-center justify-start gap-3 p-3">
|
||||
<SearchBar />
|
||||
|
||||
<BsCalendar2Date
|
||||
className={`cursor-pointer text-3xl ${datePickerColour}`}
|
||||
onClick={handleDatePickerClick}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col gap-0 text-gray-900 dark:text-white">
|
||||
<label>
|
||||
<input
|
||||
@@ -80,6 +114,12 @@ const TournamentTable = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center" ref={datePickerRef}>
|
||||
{datePickerIsOpen && (
|
||||
<DatePicker datePickerDirection={dateDirectionState} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ScrollToTopButton />
|
||||
|
||||
<div className="overflow-x-scroll">
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { differenceInDays, isSameDay, parse } from "date-fns";
|
||||
import {
|
||||
differenceInDays,
|
||||
formatISO,
|
||||
isSameDay,
|
||||
parse,
|
||||
setDefaultOptions,
|
||||
} from "date-fns";
|
||||
import { fr } from "date-fns/locale";
|
||||
import { groupBy } from "lodash";
|
||||
import { unstable_cache } from "next/cache";
|
||||
|
||||
@@ -9,6 +16,8 @@ import { errorLog } from "@/utils/logger";
|
||||
|
||||
import TournamentsDisplay from "./TournamentsDisplay";
|
||||
|
||||
setDefaultOptions({ locale: fr });
|
||||
|
||||
const getTournaments = async () => {
|
||||
try {
|
||||
const client = await clientPromise;
|
||||
@@ -129,6 +138,7 @@ const getTournaments = async () => {
|
||||
town: t.town,
|
||||
department: t.department,
|
||||
date: t.date,
|
||||
isoDate: formatISO(date),
|
||||
url: t.url,
|
||||
timeControl,
|
||||
latLng: { lat: t.coordinates[0], lng: t.coordinates[1] },
|
||||
|
||||
Reference in New Issue
Block a user