From d75d0042c7cd22a741c09375a08187bce0a57ff2 Mon Sep 17 00:00:00 2001 From: Anton Veselev Date: Sat, 6 Jul 2024 01:04:28 +0100 Subject: [PATCH] el ls --- src/config.ts | 2 +- .../LeagueStage}/MatchesTable.tsx | 0 .../LeagueStage}/Schedule.tsx | 0 src/containers/LeagueStage/index.tsx | 170 +++++++++++++++++ src/data/el/ls/2024/pots.json | 154 ++++++++++++++++ src/pages/cl/ls/index.tsx | 172 +----------------- src/pages/el/ls/index.tsx | 12 ++ src/routes/currentSeasonByTournament.ts | 4 +- src/routes/index.tsx | 9 + 9 files changed, 356 insertions(+), 167 deletions(-) rename src/{pages/cl/ls => containers/LeagueStage}/MatchesTable.tsx (100%) rename src/{pages/cl/ls => containers/LeagueStage}/Schedule.tsx (100%) create mode 100644 src/containers/LeagueStage/index.tsx create mode 100644 src/data/el/ls/2024/pots.json create mode 100644 src/pages/el/ls/index.tsx diff --git a/src/config.ts b/src/config.ts index 615a8a90..a720f7a6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -10,7 +10,7 @@ export default { ko: 2023, }, el: { - gs: 2023, + ls: 2024, ko: 2022, }, ecl: { diff --git a/src/pages/cl/ls/MatchesTable.tsx b/src/containers/LeagueStage/MatchesTable.tsx similarity index 100% rename from src/pages/cl/ls/MatchesTable.tsx rename to src/containers/LeagueStage/MatchesTable.tsx diff --git a/src/pages/cl/ls/Schedule.tsx b/src/containers/LeagueStage/Schedule.tsx similarity index 100% rename from src/pages/cl/ls/Schedule.tsx rename to src/containers/LeagueStage/Schedule.tsx diff --git a/src/containers/LeagueStage/index.tsx b/src/containers/LeagueStage/index.tsx new file mode 100644 index 00000000..4a23f97b --- /dev/null +++ b/src/containers/LeagueStage/index.tsx @@ -0,0 +1,170 @@ +import { memo, useEffect, useMemo, useState } from 'react'; +import styled from 'styled-components'; +import { shuffle } from 'lodash'; + +import usePopup from '#store/usePopup'; +import type Team from '#model/team/GsTeam'; +import generatePairings from '#engine/dfs/ls/generatePairings/index'; +import generateSchedule from '#engine/dfs/ls/generateSchedule/index'; +import useAbortSignal from '#utils/hooks/useAbortSignal'; +import Button from '#ui/Button'; +import Portal from '#ui/Portal'; +import Dots from '#ui/Dots'; + +import Schedule from './Schedule'; +import MatchesTable from './MatchesTable'; + +const Root = styled.div` + display: flex; + flex-direction: column; + margin: 10px; + font-size: 14px; +`; + +const MatrixWrapper = styled.div` + display: flex; + flex-wrap: wrap; + gap: 16px; +`; + +interface Props { + pots: readonly (readonly Team[])[]; +} + +function LeagueStage({ pots: initialPots }: Props) { + const numMatchdays = initialPots.length * 2; + + const numMatches = useMemo(() => { + const numTeams = initialPots.flat().length; + return (numTeams * numMatchdays) / 2; + }, [initialPots, numMatchdays]); + + const [, setPopup] = usePopup(); + + const [isMatchdayMode, setIsMatchdayMode] = useState(false); + + const [pairings, setPairings] = useState<(readonly [Team, Team])[]>([]); + const [schedule, setSchedule] = useState<(readonly [Team, Team])[][]>( + Array.from( + { + length: numMatchdays, + }, + () => [], + ), + ); + const [isFixturesDone, setIsFixturesDone] = useState(false); + + const abortSignal = useAbortSignal(); + + const pots = useMemo( + () => + initialPots.map(pot => + pot.map(team => ({ + ...team, + id: `${team.country}:${team.name}`, + })), + ), + [initialPots], + ); + + const allTeams = useMemo(() => pots.flat(), [pots]); + + const matchdaySize = allTeams.length / 2; + + useEffect(() => { + setPopup({ + waiting: false, + }); + }, []); + + useEffect(() => { + const formPairings = async () => { + const generator = generatePairings({ + pots, + numMatchdays: 8, + isMatchPossible: (a, b) => a.country !== b.country, + }); + for await (const pickedMatch of generator) { + setPairings(prev => [...prev, pickedMatch]); + } + setIsFixturesDone(true); + }; + + formPairings(); + }, []); + + useEffect(() => { + if (isFixturesDone) { + const formSchedule = async () => { + // setIsMatchdayMode(true); + // setSchedule(chunk(pairings, 18)); + const it = await generateSchedule({ + matchdaySize, + allGames: pairings, + currentSchedule: schedule, + signal: abortSignal, + }); + setSchedule(it.solutionSchedule.map(md => shuffle(md))); + setIsMatchdayMode(true); + }; + + formSchedule(); + } + }, [isFixturesDone]); + + const isScheduleDone = useMemo( + () => schedule.some(md => md.length > 0), + [schedule], + ); + + return ( + + + + + {isMatchdayMode ? ( + + ) : ( + + +
+ {isFixturesDone ? ( +

All {numMatches} matches have been drawn.

+ ) : ( +

+ Drawn matches: {pairings.length}/{numMatches} +

+ )} + {isFixturesDone && !isScheduleDone && ( +

+ Schedule creation in progress. This will take a while. Please do + not close the page + +

+ )} +
+
+ )} +
+ ); +} + +export default memo(LeagueStage); diff --git a/src/data/el/ls/2024/pots.json b/src/data/el/ls/2024/pots.json new file mode 100644 index 00000000..4b39a597 --- /dev/null +++ b/src/data/el/ls/2024/pots.json @@ -0,0 +1,154 @@ +[ + [ + { + "name": "Roma", + "country": "Italy" + }, + { + "name": "Man United", + "country": "England" + }, + { + "name": "Porto", + "country": "Portugal" + }, + { + "name": "Ajax", + "country": "Netherlands" + }, + { + "name": "Frankfurt", + "country": "Germany" + }, + { + "name": "Lazio", + "country": "Italy" + }, + { + "name": "Tottenham", + "country": "England" + }, + { + "name": "Real Sociedad", + "country": "Spain" + }, + { + "name": "Salzburg", + "country": "Austria" + } + ], + [ + { + "name": "AZ", + "country": "Netherlands" + }, + { + "name": "Braga", + "country": "Portugal" + }, + { + "name": "Olympiacos", + "country": "Greece" + }, + { + "name": "Lille", + "country": "France" + }, + { + "name": "Lyon", + "country": "France" + }, + { + "name": "LASK", + "country": "Austria" + }, + { + "name": "Fenerbahçe", + "country": "Turkey" + }, + { + "name": "Young Boys", + "country": "Switzerland" + }, + { + "name": "Qarabağ", + "country": "Azerbaijan" + } + ], + [ + { + "name": "Galatasaray", + "country": "Turkey" + }, + { + "name": "Slovan", + "country": "Slovakia" + }, + { + "name": "Molde", + "country": "Norway" + }, + { + "name": "Plzeň", + "country": "Czechia" + }, + { + "name": "Bodø/Glimt", + "country": "Norway" + }, + { + "name": "Union SG", + "country": "Belgium" + }, + { + "name": "Dynamo Kyiv", + "country": "Ukraine" + }, + { + "name": "Ludogorets", + "country": "Bulgaria" + }, + { + "name": "Midtjylland", + "country": "Denmark" + } + ], + [ + { + "name": "Partizan", + "country": "Serbia" + }, + { + "name": "Sparta", + "country": "Czechia" + }, + { + "name": "Sheriff", + "country": "Moldova" + }, + { + "name": "Malmö", + "country": "Sweden" + }, + { + "name": "Athletic", + "country": "Spain" + }, + { + "name": "Hoffenheim", + "country": "Germany" + }, + { + "name": "Nice", + "country": "France" + }, + { + "name": "APOEL", + "country": "Cyprus" + }, + { + "name": "Twente", + "country": "Netherlands" + } + ] +] diff --git a/src/pages/cl/ls/index.tsx b/src/pages/cl/ls/index.tsx index 4a23f97b..3a316e21 100644 --- a/src/pages/cl/ls/index.tsx +++ b/src/pages/cl/ls/index.tsx @@ -1,170 +1,12 @@ -import { memo, useEffect, useMemo, useState } from 'react'; -import styled from 'styled-components'; -import { shuffle } from 'lodash'; +import type React from 'react'; +import { memo } from 'react'; -import usePopup from '#store/usePopup'; -import type Team from '#model/team/GsTeam'; -import generatePairings from '#engine/dfs/ls/generatePairings/index'; -import generateSchedule from '#engine/dfs/ls/generateSchedule/index'; -import useAbortSignal from '#utils/hooks/useAbortSignal'; -import Button from '#ui/Button'; -import Portal from '#ui/Portal'; -import Dots from '#ui/Dots'; +import LeagueStage from '#containers/LeagueStage/index'; -import Schedule from './Schedule'; -import MatchesTable from './MatchesTable'; +type Props = React.ComponentProps; -const Root = styled.div` - display: flex; - flex-direction: column; - margin: 10px; - font-size: 14px; -`; - -const MatrixWrapper = styled.div` - display: flex; - flex-wrap: wrap; - gap: 16px; -`; - -interface Props { - pots: readonly (readonly Team[])[]; -} - -function LeagueStage({ pots: initialPots }: Props) { - const numMatchdays = initialPots.length * 2; - - const numMatches = useMemo(() => { - const numTeams = initialPots.flat().length; - return (numTeams * numMatchdays) / 2; - }, [initialPots, numMatchdays]); - - const [, setPopup] = usePopup(); - - const [isMatchdayMode, setIsMatchdayMode] = useState(false); - - const [pairings, setPairings] = useState<(readonly [Team, Team])[]>([]); - const [schedule, setSchedule] = useState<(readonly [Team, Team])[][]>( - Array.from( - { - length: numMatchdays, - }, - () => [], - ), - ); - const [isFixturesDone, setIsFixturesDone] = useState(false); - - const abortSignal = useAbortSignal(); - - const pots = useMemo( - () => - initialPots.map(pot => - pot.map(team => ({ - ...team, - id: `${team.country}:${team.name}`, - })), - ), - [initialPots], - ); - - const allTeams = useMemo(() => pots.flat(), [pots]); - - const matchdaySize = allTeams.length / 2; - - useEffect(() => { - setPopup({ - waiting: false, - }); - }, []); - - useEffect(() => { - const formPairings = async () => { - const generator = generatePairings({ - pots, - numMatchdays: 8, - isMatchPossible: (a, b) => a.country !== b.country, - }); - for await (const pickedMatch of generator) { - setPairings(prev => [...prev, pickedMatch]); - } - setIsFixturesDone(true); - }; - - formPairings(); - }, []); - - useEffect(() => { - if (isFixturesDone) { - const formSchedule = async () => { - // setIsMatchdayMode(true); - // setSchedule(chunk(pairings, 18)); - const it = await generateSchedule({ - matchdaySize, - allGames: pairings, - currentSchedule: schedule, - signal: abortSignal, - }); - setSchedule(it.solutionSchedule.map(md => shuffle(md))); - setIsMatchdayMode(true); - }; - - formSchedule(); - } - }, [isFixturesDone]); - - const isScheduleDone = useMemo( - () => schedule.some(md => md.length > 0), - [schedule], - ); - - return ( - - - - - {isMatchdayMode ? ( - - ) : ( - - -
- {isFixturesDone ? ( -

All {numMatches} matches have been drawn.

- ) : ( -

- Drawn matches: {pairings.length}/{numMatches} -

- )} - {isFixturesDone && !isScheduleDone && ( -

- Schedule creation in progress. This will take a while. Please do - not close the page - -

- )} -
-
- )} -
- ); +function CLLS(props: Props) { + return ; } -export default memo(LeagueStage); +export default memo(CLLS); diff --git a/src/pages/el/ls/index.tsx b/src/pages/el/ls/index.tsx new file mode 100644 index 00000000..87bfc9b1 --- /dev/null +++ b/src/pages/el/ls/index.tsx @@ -0,0 +1,12 @@ +import type React from 'react'; +import { memo } from 'react'; + +import LeagueStage from '#containers/LeagueStage/index'; + +type Props = React.ComponentProps; + +function ELLS(props: Props) { + return ; +} + +export default memo(ELLS); diff --git a/src/routes/currentSeasonByTournament.ts b/src/routes/currentSeasonByTournament.ts index f2484e68..ba9002c7 100644 --- a/src/routes/currentSeasonByTournament.ts +++ b/src/routes/currentSeasonByTournament.ts @@ -7,7 +7,9 @@ const { wc, uefa } = config.currentSeason; export default (tournament: Tournament | null, stage: Stage | null): number => { const resolvedTournament = tournament || 'cl'; - const resolvedState = stage || (resolvedTournament === 'cl' ? 'ls' : 'gs'); + const resolvedState = + stage || + (resolvedTournament === 'cl' || resolvedTournament === 'el' ? 'ls' : 'gs'); return resolvedTournament === 'wc' ? wc : // @ts-expect-error Fix later diff --git a/src/routes/index.tsx b/src/routes/index.tsx index f1bac03c..2c213aee 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -140,6 +140,15 @@ function Routing() { /> } /> + + } + />