From d7a3db07ab8fdab941308f5c9ee2871e9a9f4371 Mon Sep 17 00:00:00 2001 From: AmirReza Jamali Date: Wed, 20 May 2026 07:27:00 +0330 Subject: [PATCH] feat(tender-details): enhance language translation handling in TenderDetails component - Added useRef hooks to manage pending language changes and prevent retry loops during translation. - Updated the language query parameter handling to ensure translations are only triggered when the user explicitly selects a language. - Improved the useEffect logic to handle translation requests based on fetched data and user selections, enhancing user experience and performance. --- src/app/tenders/[details]/page.tsx | 59 ++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/app/tenders/[details]/page.tsx b/src/app/tenders/[details]/page.tsx index 40f0695..a41ba50 100644 --- a/src/app/tenders/[details]/page.tsx +++ b/src/app/tenders/[details]/page.tsx @@ -10,7 +10,7 @@ import { } from "@/hooks/queries"; import type { TTenderDetails } from "@/lib/api/types/Tenders"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { use, useCallback, useMemo } from "react"; +import { use, useCallback, useEffect, useMemo, useRef } from "react"; import { TenderDetailsContent } from "./components/tender-details-content"; import { TenderDetailsError } from "./components/tender-details-error"; import { TenderDetailsHeader } from "./components/tender-details-header"; @@ -31,9 +31,10 @@ const TenderDetails = ({ params }: IProps) => { const selectedLanguage = searchParams.get("lang") || "en"; const { mutate: translateTender, isPending: isTranslating } = useTranslateTenderMutation(); - const { data, isLoading, isError } = useTenderDetailQuery(details, { - lang: selectedLanguage, - }); + const { data, isLoading, isError, isFetching } = useTenderDetailQuery( + details, + { language: selectedLanguage }, + ); const breadcrumbItems = [ { name: "Dashboard", href: "/" }, @@ -63,6 +64,13 @@ const TenderDetails = ({ params }: IProps) => { [tenderNavItems], ); const activeSectionId = useTenderSectionScrollSpy(sectionIds); + + // Only translate when the user explicitly picks a language from the dropdown. + // The ref is set inside handleLanguageChange and consumed by the effect once + // the GET refetch resolves; initial page loads never trigger a translate. + const userPendingLangRef = useRef(null); + const attemptedRef = useRef>(new Set()); + const handleLanguageChange = useCallback( (event: React.ChangeEvent) => { const nextLanguage = event.target.value; @@ -71,16 +79,53 @@ const TenderDetails = ({ params }: IProps) => { const prevLanguage = searchParams.get("lang") || "en"; if (nextLanguage === prevLanguage) return; + userPendingLangRef.current = nextLanguage; + const nextParams = new URLSearchParams(searchParams.toString()); nextParams.set("lang", nextLanguage); const queryString = nextParams.toString(); router.replace(queryString ? `${pathname}?${queryString}` : pathname); - - translateTender({ id: details, language: nextLanguage }); }, - [details, pathname, router, searchParams, translateTender], + [pathname, router, searchParams], ); + // ensureTranslation flow (per backend integration doc): + // 1) User picks a language → handleLanguageChange marks it as pending. + // 2) GET detail with ?language={selected} refetches. + // 3) If the refetched data.language doesn't match, POST ai-translate. + // The mutation's onSuccess invalidates the detail query → refetch binds title/description. + // attemptedRef prevents a retry loop if translation fails. + useEffect(() => { + const fetched = data?.data; + if (!fetched) return; + if (isFetching || isTranslating) return; + + const pending = userPendingLangRef.current; + if (!pending || pending !== selectedLanguage) return; + + if (fetched.language === selectedLanguage) { + userPendingLangRef.current = null; + return; + } + + const key = `${details}::${selectedLanguage}`; + if (attemptedRef.current.has(key)) { + userPendingLangRef.current = null; + return; + } + attemptedRef.current.add(key); + userPendingLangRef.current = null; + + translateTender({ id: details, language: selectedLanguage }); + }, [ + data, + details, + isFetching, + isTranslating, + selectedLanguage, + translateTender, + ]); + if (isLoading) return ; if (isError) { return ;