import { TripCardApiData, ApiCruiseResult } from './types'; const decodeHtmlEntities = (text: string): string => { const textarea = document.createElement('textarea'); textarea.innerHTML = text; return textarea.value; }; export const transformApiResult = ( result: ApiCruiseResult ): TripCardApiData => { try { const formatPrice = (price: number | string): string => { if (price === 0 || price === 'Enquire now') { return 'Enquire now'; } return typeof price === 'string' ? price : `£${price.toLocaleString('en-GB')}`; }; const formatOriginalPrice = ( wasPrice: number | string ): string | undefined => { if (!wasPrice || wasPrice === 0 || wasPrice === 'Enquire now') { return undefined; } return typeof wasPrice === 'string' ? wasPrice : `£${wasPrice.toLocaleString('en-GB')}`; }; const findCheapestCabinType = (): keyof typeof result.was_prices | null => { if (!result.was_prices || !result.prices) { return null; } const cheapestPrice = result.prices.cheapest_price; const cabinTypes: Array = [ 'inside', 'outside', 'balcony', 'suite' ]; for (const cabinType of cabinTypes) { const cabinPrice = result.prices[cabinType]; if (Number(cabinPrice) === Number(cheapestPrice)) { return cabinType; } } return null; }; const getOriginalPrice = (): string | undefined => { if (!result.was_prices) { return undefined; } const cheapestCabinType = findCheapestCabinType(); if (cheapestCabinType && result.was_prices[cheapestCabinType]) { return formatOriginalPrice(result.was_prices[cheapestCabinType]); } return undefined; }; return { cruise_id: String(result.cruise_id), image: (result.ship && result.ship.image) || '', offer_label: result.campaigns && result.campaigns.length > 0 ? result.campaigns[0] : undefined, headline: decodeHtmlEntities(result.cruise_title || ''), date: (result.extras && result.extras.date) || result.departs, duration: (result.extras && result.extras.duration) || (result.duration ? result.duration.toString() : ''), destination: (result.extras && result.extras.destination) || '', ship: (result.ship && result.ship.name) || '', original_price: getOriginalPrice(), price: formatPrice( (result.prices && result.prices.cheapest_price) !== undefined ? result.prices.cheapest_price : 0 ), logo: (result.cruise_line && result.cruise_line.logo) || '', logo_url: (result.cruise_line && result.cruise_line.link) || '', logo_name: (result.cruise_line && result.cruise_line.name) || '', cta_url: result.link || '', cta_text: 'View Itinerary' }; } catch (error) { throw new Error(`Failed to transform cruise data: ${error}`); } }; export const decodeIds = (encoded: string): number[] => { try { const decoded = atob(encoded); const data = JSON.parse(decoded); if (Array.isArray(data)) { return data; } return []; } catch (e) { // Failed to decode trip card IDs return []; } }; export const defaultTripCardImgHeight = { $heightXs: '153px', $heightSm: '213px', $heightMd: '213px', $heightLg: '210px', $heightXl: '245px' }; const isValidOffer = (result: ApiCruiseResult): boolean => { if (result.departs) { const departDate = new Date(result.departs); if (isNaN(departDate.getTime())) { return false; } departDate.setHours(0, 0, 0, 0); const today = new Date(); today.setHours(0, 0, 0, 0); if (departDate < today) { return false; } } return true; }; export const fetchCruiseCards = async ( cruiseIds: number[], hostName?: string ): Promise => { const formData = new FormData(); formData.append('action', 'results'); formData.append('cruise_ids', cruiseIds.join(',')); const url = hostName && hostName.includes('thetimes.com') && !hostName.includes('-thetimes') ? 'https://www.thetimes.com/holidays/wp-admin/admin-ajax.php' : 'https://www.staging-thetimes.com/holidays/wp-admin/admin-ajax.php'; const response = await fetch(url, { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.results && Array.isArray(data.results)) { const validResults = data.results.filter((result: ApiCruiseResult) => isValidOffer(result) ); const transformedCards: TripCardApiData[] = validResults.map( (result: ApiCruiseResult) => transformApiResult(result) ); return cruiseIds .map(id => transformedCards.find(card => card.cruise_id === String(id))) .filter(Boolean) as TripCardApiData[]; } throw new Error('Invalid API response format'); };