feat(tender-details): add lots section and enhance tender details functionality

- Introduced a new "Lots" section in the tender details page to display lot-specific information, improving data organization and visibility.
- Updated the tender navigation to conditionally include the "Lots" section based on its availability.
- Enhanced the tender display utilities with functions to determine the visibility of various sections, including lots, description, and translation.
- Refactored components to utilize the new utilities for better conditional rendering and improved user experience.
- Improved type definitions for tender details to ensure better type safety and clarity.
This commit is contained in:
AmirReza Jamali
2026-05-13 09:44:43 +03:30
parent 2178f25af5
commit 37e2744420
13 changed files with 850 additions and 137 deletions
+106 -32
View File
@@ -1,37 +1,111 @@
import { z } from "zod";
export const TenderSchema = z.object({
id: z.string(),
buyer_organization: z.object({
name: z.string(),
}),
application_deadline: z.number(),
country_code: z.string(),
currency: z.string(),
description: z.string(),
duration: z.string(),
duration_unit: z.string(),
estimated_value: z.number(),
notice_publication_id: z.string(),
/** Related TED notice publication IDs (e.g. modifications, same procedure). */
related_notice_publication_ids: z.array(z.string()).optional(),
/** TED form / notice type code (e.g. can-modif). */
notice_type_code: z.string().optional(),
/** Primary CPV classification code. */
main_classification: z.string().optional(),
procedure_code: z.string(),
procurement_type_code: z.string(),
publication_date: z.number(),
status: z.string(),
submission_deadline: z.number(),
submission_url: z.string(),
tender_deadline: z.number(),
created_at: z.number().optional(),
tender_id: z.string(),
title: z.string(),
language: z.string().optional(),
overall_summary: z.string().optional(),
});
/** TED / eForms notice address block */
export const TenderAddressSchema = z
.object({
street_name: z.string().optional(),
city_name: z.string().optional(),
postal_zone: z.string().optional(),
country_subentity_code: z.string().optional(),
country_code: z.string().optional(),
})
.passthrough();
export type TTenderAddress = z.infer<typeof TenderAddressSchema>;
export const BuyerOrganizationSchema = z
.object({
name: z.string().default(""),
company_id: z.string().optional(),
website_uri: z.string().optional(),
contact_name: z.string().optional(),
contact_telephone: z.string().optional(),
contact_email: z.string().optional(),
address: TenderAddressSchema.optional(),
})
.passthrough();
export type TBuyerOrganization = z.infer<typeof BuyerOrganizationSchema>;
export const WinningTendererSchema = z
.object({
name: z.string().optional(),
address: TenderAddressSchema.optional(),
})
.passthrough();
export type TWinningTenderer = z.infer<typeof WinningTendererSchema>;
export const TenderLotSchema = z
.object({
lot_id: z.string().optional(),
title: z.string().optional(),
description: z.string().optional(),
main_nature_of_contract: z.string().optional(),
main_classification: z.string().optional(),
main_classification_display: z.string().optional(),
estimated_value: z.number().optional(),
currency: z.string().optional(),
})
.passthrough();
export type TTenderLot = z.infer<typeof TenderLotSchema>;
export const TenderSchema = z
.object({
id: z.string(),
notice_publication_id: z.string(),
/** Related TED notice publication IDs (e.g. modifications, same procedure). */
related_notice_publication_ids: z.array(z.string()).optional(),
contract_folder_id: z.string().optional(),
procurement_project_id: z.string().optional(),
contract_notice_id: z.string().optional(),
notice_type_code: z.string().optional(),
form_type: z.string().optional(),
notice_sub_type_code: z.string().optional(),
notice_identifier: z.string().optional(),
notice_version: z.string().optional(),
notice_language_code: z.string().optional(),
gazette_id: z.string().optional(),
notice_dispatch_at: z.number().optional(),
notice_dispatch_date_with_timezone: z.string().optional(),
issue_date: z.number().optional(),
issue_time: z.number().optional(),
title: z.string(),
description: z.string(),
procurement_type_code: z.string(),
procedure_code: z.string(),
main_classification: z.string().optional(),
main_classification_display: z.string().optional(),
estimated_value: z.number(),
currency: z.string(),
estimated_value_vat_basis: z.string().optional(),
duration: z.string().optional(),
duration_unit: z.string().optional(),
publication_date: z.number(),
tender_deadline: z.number(),
submission_deadline: z.number(),
application_deadline: z.number(),
submission_url: z.string(),
country_code: z.string(),
region_code: z.string().optional(),
document_url: z.string().optional(),
tender_url: z.string().optional(),
buyer_profile_url: z.string().optional(),
buyer_organization: BuyerOrganizationSchema,
winning_tenderer: WinningTendererSchema.optional(),
lots: z.array(TenderLotSchema).optional(),
award_date: z.number().optional(),
status: z.string(),
source: z.string().optional(),
tender_id: z.string(),
project_name: z.string().optional(),
created_at: z.number().optional(),
/** Resolved / UI language (may come from translation pipeline). */
language: z.string().optional(),
overall_summary: z.string().optional(),
})
.passthrough();
export const TenderDocumentsResponseSchema = z.object({
filename: z.string(),