diff --git a/app/discount/page.tsx b/app/discount/page.tsx index 98514b1..2d2db4a 100644 --- a/app/discount/page.tsx +++ b/app/discount/page.tsx @@ -10,11 +10,13 @@ import { fetchAndValidateJson } from "@/utils/fetchAndValidateJson"; import { searchForJourneys } from "@/utils/journeyUtils"; import type { VendoJourney } from "@/utils/schemas"; import { useSearchParams } from "next/navigation"; -import { Suspense, useEffect } from "react"; +import { Suspense, useEffect, useRef } from "react"; import { z } from "zod/v4"; function Discount() { - const searchParams = useSearchParams(); + const searchParams = useSearchParams(); + const searchParamsString = searchParams.toString(); + const lastSearchParamsRef = useRef(null); const { status, journeys, @@ -34,55 +36,59 @@ function Discount() { handleJourneySelect, } = useDiscountAnalysis(); - // Effects - useEffect(() => { - const initializeFlow = async () => { - try { - const urlFromParams = searchParams.get("url"); - if (!urlFromParams) { - throw new Error("No URL provided for parsing."); - } - - // Parse URL - setLoadingMessage(LOADING_MESSAGES.parsing); - - // yes, the following is some ugly and probably redundant client-side validation, but imo this will help us progressively narrow down certain bugs - const parseUrlRequest = await fetchAndValidateJson({ - url: "/api/parse-url", - method: "POST", - body: { url: urlFromParams }, - schema: z.object({ - success: z.literal(true), - journeyDetails: z.object({ - fromStation: z.string().nullable().optional(), - toStation: z.string().nullable().optional(), - fromStationId: z.string().nullable().optional(), - toStationId: z.string().nullable().optional(), - date: z.unknown().optional(), - time: z.string().nullable().optional(), - class: z.number().nullable().optional(), - }), - }), - }); - - const { journeyDetails } = parseUrlRequest.data; - - const journeyData = { - fromStation: journeyDetails.fromStation, - toStation: journeyDetails.toStation, - fromStationId: journeyDetails.fromStationId, - toStationId: journeyDetails.toStationId, - date: journeyDetails.date, - time: journeyDetails.time, - travelClass: - journeyDetails.class?.toString() || - searchParams.get("travelClass") || - "2", - bahnCard: searchParams.get("bahnCard") || "none", - hasDeutschlandTicket: - searchParams.get("hasDeutschlandTicket") === "true", - passengerAge: searchParams.get("passengerAge") || "", - }; + // Effects + useEffect(() => { + if (lastSearchParamsRef.current === searchParamsString) return; + lastSearchParamsRef.current = searchParamsString; + + const initializeFlow = async () => { + try { + const params = new URLSearchParams(searchParamsString); + const urlFromParams = params.get("url"); + if (!urlFromParams) { + throw new Error("No URL provided for parsing."); + } + + // Parse URL + setLoadingMessage(LOADING_MESSAGES.parsing); + + // yes, the following is some ugly and probably redundant client-side validation, but imo this will help us progressively narrow down certain bugs + const parseUrlRequest = await fetchAndValidateJson({ + url: "/api/parse-url", + method: "POST", + body: { url: urlFromParams }, + schema: z.object({ + success: z.literal(true), + journeyDetails: z.object({ + fromStation: z.string().nullable().optional(), + toStation: z.string().nullable().optional(), + fromStationId: z.string().nullable().optional(), + toStationId: z.string().nullable().optional(), + date: z.unknown().optional(), + time: z.string().nullable().optional(), + class: z.number().nullable().optional(), + }), + }), + }); + + const { journeyDetails } = parseUrlRequest.data; + + const journeyData = { + fromStation: journeyDetails.fromStation, + toStation: journeyDetails.toStation, + fromStationId: journeyDetails.fromStationId, + toStationId: journeyDetails.toStationId, + date: journeyDetails.date, + time: journeyDetails.time, + travelClass: + journeyDetails.class?.toString() || + params.get("travelClass") || + "2", + bahnCard: params.get("bahnCard") || "none", + hasDeutschlandTicket: + params.get("hasDeutschlandTicket") === "true", + passengerAge: params.get("passengerAge") || "", + }; setExtractedData(journeyData); @@ -110,8 +116,8 @@ function Discount() { } }; - initializeFlow(); - }, [searchParams, analyzeSplitOptions]); + initializeFlow(); + }, [searchParamsString, analyzeSplitOptions]); // Computed values const getStatusMessage = () => {