From 8e56032b2a3682e9a6d100792c7376a067e594d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aron=20Sch=C3=BCler?= Date: Sun, 1 Sep 2024 13:47:20 +0200 Subject: [PATCH] fix: correct date parsing for other english dates also move utils a bit around --- src/{utils => components}/AuthRedirects.tsx | 0 .../panels/AllDataCombinedTable.tsx | 2 +- src/components/panels/AveragesPerSession.tsx | 4 +- src/components/panels/AveragesTable.tsx | 2 +- src/components/panels/ShotScatterPlot.tsx | 7 ++- src/firebase.ts | 2 + src/provider/SessionContext.tsx | 56 ++++++++++--------- src/router.tsx | 4 +- src/utils/date.utils.ts | 17 ++++++ src/{provider => utils}/settings.utils.ts | 0 src/{ => utils}/utils.ts | 34 +++++++++-- 11 files changed, 87 insertions(+), 41 deletions(-) rename src/{utils => components}/AuthRedirects.tsx (100%) create mode 100644 src/utils/date.utils.ts rename src/{provider => utils}/settings.utils.ts (100%) rename src/{ => utils}/utils.ts (82%) diff --git a/src/utils/AuthRedirects.tsx b/src/components/AuthRedirects.tsx similarity index 100% rename from src/utils/AuthRedirects.tsx rename to src/components/AuthRedirects.tsx diff --git a/src/components/panels/AllDataCombinedTable.tsx b/src/components/panels/AllDataCombinedTable.tsx index 3f67c8c..6e64c8d 100644 --- a/src/components/panels/AllDataCombinedTable.tsx +++ b/src/components/panels/AllDataCombinedTable.tsx @@ -5,9 +5,9 @@ import { AgGridReact } from "ag-grid-react"; import { useContext, useEffect, useState } from "react"; import { SessionContext } from "../../provider/SessionContext"; import { GolfSwingData } from "../../types/GolfSwingData"; -import { sortGolfSwingKeysForHeader } from "../../utils"; import { translateHeader } from "../../utils/csvLocalization"; import { getAllDataFromSession } from "../../utils/getAllDataFromSession"; +import { sortGolfSwingKeysForHeader } from "../../utils/utils"; import { BaseDisclosure } from "../base/BaseDisclosure"; export const AllDataCombinedTable = () => { diff --git a/src/components/panels/AveragesPerSession.tsx b/src/components/panels/AveragesPerSession.tsx index d52c7c3..13adb41 100644 --- a/src/components/panels/AveragesPerSession.tsx +++ b/src/components/panels/AveragesPerSession.tsx @@ -6,11 +6,11 @@ import { useClubsPerSession } from "../../hooks/useClubsPerSesssion"; import { SessionContext } from "../../provider/SessionContext"; import { GolfSwingData } from "../../types/GolfSwingData"; import { useAveragePerSession } from "../../utils/calculateAverages"; +import { getPairsForYfield, parseDate } from "../../utils/utils"; import { BaseLabel } from "../base/BaseLabel"; import { BaseListbox } from "../base/BaseListbox"; import type { ClubDataForTable } from "./AveragesPerSessionGraph"; import { AveragesPerSessionGraph } from "./AveragesPerSessionGraph"; -import { parseDate, getPairsForYfield } from "../../utils"; dayjs.extend(customParseFormat); export const AveragesPerSession = () => { @@ -47,8 +47,6 @@ export const AveragesPerSession = () => { return []; }, [sessions, clubSelected, clubDataByDate, averages, yField, club]); - console.log(averagesByDate); - return (

diff --git a/src/components/panels/AveragesTable.tsx b/src/components/panels/AveragesTable.tsx index d9f21f0..32e2b4e 100644 --- a/src/components/panels/AveragesTable.tsx +++ b/src/components/panels/AveragesTable.tsx @@ -2,12 +2,12 @@ import { ColDef } from "ag-grid-community"; import { AgGridReact } from "ag-grid-react"; import { useContext, useEffect, useState } from "react"; import { SessionContext } from "../../provider/SessionContext"; -import { sortGolfSwingKeysForHeader } from "../../utils"; import { AveragedSwing, useAveragedSwings, } from "../../utils/calculateAverages"; import { translateHeader } from "../../utils/csvLocalization"; +import { sortGolfSwingKeysForHeader } from "../../utils/utils"; import { BaseDisclosure } from "../base/BaseDisclosure"; import { BaseLabel } from "../base/BaseLabel"; const defaultColumns: ColDef[] = [ diff --git a/src/components/panels/ShotScatterPlot.tsx b/src/components/panels/ShotScatterPlot.tsx index 332845b..89989c6 100644 --- a/src/components/panels/ShotScatterPlot.tsx +++ b/src/components/panels/ShotScatterPlot.tsx @@ -7,10 +7,11 @@ import { golfSwingDataKeysInMeters, } from "../../types/GolfSwingData"; import { Sessions } from "../../types/Sessions"; +import { getDayFromRow } from "../../utils/date.utils"; import { getAllDataFromSession } from "../../utils/getAllDataFromSession"; +import { parseDate } from "../../utils/utils"; import { BaseLabel } from "../base/BaseLabel"; import { BaseListbox } from "../base/BaseListbox"; -import { parseDate } from "../../utils"; export const ShotScatterPlot = () => { const { sessions } = useContext(SessionContext); @@ -134,7 +135,7 @@ export const ShotScatterPlot = () => { table: clubs[club].map((row) => ({ x: row[xField as keyof GolfSwingData], y: row[yField as keyof GolfSwingData], - date: parseDate((row["Date"] || row["Datum"])?.split(" ")[0] ?? ""), + date: parseDate(getDayFromRow(row)), })), }; } @@ -143,7 +144,7 @@ export const ShotScatterPlot = () => { table: swings.map((row) => ({ x: row[xField as keyof GolfSwingData], y: row[yField as keyof GolfSwingData], - date: parseDate((row["Date"] || row["Datum"])?.split(" ")[0] ?? ""), + date: parseDate(getDayFromRow(row)), })), }; } diff --git a/src/firebase.ts b/src/firebase.ts index 0f1868b..f90edcc 100644 --- a/src/firebase.ts +++ b/src/firebase.ts @@ -24,3 +24,5 @@ if (import.meta.env.DEV && import.meta.env.VITE_USE_EMULATORS === "true") { connectAuthEmulator(auth, "http://localhost:9099"); connectFirestoreEmulator(db, "localhost", 8080); } + +export const ANONYMOUS_USER_UID = "6U4S2ruwXMPrULj50b9uLpsaRk53"; diff --git a/src/provider/SessionContext.tsx b/src/provider/SessionContext.tsx index 84fa365..f260ca0 100644 --- a/src/provider/SessionContext.tsx +++ b/src/provider/SessionContext.tsx @@ -7,11 +7,12 @@ import React, { useMemo, useState, } from "react"; -import { db } from "../firebase"; +import { ANONYMOUS_USER_UID, db } from "../firebase"; import { Session, Sessions } from "../types/Sessions"; -import { reduceSessionToDefinedValues } from "../utils"; import { translateSessionsToEnglish } from "../utils/csvLocalization"; +import { getDateFromResults } from "../utils/date.utils"; import { filterResultsWithMissingCells } from "../utils/filterResultsWithMissingCells"; +import { reduceSessionToDefinedValues } from "../utils/utils"; import { UserContext } from "./UserContext"; export interface SessionContextInterface { @@ -21,27 +22,29 @@ export interface SessionContextInterface { setSessions: (sessions: Sessions) => void; fetchSnapshot: () => Promise; deleteSession: (id: string) => Promise; + exportSessionsToJson: (sessions: Sessions) => void; } const SessionContext = createContext({ initialized: false, isLoading: false, sessions: {}, - setSessions: () => {}, + setSessions: () => { }, fetchSnapshot: () => Promise.resolve(undefined), deleteSession: () => Promise.resolve(), + exportSessionsToJson: () => { }, }); const SessionProvider: React.FC = ({ children }) => { - const [sessions, _setSessions] = useState({}); - const setSessions = useCallback((sessions: Sessions) => { - _setSessions(filterResultsWithMissingCells(sessions)); + const [sessions, setSessions] = useState({}); + const setSessionsCallback = useCallback((sessions: Sessions) => { + setSessions(filterResultsWithMissingCells(sessions)); }, []); const [initialized, setInitialized] = useState(false); const [isLoading, setIsLoading] = useState(false); const { user } = useContext(UserContext); - const uuid = user?.isAnonymous ? "6U4S2ruwXMPrULj50b9uLpsaRk53" : user?.uid; + const uuid = user?.isAnonymous ? ANONYMOUS_USER_UID : user?.uid; const fetchSnapshot = useCallback(async () => { setIsLoading(true); @@ -58,13 +61,25 @@ const SessionProvider: React.FC = ({ children }) => { }; return acc; }, {} as Sessions); - setSessions(sessionResult); + setSessionsCallback(sessionResult); setInitialized(true); setIsLoading(false); return sessionResult; } setIsLoading(false); - }, [uuid, setSessions]); + }, [uuid, setSessionsCallback]); + + // Export current sessions as JSON file + const exportSessionsToJson = useCallback((sessions: Sessions) => { + const element = document.createElement("a"); + const file = new Blob([JSON.stringify(sessions)], { + type: "application/json", + }); + element.href = URL.createObjectURL(file); + element.download = "sessions.json"; + document.body.appendChild(element); + element.click(); + }, []); const deleteSession = useCallback( async (id: string) => { @@ -73,13 +88,13 @@ const SessionProvider: React.FC = ({ children }) => { } const document = doc(db, "r10data", uuid, "data", id); await deleteDoc(document); - _setSessions((prev: Sessions) => { + setSessions((prev: Sessions) => { const newSessions = { ...prev }; delete newSessions[id]; return newSessions; }); }, - [uuid, _setSessions], + [uuid, setSessions], ); const memoizedValue = useMemo( @@ -87,17 +102,19 @@ const SessionProvider: React.FC = ({ children }) => { initialized, isLoading, sessions: translateSessionsToEnglish(sessions), - setSessions, + setSessions: setSessionsCallback, fetchSnapshot, deleteSession, + exportSessionsToJson, }), [ initialized, isLoading, sessions, - setSessions, + setSessionsCallback, fetchSnapshot, deleteSession, + exportSessionsToJson, ], ); @@ -109,16 +126,3 @@ const SessionProvider: React.FC = ({ children }) => { }; export { SessionContext, SessionProvider }; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const getDateFromResults = (results: any[]) => { - if (results.length > 0) { - const date: string = - results[0].Date || - results[0].Datum || - "No date found. Contact me please."; - - return date.split(" ")[0]; - } - return ""; -}; diff --git a/src/router.tsx b/src/router.tsx index 0458e16..c55c136 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -1,9 +1,9 @@ import { Navigate, Outlet, createBrowserRouter } from "react-router-dom"; -import { routes } from "./routes"; import { RedirectIfNotLoggedIn, RedirectIfSignedIn, -} from "./utils/AuthRedirects"; +} from "./components/AuthRedirects"; +import { routes } from "./routes"; import { Authentication } from "./views/Authentication"; import { Dashboard } from "./views/Dashboard"; import { Goals } from "./views/Goals"; diff --git a/src/utils/date.utils.ts b/src/utils/date.utils.ts new file mode 100644 index 0000000..ddc9ed1 --- /dev/null +++ b/src/utils/date.utils.ts @@ -0,0 +1,17 @@ +export const getDateFromResults = (results: any[]) => { + if (results.length > 0) { + const date: string = + results[0].Date || + results[0].Datum || + "No date found. Contact me please."; + + return date.split(" ")[0]; + } + return ""; +}; + +export const getDayFromRow = (row: any) => { + const date = row["Date"] || row["Datum"] || ""; + const dayPart = date.split(" ")[0] ?? ""; + return dayPart; +}; diff --git a/src/provider/settings.utils.ts b/src/utils/settings.utils.ts similarity index 100% rename from src/provider/settings.utils.ts rename to src/utils/settings.utils.ts diff --git a/src/utils.ts b/src/utils/utils.ts similarity index 82% rename from src/utils.ts rename to src/utils/utils.ts index cd3c5b0..e80ff7f 100644 --- a/src/utils.ts +++ b/src/utils/utils.ts @@ -1,8 +1,8 @@ import dayjs from "dayjs"; -import { ClubDataForTable } from "./components/panels/AveragesPerSessionGraph"; -import { GolfSwingData } from "./types/GolfSwingData"; -import { Session } from "./types/Sessions"; -import { AveragedSwing, AveragedSwingRecord } from "./utils/calculateAverages"; +import { ClubDataForTable } from "../components/panels/AveragesPerSessionGraph"; +import { GolfSwingData } from "../types/GolfSwingData"; +import { Session } from "../types/Sessions"; +import { AveragedSwing, AveragedSwingRecord } from "./calculateAverages"; export const sortGolfSwingKeysForHeader = (a: string, b: string) => { // We want to prioritize Carry Distance, Total Distance, Club Name, Ball Spped, and Date @@ -144,7 +144,31 @@ export const getPairsForYfield: ( // might be english or german format export const parseDate = (input: string) => { if (input.includes("/")) { - return dayjs(input, "MM/DD/YY").format("YYYY-MM-DD"); + const possibleDatFormats = [ + "MM/DD/YYYY", + "MM/DD/YY", + "M/DD/YYYY", + "M/DD/YY", + "MM/D/YYYY", + "MM/D/YY", + "M/D/YYYY", + "M/D/YY", + ]; + + let date = ""; + + const isDateParsed = possibleDatFormats.some((format) => { + const parsedDate = dayjs(input, format, "en"); + if (parsedDate.isValid()) { + date = parsedDate.format("YYYY-MM-DD"); + return true; // Stop iteration once a valid date is found + } + return false; + }); + + if (!isDateParsed) console.error("Could not parse date", input); + + return date; } else if (input.includes(".")) { return dayjs(input, "DD.MM.YY", "de").format("YYYY-MM-DD"); } else {