diff --git a/.prettierrc b/.prettierrc index 3e7b163..bd178d4 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,9 @@ { "trailingComma": "all", "bracketSpacing": true, - "plugins": ["prettier-plugin-tailwindcss"] + "plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"], + "importOrder": ["^react$", "^@?\\w", "^@\\\\", "", "^\\.\\.", "^\\."], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "tailwindFunctions": ["twMerge"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 7e439e4..89c27a5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,9 +23,12 @@ "spiderfied", "Spiderfy", "spiderified", + "superjson", "tailwindcss", "timothyarmes", "tournois", + "trivago", + "TRPC", "unspiderfied", "unspiderfy" ], diff --git a/README.md b/README.md index 4fc7413..0bee3dc 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,18 @@ and is deployed on [Vercel](https://vercel.com/) - [Leaflet](https://leafletjs.com/) - a Javascript library for interactive maps - [Nodemailer](https://nodemailer.com/) - a module for Node.js applications to allow easy as cake email sending +## Required VSCode plugins + +- ESLint +- Prettier - Code formatter + +## Recommended VSCode plugins + +- Tailwind CSS IntelliSense +- Tailwind Docs +- Code Spell Checker (to avoid English spelling mistakes in the code) +- Console Ninja (for easier debugging) + ## Contributions Contributions are encouraged. Please open an issue to discuss your ideas. diff --git a/app/[lang]/ajouter-un-tournoi/Map.tsx b/app/[lang]/ajouter-un-tournoi/Map.tsx index bba2e7d..b6937e0 100644 --- a/app/[lang]/ajouter-un-tournoi/Map.tsx +++ b/app/[lang]/ajouter-un-tournoi/Map.tsx @@ -1,18 +1,18 @@ "use client"; -import { MapProps } from "@/types"; -import { useMemo, useRef, ChangeEvent } from "react"; -import { useTranslations } from "next-intl"; -import { useSetAtom } from "jotai"; -import { mapBoundsAtom } from "@/app/atoms"; +import { ChangeEvent, useMemo, useRef } from "react"; +import { useSetAtom } from "jotai"; import L from "leaflet"; -import { MapContainer, TileLayer, Marker } from "react-leaflet"; -import "leaflet/dist/leaflet.css"; -import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css"; import "leaflet-defaulticon-compatibility"; +import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css"; +import "leaflet/dist/leaflet.css"; +import { useTranslations } from "next-intl"; +import { MapContainer, Marker, TileLayer } from "react-leaflet"; import MapEvents from "@/app/[lang]/components/MapEvents"; +import { mapBoundsAtom } from "@/app/atoms"; +import { MapProps } from "@/types"; const Map = ({ position, setPosition, center }: MapProps) => { const t = useTranslations("Map"); diff --git a/app/[lang]/ajouter-un-tournoi/TournamentForm.tsx b/app/[lang]/ajouter-un-tournoi/TournamentForm.tsx index 85164d5..6aa0f52 100644 --- a/app/[lang]/ajouter-un-tournoi/TournamentForm.tsx +++ b/app/[lang]/ajouter-un-tournoi/TournamentForm.tsx @@ -1,17 +1,19 @@ "use client"; -import dynamic from "next/dynamic"; import { useState } from "react"; -import { useTranslations } from "next-intl"; + import { useAtomValue } from "jotai"; -import { franceCenterAtom } from "@/app/atoms"; -import useTournamentForm from "@/hooks/useTournamentForm"; +import { useTranslations } from "next-intl"; +import dynamic from "next/dynamic"; + import InfoMessage from "@/app/[lang]/components/InfoMessage"; -import { - handleTournamentSubmit, - handleClearTournamentForm, -} from "@/handlers/formHandlers"; import LoadingMap from "@/app/[lang]/components/LoadingMap"; +import { franceCenterAtom } from "@/app/atoms"; +import { + handleClearTournamentForm, + handleTournamentSubmit, +} from "@/handlers/formHandlers"; +import useTournamentForm from "@/hooks/useTournamentForm"; const Map = dynamic(() => import("./Map"), { ssr: false, diff --git a/app/[lang]/components/Footer.tsx b/app/[lang]/components/Footer.tsx index 9c6a578..da8fdd1 100644 --- a/app/[lang]/components/Footer.tsx +++ b/app/[lang]/components/Footer.tsx @@ -1,6 +1,7 @@ +import { useTranslations } from "next-intl"; import Link from "next-intl/link"; import { FaGithub, FaRegEnvelope } from "react-icons/fa"; -import { useTranslations } from "next-intl"; + import ThemeSwitcher from "@/app/[lang]/components/ThemeSwitcher"; export default function Footer() { diff --git a/app/[lang]/components/Hamburger.tsx b/app/[lang]/components/Hamburger.tsx index b509c95..cc3b6de 100644 --- a/app/[lang]/components/Hamburger.tsx +++ b/app/[lang]/components/Hamburger.tsx @@ -1,6 +1,7 @@ "use client"; import { useRef, useState } from "react"; + import { twMerge } from "tailwind-merge"; import HamburgerMenu from "./HamburgerMenu"; diff --git a/app/[lang]/components/HamburgerMenu.tsx b/app/[lang]/components/HamburgerMenu.tsx index 4b1077f..f71e109 100644 --- a/app/[lang]/components/HamburgerMenu.tsx +++ b/app/[lang]/components/HamburgerMenu.tsx @@ -1,16 +1,17 @@ +import { Dispatch, RefObject, SetStateAction, useRef, useState } from "react"; + +import { useTranslations } from "next-intl"; +import Link from "next-intl/link"; +import { twMerge } from "tailwind-merge"; + +import useHamburgerClose from "@/hooks/useHamburgerClose"; + interface HamburgerMenuState { menuVisible: boolean; setMenuVisible: Dispatch>; hamburgerButtonRef: RefObject; } -import { Dispatch, RefObject, SetStateAction, useRef, useState } from "react"; -import { twMerge } from "tailwind-merge"; -import Link from "next-intl/link"; -import { useTranslations } from "next-intl"; - -import useHamburgerClose from "@/hooks/useHamburgerClose"; - const HamburgerMenu = ({ menuVisible, setMenuVisible, diff --git a/app/[lang]/components/InfoMessage.tsx b/app/[lang]/components/InfoMessage.tsx index e8af19e..5d44eb3 100644 --- a/app/[lang]/components/InfoMessage.tsx +++ b/app/[lang]/components/InfoMessage.tsx @@ -1,6 +1,7 @@ -import { ResponseMessage } from "@/types"; import { Dispatch, SetStateAction } from "react"; +import { ResponseMessage } from "@/types"; + const InfoMessage = ({ responseMessage, }: { diff --git a/app/[lang]/components/MapEvents.tsx b/app/[lang]/components/MapEvents.tsx index 7fa37fc..56d4328 100644 --- a/app/[lang]/components/MapEvents.tsx +++ b/app/[lang]/components/MapEvents.tsx @@ -1,9 +1,11 @@ import { useEffect } from "react"; + import { useSetAtom } from "jotai"; -import { mapBoundsAtom } from "@/app/atoms"; import L from "leaflet"; import { useMapEvent } from "react-leaflet"; +import { mapBoundsAtom } from "@/app/atoms"; + const MapEvents = () => { const setMapBounds = useSetAtom(mapBoundsAtom); diff --git a/app/[lang]/components/Navbar.tsx b/app/[lang]/components/Navbar.tsx index 4b1744e..3fd7c6f 100644 --- a/app/[lang]/components/Navbar.tsx +++ b/app/[lang]/components/Navbar.tsx @@ -1,5 +1,5 @@ -import Link from "next-intl/link"; import { useTranslations } from "next-intl"; +import Link from "next-intl/link"; import Hamburger from "./Hamburger"; diff --git a/app/[lang]/components/TestableLayout.tsx b/app/[lang]/components/TestableLayout.tsx index d75f1bf..5e91040 100644 --- a/app/[lang]/components/TestableLayout.tsx +++ b/app/[lang]/components/TestableLayout.tsx @@ -1,10 +1,11 @@ -import { NextIntlClientProvider, AbstractIntlMessages } from "next-intl"; import { ReactNode } from "react"; +import { AbstractIntlMessages, NextIntlClientProvider } from "next-intl"; + import "@/css/globals.css"; -import Navbar from "./Navbar"; import Footer from "./Footer"; +import Navbar from "./Navbar"; const TestableLayout = ({ children, diff --git a/app/[lang]/components/ThemeSwitcher.tsx b/app/[lang]/components/ThemeSwitcher.tsx index db7241e..e52b54c 100644 --- a/app/[lang]/components/ThemeSwitcher.tsx +++ b/app/[lang]/components/ThemeSwitcher.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState } from "react"; + import useDarkMode from "@/hooks/useDarkMode"; const ThemeSwitcher = () => { diff --git a/app/[lang]/contactez-nous/ContactForm.tsx b/app/[lang]/contactez-nous/ContactForm.tsx index 8505744..aa75423 100644 --- a/app/[lang]/contactez-nous/ContactForm.tsx +++ b/app/[lang]/contactez-nous/ContactForm.tsx @@ -1,12 +1,12 @@ "use client"; import { useState } from "react"; + import { useTranslations } from "next-intl"; -import useContactForm from "@/hooks/useContactForm"; -import { handleEmailSubmit } from "@/handlers/formHandlers"; - import InfoMessage from "@/app/[lang]/components/InfoMessage"; +import { handleEmailSubmit } from "@/handlers/formHandlers"; +import useContactForm from "@/hooks/useContactForm"; const ContactForm = () => { const t = useTranslations("Contact"); diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx index 1b27b2c..9700ed4 100644 --- a/app/[lang]/layout.tsx +++ b/app/[lang]/layout.tsx @@ -1,15 +1,16 @@ -import Script from "next/script"; -import { Inter, Julius_Sans_One } from "next/font/google"; -import { useLocale, NextIntlClientProvider } from "next-intl"; -import { getTranslator } from "next-intl/server"; -import { notFound } from "next/navigation"; import { ReactNode } from "react"; +import { NextIntlClientProvider, useLocale } from "next-intl"; +import { getTranslator } from "next-intl/server"; +import { Inter, Julius_Sans_One } from "next/font/google"; +import { notFound } from "next/navigation"; +import Script from "next/script"; + import "@/css/globals.css"; -import Providers from "./providers"; -import Navbar from "./components/Navbar"; import Footer from "./components/Footer"; +import Navbar from "./components/Navbar"; +import Providers from "./providers"; const inter = Inter({ subsets: ["latin"], variable: "--font-inter" }); const title = Julius_Sans_One({ diff --git a/app/[lang]/page.tsx b/app/[lang]/page.tsx index 6abe420..10ebb7c 100644 --- a/app/[lang]/page.tsx +++ b/app/[lang]/page.tsx @@ -1,6 +1,6 @@ -import Link from "next/link"; -import Image from "next/image"; import { useTranslations } from "next-intl"; +import Image from "next/image"; +import Link from "next/link"; import bgImage from "@/public/images/map-bg.jpg"; diff --git a/app/[lang]/qui-sommes-nous/page.tsx b/app/[lang]/qui-sommes-nous/page.tsx index d7fe319..c27d29d 100644 --- a/app/[lang]/qui-sommes-nous/page.tsx +++ b/app/[lang]/qui-sommes-nous/page.tsx @@ -1,5 +1,5 @@ -import Link from "next-intl/link"; import { useTranslations } from "next-intl"; +import Link from "next-intl/link"; export default function About() { const t = useTranslations("About"); diff --git a/app/[lang]/tournois/Legend.tsx b/app/[lang]/tournois/Legend.tsx index 99811f3..a8b6d56 100644 --- a/app/[lang]/tournois/Legend.tsx +++ b/app/[lang]/tournois/Legend.tsx @@ -1,12 +1,13 @@ -import { useMap } from "react-leaflet"; import { useEffect, useMemo } from "react"; -import { useTranslations } from "next-intl"; -import L from "leaflet"; -import { useAtomValue } from "jotai"; -import { TimeControl } from "@/types"; +import { useAtomValue } from "jotai"; +import L from "leaflet"; +import { useTranslations } from "next-intl"; +import { useMap } from "react-leaflet"; + import { filteredTournamentsByTimeControlAtom } from "@/app/atoms"; import { TimeControlColours } from "@/app/constants"; +import { TimeControl } from "@/types"; const Legend = () => { const t = useTranslations("Tournaments"); diff --git a/app/[lang]/tournois/ScrollToTopButton.tsx b/app/[lang]/tournois/ScrollToTopButton.tsx index 7240b61..1c81262 100644 --- a/app/[lang]/tournois/ScrollToTopButton.tsx +++ b/app/[lang]/tournois/ScrollToTopButton.tsx @@ -1,11 +1,12 @@ "use client"; -import { ScrollableElement } from "@/types"; +import { useEffect, useRef, useState } from "react"; import { FaArrowUp } from "react-icons/fa"; + import { handleScrollToTop } from "@/handlers/scrollHandlers"; -import { useEffect, useRef, useState } from "react"; import { useBreakpoint } from "@/hooks/tailwind"; +import { ScrollableElement } from "@/types"; const ScrollToTopButton = () => { const scrollToTopElementRef = useRef(null); diff --git a/app/[lang]/tournois/SearchBar.tsx b/app/[lang]/tournois/SearchBar.tsx index c431915..74b5eb7 100644 --- a/app/[lang]/tournois/SearchBar.tsx +++ b/app/[lang]/tournois/SearchBar.tsx @@ -1,6 +1,7 @@ -import { useTranslations } from "next-intl"; import { useAtom } from "jotai/index"; +import { useTranslations } from "next-intl"; import { IoCloseOutline } from "react-icons/io5"; + import { searchStringAtom } from "@/app/atoms"; const SearchBar = () => { diff --git a/app/[lang]/tournois/TimeControlFilters.tsx b/app/[lang]/tournois/TimeControlFilters.tsx index 6fde101..a2704bb 100644 --- a/app/[lang]/tournois/TimeControlFilters.tsx +++ b/app/[lang]/tournois/TimeControlFilters.tsx @@ -1,13 +1,13 @@ import { useAtom, useAtomValue } from "jotai"; +import { useTranslations } from "next-intl"; import { - classicAtom, - rapidAtom, blitzAtom, + classicAtom, otherAtom, + rapidAtom, tournamentsAtom, } from "@/app/atoms"; -import { useTranslations } from "next-intl"; import { TimeControl } from "@/types"; const TimeControlFilters = () => { diff --git a/app/[lang]/tournois/TournamentMap.tsx b/app/[lang]/tournois/TournamentMap.tsx index aa10b2b..8213e1a 100644 --- a/app/[lang]/tournois/TournamentMap.tsx +++ b/app/[lang]/tournois/TournamentMap.tsx @@ -1,34 +1,32 @@ "use client"; -import { TimeControl } from "@/types"; +import { useCallback, useEffect, useMemo, useRef } from "react"; -import { useMemo, useRef, useCallback, useEffect } from "react"; -import L, { LatLngLiteral, Marker, DomUtil } from "leaflet"; -import { MapContainer, TileLayer, LayerGroup } from "react-leaflet"; -import MarkerClusterGroup from "react-leaflet-cluster"; -import { FaAngleDoubleDown } from "react-icons/fa"; import { useAtomValue, useSetAtom } from "jotai"; +import L, { DomUtil, LatLngLiteral, Marker } from "leaflet"; +import "leaflet-defaulticon-compatibility"; +import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css"; +import "leaflet.smooth_marker_bouncing"; +import "leaflet/dist/leaflet.css"; import { countBy, groupBy } from "lodash"; - -import { - mapBoundsAtom, - debouncedHoveredListTournamentIdAtom, - filteredTournamentsByTimeControlAtom, - normsOnlyAtom, -} from "@/app/atoms"; -import { generatePieSVG } from "@/lib/pie"; -import { TimeControlColours } from "@/app/constants"; - -import Legend from "./Legend"; -import { TournamentMarker, TournamentMarkerRef } from "./TournamentMarker"; -import TimeControlFilters from "./TimeControlFilters"; +import { FaAngleDoubleDown } from "react-icons/fa"; +import { LayerGroup, MapContainer, TileLayer } from "react-leaflet"; +import MarkerClusterGroup from "react-leaflet-cluster"; import MapEvents from "@/app/[lang]/components/MapEvents"; +import { + debouncedHoveredListTournamentIdAtom, + filteredTournamentsByTimeControlAtom, + mapBoundsAtom, + normsOnlyAtom, +} from "@/app/atoms"; +import { TimeControlColours } from "@/app/constants"; +import { generatePieSVG } from "@/lib/pie"; +import { TimeControl } from "@/types"; -import "leaflet/dist/leaflet.css"; -import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css"; -import "leaflet-defaulticon-compatibility"; -import "leaflet.smooth_marker_bouncing"; +import Legend from "./Legend"; +import TimeControlFilters from "./TimeControlFilters"; +import { TournamentMarker, TournamentMarkerRef } from "./TournamentMarker"; // Declare a class type that adds in methods etc. defined by leaflet.smooth_marker_bouncing // to keep Typescript happy diff --git a/app/[lang]/tournois/TournamentMarker.tsx b/app/[lang]/tournois/TournamentMarker.tsx index 01c18b2..a70fb5b 100644 --- a/app/[lang]/tournois/TournamentMarker.tsx +++ b/app/[lang]/tournois/TournamentMarker.tsx @@ -1,17 +1,18 @@ "use client"; -import { forwardRef, useMemo, useImperativeHandle, useRef } from "react"; -import { Tournament } from "@/types"; -import L from "leaflet"; -import { Marker, Popup, MarkerProps } from "react-leaflet"; -import { useTranslations } from "next-intl"; -import { useSetAtom } from "jotai"; -import { FaTrophy } from "react-icons/fa"; -import { last } from "lodash"; +import { forwardRef, useImperativeHandle, useMemo, useRef } from "react"; + +import { useSetAtom } from "jotai"; +import L from "leaflet"; +import { last } from "lodash"; +import { useTranslations } from "next-intl"; +import { FaTrophy } from "react-icons/fa"; +import { Marker, MarkerProps, Popup } from "react-leaflet"; -import type { BouncingMarker } from "@/leafletTypes"; import { debouncedHoveredMapTournamentGroupIdAtom } from "@/app/atoms"; import { TimeControlColours } from "@/app/constants"; +import type { BouncingMarker } from "@/leafletTypes"; +import { Tournament } from "@/types"; export type TournamentMarkerRef = { getMarker: () => L.Marker; diff --git a/app/[lang]/tournois/TournamentTable.tsx b/app/[lang]/tournois/TournamentTable.tsx index d1fcc2c..164ead0 100644 --- a/app/[lang]/tournois/TournamentTable.tsx +++ b/app/[lang]/tournois/TournamentTable.tsx @@ -1,26 +1,27 @@ "use client"; import { useEffect } from "react"; + +import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { useTranslations } from "next-intl"; -import { useAtomValue, useSetAtom, useAtom } from "jotai"; -import { twMerge } from "tailwind-merge"; import { FaExternalLinkAlt } from "react-icons/fa"; import { FaTrophy } from "react-icons/fa"; import { Tooltip } from "react-tooltip"; +import { twMerge } from "tailwind-merge"; import { - filteredTournamentsListAtom, - syncVisibleAtom, - normsOnlyAtom, - hoveredMapTournamentGroupIdAtom, - debouncedHoveredMapTournamentGroupIdAtom, debouncedHoveredListTournamentIdAtom, + debouncedHoveredMapTournamentGroupIdAtom, + filteredTournamentsListAtom, + hoveredMapTournamentGroupIdAtom, + normsOnlyAtom, + syncVisibleAtom, } from "@/app/atoms"; import { useBreakpoint } from "@/hooks/tailwind"; +import ScrollToTopButton from "./ScrollToTopButton"; import SearchBar from "./SearchBar"; import TimeControlFilters from "./TimeControlFilters"; -import ScrollToTopButton from "./ScrollToTopButton"; export default function TournamentTable() { const t = useTranslations("Tournaments"); diff --git a/app/[lang]/tournois/TournamentsDisplay.tsx b/app/[lang]/tournois/TournamentsDisplay.tsx index 4165a9e..9552797 100644 --- a/app/[lang]/tournois/TournamentsDisplay.tsx +++ b/app/[lang]/tournois/TournamentsDisplay.tsx @@ -1,18 +1,18 @@ "use client"; +import { useHydrateAtoms } from "jotai/utils"; +import dynamic from "next/dynamic"; + +import LoadingMap from "@/app/[lang]/components/LoadingMap"; +import { tournamentsAtom } from "@/app/atoms"; +import { Tournament } from "@/types"; + +import TournamentTable from "./TournamentTable"; + type TournamentsDisplayProps = { tournaments: Tournament[]; }; -import { Tournament } from "@/types"; - -import dynamic from "next/dynamic"; -import { useHydrateAtoms } from "jotai/utils"; -import { tournamentsAtom } from "@/app/atoms"; - -import TournamentTable from "./TournamentTable"; -import LoadingMap from "@/app/[lang]/components/LoadingMap"; - /** * Imports the tournament map component, ensuring CSR only. * @remarks SSR is not supported by react-leaflet diff --git a/app/[lang]/tournois/page.tsx b/app/[lang]/tournois/page.tsx index 70b812a..1fccea9 100644 --- a/app/[lang]/tournois/page.tsx +++ b/app/[lang]/tournois/page.tsx @@ -1,12 +1,12 @@ -import clientPromise from "@/lib/mongodb"; - -import { errorLog } from "@/utils/logger"; -import { Tournament, TimeControl } from "@/types"; +import { differenceInDays, isSameDay, parse } from "date-fns"; import { groupBy } from "lodash"; -import { parse, differenceInDays, isSameDay } from "date-fns"; +import { ObjectId } from "mongodb"; + +import clientPromise from "@/lib/mongodb"; +import { TimeControl, Tournament } from "@/types"; +import { errorLog } from "@/utils/logger"; import TournamentsDisplay from "./TournamentsDisplay"; -import { ObjectId } from "mongodb"; export const revalidate = 3600; // Revalidate cache every 6 hours diff --git a/app/api/add-tournament/route.ts b/app/api/add-tournament/route.ts index 3a0a295..727a2e4 100644 --- a/app/api/add-tournament/route.ts +++ b/app/api/add-tournament/route.ts @@ -1,5 +1,6 @@ -import clientPromise from "@/lib/mongodb"; import { NextResponse } from "next/server"; + +import clientPromise from "@/lib/mongodb"; import { errorLog } from "@/utils/logger"; export async function POST(req: Request) { diff --git a/app/api/send-mail/route.ts b/app/api/send-mail/route.ts index fa292cc..ca8822b 100644 --- a/app/api/send-mail/route.ts +++ b/app/api/send-mail/route.ts @@ -1,6 +1,7 @@ -import NodeMailer from "nodemailer"; import { NextResponse } from "next/server"; -import { infoLog, errorLog } from "@/utils/logger"; +import NodeMailer from "nodemailer"; + +import { errorLog, infoLog } from "@/utils/logger"; export async function POST(req: Request) { const { email, subject, message } = await req.json(); diff --git a/app/atoms.ts b/app/atoms.ts index 1ad5676..6161491 100644 --- a/app/atoms.ts +++ b/app/atoms.ts @@ -1,7 +1,7 @@ -import { TimeControl, Tournament } from "@/types"; import { atom } from "jotai"; import { LatLngBounds, LatLngLiteral } from "leaflet"; +import { TimeControl, Tournament } from "@/types"; import atomWithDebounce from "@/utils/atomWithDebounce"; import { normalizedContains } from "@/utils/string"; diff --git a/app/constants.ts b/app/constants.ts index 4e614c6..64e8189 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -1,8 +1,8 @@ -import { TimeControl } from "@/types" +import { TimeControl } from "@/types"; export const TimeControlColours = { [TimeControl.Classic]: "#00ac39", [TimeControl.Rapid]: "#0086c7", [TimeControl.Blitz]: "#ddce20", [TimeControl.Other]: "#ea5f17", -} +}; diff --git a/handlers/formHandlers.ts b/handlers/formHandlers.ts index 15059f6..155385a 100644 --- a/handlers/formHandlers.ts +++ b/handlers/formHandlers.ts @@ -1,12 +1,14 @@ -import { ResponseMessage, TournamentFormProps } from "@/types"; -import { LatLngLiteral } from "leaflet"; import { Dispatch, FormEvent, SetStateAction } from "react"; + +import { LatLngLiteral } from "leaflet"; import { useTranslations } from "next-intl"; + import { clearMessage } from "@/app/[lang]/components/InfoMessage"; -import { errorLog } from "@/utils/logger"; +import discordWebhook from "@/lib/discordWebhook"; import sendMail from "@/lib/sendMail"; import tournamentFormToDB from "@/lib/tournamentFormToDB"; -import discordWebhook from "@/lib/discordWebhook"; +import { ResponseMessage, TournamentFormProps } from "@/types"; +import { errorLog } from "@/utils/logger"; export const handleClearTournamentForm = ( formRefs: TournamentFormProps, diff --git a/hooks/tailwind.ts b/hooks/tailwind.ts index caf73d5..c96ea79 100644 --- a/hooks/tailwind.ts +++ b/hooks/tailwind.ts @@ -1,11 +1,15 @@ -import { useLayoutEffect, useEffect, useState, useRef, useMemo } from "react"; +import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; + import resolveConfig from "tailwindcss/resolveConfig"; import tailwindConfig from "@/tailwind.config.js"; + const config = resolveConfig(tailwindConfig); const isSSR = - typeof window === "undefined" || !window.navigator || /ServerSideRendering|^Deno\//.test(window.navigator.userAgent); + typeof window === "undefined" || + !window.navigator || + /ServerSideRendering|^Deno\//.test(window.navigator.userAgent); const isBrowser = !isSSR; const useIsomorphicEffect = isBrowser ? useLayoutEffect : useEffect; @@ -18,7 +22,9 @@ export type CreatorReturnType = { function create(screens: object | undefined) { if (!screens) { - throw new Error("Failed to create breakpoint hooks, given `screens` value is invalid."); + throw new Error( + "Failed to create breakpoint hooks, given `screens` value is invalid.", + ); } function useBreakpoint(breakpoint: string, defaultValue: boolean = false) { @@ -46,15 +52,25 @@ function create(screens: object | undefined) { return match; } - function useBreakpointEffect(breakpoint: Breakpoint, effect: (match: boolean) => void) { + function useBreakpointEffect( + breakpoint: Breakpoint, + effect: (match: boolean) => void, + ) { const match = useBreakpoint(breakpoint); useEffect(() => effect(match)); return null; } - function useBreakpointValue(breakpoint: Breakpoint, valid: T, invalid: U) { + function useBreakpointValue( + breakpoint: Breakpoint, + valid: T, + invalid: U, + ) { const match = useBreakpoint(breakpoint); - const value = useMemo(() => (match ? valid : invalid), [invalid, match, valid]); + const value = useMemo( + () => (match ? valid : invalid), + [invalid, match, valid], + ); return value; } @@ -65,4 +81,5 @@ function create(screens: object | undefined) { } as CreatorReturnType; } -export const { useBreakpoint, useBreakpointEffect, useBreakpointValue } = create(config.theme!.screens); +export const { useBreakpoint, useBreakpointEffect, useBreakpointValue } = + create(config.theme!.screens); diff --git a/hooks/useDynamicViewportUnits.ts b/hooks/useDynamicViewportUnits.ts index 5f9928f..e536d47 100644 --- a/hooks/useDynamicViewportUnits.ts +++ b/hooks/useDynamicViewportUnits.ts @@ -1,31 +1,36 @@ export const useDynamicViewportUnits = () => { const setVh = function () { var svh = document.documentElement.clientHeight * 0.01; - document.documentElement.style.setProperty('--1svh', (svh + "px")); + document.documentElement.style.setProperty("--1svh", svh + "px"); var dvh = window.innerHeight * 0.01; - document.documentElement.style.setProperty('--1dvh', (dvh + "px")); + document.documentElement.style.setProperty("--1dvh", dvh + "px"); }; - const isMobile = function() { - if(/Android|iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2)) { - return true; + const isMobile = function () { + if ( + /Android|iPhone|iPad|iPod/i.test(navigator.userAgent) || + (navigator.userAgent.match(/Mac/) && + navigator.maxTouchPoints && + navigator.maxTouchPoints > 2) + ) { + return true; } return false; - } - + }; // SSR support - if (typeof window === 'undefined') { + if (typeof window === "undefined") { return; } // Don't run polyfill if browser supports the units natively - if ('CSS' in window && - 'supports' in window.CSS && - window.CSS.supports('height: 100svh') && - window.CSS.supports('height: 100dvh') && - window.CSS.supports('height: 100lvh') + if ( + "CSS" in window && + "supports" in window.CSS && + window.CSS.supports("height: 100svh") && + window.CSS.supports("height: 100dvh") && + window.CSS.supports("height: 100lvh") ) { return; } @@ -35,10 +40,10 @@ export const useDynamicViewportUnits = () => { return; } - window.addEventListener('resize', setVh); + window.addEventListener("resize", setVh); setVh(); return () => { - window.removeEventListener('resize', setVh); - } -} + window.removeEventListener("resize", setVh); + }; +}; diff --git a/hooks/useHamburgerClose.ts b/hooks/useHamburgerClose.ts index 73277cc..4d0f8e4 100644 --- a/hooks/useHamburgerClose.ts +++ b/hooks/useHamburgerClose.ts @@ -1,3 +1,5 @@ +import { Dispatch, RefObject, SetStateAction, useEffect } from "react"; + interface HamburgerClose { menuVisible: boolean; setMenuVisible: Dispatch>; @@ -9,8 +11,6 @@ interface HamburgerClose { menuTimeout: () => void; } -import { Dispatch, RefObject, SetStateAction, useEffect } from "react"; - const useHamburgerClose = ({ menuVisible, setMenuVisible, diff --git a/hooks/useTournamentForm.ts b/hooks/useTournamentForm.ts index 2ce08a9..53448eb 100644 --- a/hooks/useTournamentForm.ts +++ b/hooks/useTournamentForm.ts @@ -1,8 +1,10 @@ -import { TournamentFormProps } from "@/types"; import { useRef, useState } from "react"; -import { LatLngLiteral } from "leaflet"; + import { useAtomValue } from "jotai"; +import { LatLngLiteral } from "leaflet"; + import { franceCenterAtom } from "@/app/atoms"; +import { TournamentFormProps } from "@/types"; export const useTournamentForm = (): TournamentFormProps => { const center = useAtomValue(franceCenterAtom); diff --git a/leafletTypes.ts b/leafletTypes.ts index 00017fa..71f4677 100644 --- a/leafletTypes.ts +++ b/leafletTypes.ts @@ -1,4 +1,4 @@ -import { Marker } from "leaflet"; +import { Marker } from "leaflet"; declare class BouncingOptions { bounceHeight: number; diff --git a/lib/pie.ts b/lib/pie.ts index bd67be7..0eb1d20 100644 --- a/lib/pie.ts +++ b/lib/pie.ts @@ -3,11 +3,11 @@ function round(n: number) { } function polarToCartesian(radius: number, angleInDegrees: number) { - var radians = (angleInDegrees - 90) * Math.PI / 180; + var radians = ((angleInDegrees - 90) * Math.PI) / 180; return { - x: round(radius + (radius * Math.cos(radians))), - y: round(radius + (radius * Math.sin(radians))) + x: round(radius + radius * Math.cos(radians)), + y: round(radius + radius * Math.sin(radians)), }; } @@ -21,8 +21,17 @@ function getDAttribute(radius: number, startAngle: number, endAngle: number) { const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1; const d = [ - "M", start.x, start.y, - "A", radius, radius, 0, largeArcFlag, 1, end.x, end.y + "M", + start.x, + start.y, + "A", + radius, + radius, + 0, + largeArcFlag, + 1, + end.x, + end.y, ]; if (isCircle) { @@ -37,32 +46,39 @@ function path(d: string, colour: string) { return ``; } -function svg(className: string, width: number, content: string ) { +function svg(className: string, width: number, content: string) { return `${content}`; } type PieSlice = { value: number; colour: string; -} +}; -export function generatePieSVG(className: string, radius: number, values: PieSlice[]) { +export function generatePieSVG( + className: string, + radius: number, + values: PieSlice[], +) { type Sector = { colour: string; degrees: number; from?: number; to?: number; - } + }; const total = values.reduce((a, b) => a + b.value, 0); - const data: Sector[] = values.map(({ value, colour }) => ({ colour, degrees: value / total * 360 })); + const data: Sector[] = values.map(({ value, colour }) => ({ + colour, + degrees: (value / total) * 360, + })); const paths = data.reduce<[number, string][]>((prev, value, i) => { const from = i === 0 ? 0 : prev[i - 1][0]; const to = from + value.degrees; - return [...prev, [to, path(getDAttribute(radius, from, to ), value.colour)]]; + return [...prev, [to, path(getDAttribute(radius, from, to), value.colour)]]; }, []); - return svg(className, radius * 2, paths.map(([to, path]) => path) .join('')); + return svg(className, radius * 2, paths.map(([to, path]) => path).join("")); } diff --git a/lib/tournamentFormToDB.ts b/lib/tournamentFormToDB.ts index 7256c86..45e9756 100644 --- a/lib/tournamentFormToDB.ts +++ b/lib/tournamentFormToDB.ts @@ -1,4 +1,5 @@ import { LatLngLiteral } from "leaflet"; + import { TournamentFormProps } from "@/types"; import formatDate from "@/utils/formatDate"; diff --git a/package-lock.json b/package-lock.json index ea700c1..41a0c11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@kodingdotninja/use-tailwind-breakpoint": "^0.0.5", "@next/bundle-analyzer": "^13.4.12", + "@trivago/prettier-plugin-sort-imports": "^4.2.0", "@types/node": "20.5.1", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", @@ -82,34 +83,198 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", - "dev": true, + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-identifier": { + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "dev": true, + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/types": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", - "dev": true, + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -120,7 +285,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -132,7 +296,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -146,7 +309,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -154,14 +316,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -170,7 +330,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -179,7 +338,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -187,6 +345,17 @@ "node": ">=4" } }, + "node_modules/@babel/parser": { + "version": "7.22.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", @@ -198,6 +367,72 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/types": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -815,6 +1050,28 @@ "react-dom": "^18.0.0" } }, + "node_modules/@trivago/prettier-plugin-sort-imports": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.2.0.tgz", + "integrity": "sha512-YBepjbt+ZNBVmN3ev1amQH3lWCmHyt5qTbLCp/syXJRu/Kw2koXh44qayB1gMRxcL/gV8egmjN5xWSrYyfUtyw==", + "dependencies": { + "@babel/generator": "7.17.7", + "@babel/parser": "^7.20.5", + "@babel/traverse": "7.17.3", + "@babel/types": "7.17.0", + "javascript-natural-sort": "0.7.1", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@vue/compiler-sfc": "3.x", + "prettier": "2.x - 3.x" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + } + } + }, "node_modules/@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", @@ -1725,7 +1982,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -3584,6 +3840,11 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, "node_modules/jiti": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", @@ -3625,6 +3886,17 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3942,8 +4214,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mz": { "version": "2.7.0", @@ -4663,7 +4934,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", - "dev": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5416,6 +5686,14 @@ "npm": ">= 3.0.0" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5802,6 +6080,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 535855a..37a1bfd 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,14 @@ "dev": "next dev", "build": "ANALYZE=true next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "format": "prettier --check \"**/*.{js,jsx,ts,tsx}\"", + "format:fix": "prettier --write \"**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { "@kodingdotninja/use-tailwind-breakpoint": "^0.0.5", "@next/bundle-analyzer": "^13.4.12", + "@trivago/prettier-plugin-sort-imports": "^4.2.0", "@types/node": "20.5.1", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", diff --git a/tailwind.config.js b/tailwind.config.js index 7ae172a..acf2c90 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -9,35 +9,41 @@ module.exports = { theme: { extend: { fontFamily: { - sans: ['var(--font-inter)'], - title: ['var(--font-title)'], + sans: ["var(--font-inter)"], + title: ["var(--font-title)"], }, colors: { - 'primary': { - DEFAULT: '#0086C7', - 50: '#A8E3FF', - 100: '#94DCFF', - 200: '#6BCFFF', - 300: '#42C1FF', - 400: '#1AB4FF', - 500: '#00A1F0', - 600: '#0086C7', - 700: '#00608F', - 800: '#003A57', - 900: '#00151F', - 950: '#000203' + primary: { + DEFAULT: "#0086C7", + 50: "#A8E3FF", + 100: "#94DCFF", + 200: "#6BCFFF", + 300: "#42C1FF", + 400: "#1AB4FF", + 500: "#00A1F0", + 600: "#0086C7", + 700: "#00608F", + 800: "#003A57", + 900: "#00151F", + 950: "#000203", }, }, minHeight: { // We use 100svh, falling back to the the --1svh var that we add as a polyfill for older browsers. // The 9rem is for the header and footer. // In the event that the polyfill fails, we fall back to 0.9vh as a guess of how much 1svh is. - content: ["calc(var(--1svh, 0.9vh) * 100 - 9rem)", "calc(100svh - 9rem)"], + content: [ + "calc(var(--1svh, 0.9vh) * 100 - 9rem)", + "calc(100svh - 9rem)", + ], }, height: { - content: ["calc(var(--1svh, 0.9vh) * 100 - 9rem)", "calc(100svh - 9rem)"], + content: [ + "calc(var(--1svh, 0.9vh) * 100 - 9rem)", + "calc(100svh - 9rem)", + ], }, }, }, - plugins: [require('@tailwindcss/forms')], + plugins: [require("@tailwindcss/forms")], }; diff --git a/types.ts b/types.ts index aa298a3..5f281d3 100644 --- a/types.ts +++ b/types.ts @@ -1,6 +1,7 @@ -import { LatLngLiteral } from "leaflet"; import { Dispatch, MutableRefObject, SetStateAction } from "react"; +import { LatLngLiteral } from "leaflet"; + export enum TimeControl { Classic = "Classic", Rapid = "Rapid", diff --git a/utils/atomWithDebounce.ts b/utils/atomWithDebounce.ts index 3765983..4ffa906 100644 --- a/utils/atomWithDebounce.ts +++ b/utils/atomWithDebounce.ts @@ -1,4 +1,4 @@ -import { atom, SetStateAction } from "jotai"; +import { SetStateAction, atom } from "jotai"; export default function atomWithDebounce( initialValue: T, @@ -37,9 +37,14 @@ export default function atomWithDebounce( onDebounceStart(); - const nextTimeoutId = setTimeout(() => { - onDebounceEnd(); - }, nextValue === initialValue ? delayOnResetMilliseconds : delayMilliseconds); + const nextTimeoutId = setTimeout( + () => { + onDebounceEnd(); + }, + nextValue === initialValue + ? delayOnResetMilliseconds + : delayMilliseconds, + ); // set previous timeout atom in case it needs to get cleared set(prevTimeoutAtom, nextTimeoutId);