feat(tender-details): normalize tender documents extraction logic

- Added a new function to handle varying document payload structures from the backend, ensuring consistent extraction of tender documents.
- Updated the TenderDetails component to utilize the new extraction logic, improving reliability in document handling.
- Refactored related imports and removed unused code for better clarity and maintainability.
This commit is contained in:
AmirReza Jamali
2026-05-23 09:12:52 +03:30
parent 2daf5e7d15
commit 4607de5677
2 changed files with 37 additions and 10 deletions
@@ -2,10 +2,10 @@ import type {
TTenderDetails, TTenderDetails,
TTenderDocumentsResponse, TTenderDocumentsResponse,
} from "@/lib/api/types/Tenders"; } from "@/lib/api/types/Tenders";
import { shouldShowLocationSection } from "../tender-nav-utils";
import type { TenderNavItem } from "../types";
import { scrollToTenderSection } from "../scroll-to-section"; import { scrollToTenderSection } from "../scroll-to-section";
import { shouldShowLotsSection } from "../tender-display-utils"; import { shouldShowLotsSection } from "../tender-display-utils";
import { shouldShowLocationSection } from "../tender-nav-utils";
import type { TenderNavItem } from "../types";
import { TenderAiSummarySection } from "./tender-ai-summary-section"; import { TenderAiSummarySection } from "./tender-ai-summary-section";
import { TenderDescriptionSection } from "./tender-description-section"; import { TenderDescriptionSection } from "./tender-description-section";
import { TenderDocumentsSection } from "./tender-documents-section"; import { TenderDocumentsSection } from "./tender-documents-section";
+35 -8
View File
@@ -8,7 +8,10 @@ import {
useTenderDetailQuery, useTenderDetailQuery,
useTranslateTenderMutation, useTranslateTenderMutation,
} from "@/hooks/queries"; } from "@/hooks/queries";
import type { TTenderDetails } from "@/lib/api/types/Tenders"; import type {
TTenderDetails,
TTenderDocumentsResponse,
} from "@/lib/api/types/Tenders";
import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { use, useCallback, useEffect, useMemo, useRef } from "react"; import { use, useCallback, useEffect, useMemo, useRef } from "react";
import { TenderDetailsContent } from "./components/tender-details-content"; import { TenderDetailsContent } from "./components/tender-details-content";
@@ -16,7 +19,6 @@ import { TenderDetailsError } from "./components/tender-details-error";
import { TenderDetailsHeader } from "./components/tender-details-header"; import { TenderDetailsHeader } from "./components/tender-details-header";
import { useTenderSectionScrollSpy } from "./hooks/use-tender-section-scroll-spy"; import { useTenderSectionScrollSpy } from "./hooks/use-tender-section-scroll-spy";
import { getTenderNavItems } from "./tender-nav-utils"; import { getTenderNavItems } from "./tender-nav-utils";
import { shouldShowAiSummarySection } from "./tender-display-utils";
interface IProps { interface IProps {
params: Promise<{ params: Promise<{
@@ -24,6 +26,33 @@ interface IProps {
}>; }>;
} }
// Backend has shipped the documents payload as a bare array, an `ApiResponse`
// wrapper, and a `{ data: { documents: [...] } }` wrapper at different times.
// Normalize so a real list is never dropped because of a wrapper mismatch.
function extractTenderDocuments(
payload: unknown,
): TTenderDocumentsResponse[] {
if (!payload) return [];
if (Array.isArray(payload)) return payload as TTenderDocumentsResponse[];
const wrapper = payload as {
data?: unknown;
documents?: unknown;
};
if (Array.isArray(wrapper.documents)) {
return wrapper.documents as TTenderDocumentsResponse[];
}
if (Array.isArray(wrapper.data)) {
return wrapper.data as TTenderDocumentsResponse[];
}
if (wrapper.data && typeof wrapper.data === "object") {
const inner = (wrapper.data as { documents?: unknown }).documents;
if (Array.isArray(inner)) return inner as TTenderDocumentsResponse[];
}
return [];
}
const TenderDetails = ({ params }: IProps) => { const TenderDetails = ({ params }: IProps) => {
const { details } = use(params); const { details } = use(params);
const router = useRouter(); const router = useRouter();
@@ -44,14 +73,12 @@ const TenderDetails = ({ params }: IProps) => {
]; ];
const tenderForNav = data?.data; const tenderForNav = data?.data;
const hasValidAiSummary = tenderForNav
? shouldShowAiSummarySection(tenderForNav)
: false;
const { data: documentsResponse, isPending: isDocumentsLoading } = const { data: documentsResponse, isPending: isDocumentsLoading } =
useGetTenderDocumentsQuery(details, { enabled: hasValidAiSummary }); useGetTenderDocumentsQuery(details, { enabled: Boolean(tenderForNav) });
const documents = documentsResponse?.data ?? []; const documents = extractTenderDocuments(documentsResponse);
const showDocumentsSection = const showDocumentsSection =
hasValidAiSummary && (isDocumentsLoading || documents.length > 0); Boolean(tenderForNav) && !isDocumentsLoading && documents.length > 0;
const tenderNavItems = useMemo( const tenderNavItems = useMemo(
() => () =>