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:
@@ -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";
|
||||||
|
|||||||
@@ -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(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
Reference in New Issue
Block a user