'use strict'; import { global_unavailableDates, getDayOfWeek, getPostId, insertRating, getNonce, getNameLanguage, filterResponse, updateStorage, fixedDate, fixedMonth, useGeocode } from './functions.js'; const {__, _x, _n, _nx} = wp.i18n; const $ = jQuery; // We'll define these globally just in case your code references them: let $dateLoad = $('.aside-date-loaded'); let $asideDate = $('.aside-date'); /** * 1) FETCH AVAILABILITY */ const getAvailability = async (id) => { console.log('[DEBUG] getAvailability() called with ID:', id); try { let availabilityResponse = await axios({ method: 'POST', data: new URLSearchParams({ action: 'availability_product', id: id, targetCurrency: localStorage.getItem('viatorCurrency') ?? viatorParameters?.default_currency, }), url: '/wp-admin/admin-ajax.php', }); let availability = filterResponse(availabilityResponse); console.log('[DEBUG] Availability raw data:', availability); // Hide the date loader, show the calendar date $dateLoad.addClass('d-none'); $asideDate.removeClass('d-none'); return { items: availability.bookableItems, originalCurrency: availability.currency, rates: availability.rates }; } catch (err) { console.error('[ERROR] getAvailability failed:', err); return { items: [], originalCurrency: null, rates: [] }; } }; /** * 2) INSERT PRODUCT INTO DOM */ const insertProduct = ($wrapper, product) => { console.log('[DEBUG] insertProduct() called. Product code =', product?.productCode, product); // 2.1) Find the .excursion__content inside $wrapper const $cntBlock = $wrapper.querySelector('.excursion__content'); if (!$cntBlock) { console.warn('[DEBUG] No .excursion__content found inside the wrapper, cannot insert product details.'); return; } // For debugging, let’s empty the .excursion__content first $cntBlock.innerHTML = ''; /******************** * PRODUCT IMAGES ********************/ const productSlider = () => { if (!product?.images?.length) { console.log('[DEBUG] No images found in product'); return; } console.log('[DEBUG] Found images, constructing slider...'); // Just a small example: // You’d normally have wp.template('productSlider') or something. const $sliderBox = document.createElement('div'); $sliderBox.classList.add('excursion__slider'); let html = `
`; for (const imgObj of product.images) { // We pick the first variant or largest if (!imgObj.variants?.length) continue; let bestVariant = imgObj.variants[0]; html += `
${imgObj.caption ?? ''}
`; } html += `
`; // Insert $sliderBox.innerHTML = html; $cntBlock.appendChild($sliderBox); // If you have existing slick() init calls: // jQuery('.excursion__slider-main').slick({ ... }); }; /******************** * PRODUCT TAGS ********************/ const tags = () => { // If product.tags is an array of IDs or strings, we can just do: if (!product?.tags?.length) { console.log('[DEBUG] No tags found in product'); return; } console.log('[DEBUG] Found tags:', product.tags); const $tagsWrap = document.createElement('div'); $tagsWrap.classList.add('d-flex', 'tags'); product.tags.forEach((tg) => { let $tgItem = document.createElement('div'); $tgItem.classList.add('tags__item'); $tgItem.textContent = tg; // or do your localized name $tagsWrap.appendChild($tgItem); }); $cntBlock.appendChild($tagsWrap); }; /******************** * PRODUCT TITLE ********************/ const title = () => { const $h2 = document.createElement('h2'); $h2.classList.add('excursion__title'); $h2.textContent = product?.title || ''; $cntBlock.appendChild($h2); // Also update document.title = product?.title || document.title; }; /******************** * INFO BOX: * - Reviews * - Duration * - Free cancellation * - Pickup offered * - Ticket type ********************/ const buildInfoBox = () => { let $infoBox = document.createElement('div'); $infoBox.classList.add('row','align-items-center','excursion__info'); // 1) REVIEWS if (product?.reviews) { let $colLeft = document.createElement('div'); $colLeft.classList.add('col-xl-5','col-sm-6','excursion__info-wrap','reviews-block'); let $rev = document.createElement('div'); $rev.classList.add('review'); // Star rating insertRating($rev, Math.round(product.reviews.combinedAverageRating)); // Number of reviews let $txt = document.createElement('span'); $txt.classList.add('review__text'); $txt.textContent = product.reviews.totalReviews + ' ' + __('reviews','viator'); $rev.appendChild($txt); $colLeft.appendChild($rev); $infoBox.appendChild($colLeft); } // 2) DURATION // we check both fixedDurationInMinutes or variableDurationFrom/toMinutes const getDurationInHours = () => { if (!product?.itinerary?.duration) return null; if (product.itinerary.duration.fixedDurationInMinutes) { return (product.itinerary.duration.fixedDurationInMinutes / 60).toFixed(2); } else if (product.itinerary.duration.variableDurationToMinutes) { return (product.itinerary.duration.variableDurationToMinutes / 60).toFixed(2); } return null; }; let durationHours = getDurationInHours(); if (durationHours) { let $colDur = document.createElement('div'); $colDur.classList.add('col-xl-5','col-sm-6','excursion__info-wrap'); let $durBlock = document.createElement('div'); $durBlock.classList.add('d-flex','align-items-center','excursion__info-item','excursion-grey'); let $span = document.createElement('span'); $span.classList.add('excursion__duration-icon'); let $img = document.createElement('img'); $img.src = `${window.location.origin}/wp-content/plugins/viator/assets/img/icons/icon-clock.svg`; $img.alt = 'clock'; $span.appendChild($img); $durBlock.appendChild($span); $durBlock.appendChild(document.createTextNode(` ${durationHours} ${__('hours','viator')}`)); $colDur.appendChild($durBlock); $infoBox.appendChild($colDur); } // 3) FREE CANCELLATION if (product?.cancellationPolicy?.type !== 'ALL_SALES_FINAL') { let $colCancel = document.createElement('div'); $colCancel.classList.add('col-xl-5','col-sm-6','excursion__info-wrap'); let $cancelBlock = document.createElement('div'); $cancelBlock.classList.add('d-flex','align-items-center','excursion__info-item','excursion-green'); $cancelBlock.setAttribute('title', __('Up to 24 hours in advance.', 'viator')); let $spanC = document.createElement('span'); $spanC.classList.add('excursion__cancellation-icon'); let $imgC = document.createElement('img'); $imgC.src = `${window.location.origin}/wp-content/plugins/viator/assets/img/icons/icon-check.svg`; $imgC.alt = 'check'; $spanC.appendChild($imgC); $cancelBlock.appendChild($spanC); $cancelBlock.appendChild(document.createTextNode(__('Free cancellation','viator'))); $colCancel.appendChild($cancelBlock); $infoBox.appendChild($colCancel); } // 4) PICKUP if (product?.logistics?.travelerPickup?.pickupOptionType === 'PICKUP_EVERYONE' || product?.logistics?.travelerPickup?.pickupOptionType === 'PICKUP_AND_MEET_AT_START_POINT') { let $colPick = document.createElement('div'); $colPick.classList.add('col-xl-5','col-sm-6','excursion__info-wrap'); let $pickup = document.createElement('div'); $pickup.classList.add('d-flex','align-items-center','excursion__info-item','excursion-green'); let $spanP = document.createElement('span'); $spanP.classList.add('excursion__cancellation-icon'); let $imgP = document.createElement('img'); $imgP.src = `${window.location.origin}/wp-content/plugins/viator/assets/img/icons/icon-check.svg`; $imgP.alt = 'check'; $spanP.appendChild($imgP); $pickup.appendChild($spanP); $pickup.appendChild(document.createTextNode(__('Pickup offered','viator'))); $colPick.appendChild($pickup); $infoBox.appendChild($colPick); } // 5) TICKET TYPE if (product?.ticketInfo?.ticketTypes?.length) { let $typeArr = product.ticketInfo.ticketTypes; let ticketTxt = __('Mobile ticket','viator'); if ($typeArr.length > 1) { ticketTxt = __('Mobile and paper ticket','viator'); } else if ($typeArr[0] === 'PAPER') { ticketTxt = __('Paper ticket','viator'); } let $colTick = document.createElement('div'); $colTick.classList.add('col-xl-5','col-sm-6','excursion__info-wrap'); let $tickBlock = document.createElement('div'); $tickBlock.classList.add('d-flex','align-items-center','excursion__info-item','excursion-green'); let $spanT = document.createElement('span'); $spanT.classList.add('excursion__cancellation-icon'); let $imgT = document.createElement('img'); $imgT.src = `${window.location.origin}/wp-content/plugins/viator/assets/img/icons/icon-check.svg`; $imgT.alt = 'check'; $spanT.appendChild($imgT); $tickBlock.appendChild($spanT); $tickBlock.appendChild(document.createTextNode(ticketTxt)); $colTick.appendChild($tickBlock); $infoBox.appendChild($colTick); } $cntBlock.appendChild($infoBox); }; /******************** * DESCRIPTION, DETAILS, CANCELLATIONS, ETC. ********************/ const buildFaqContent = () => { let $descContainer = document.createElement('div'); $descContainer.classList.add('excursion__description'); // 1) Description if (product?.description) { let $descItem = document.createElement('div'); $descItem.classList.add('faq__item','active','description'); let $descHeader = document.createElement('div'); $descHeader.classList.add('faq__item-header'); $descHeader.textContent = __('Description','viator'); let $descBody = document.createElement('div'); $descBody.classList.add('faq__item-body','test'); let $p = document.createElement('p'); $p.innerHTML = product.description.replace(/\n/g, '<br>'); $descBody.appendChild($p); $descItem.appendChild($descHeader); $descItem.appendChild($descBody); $descContainer.appendChild($descItem); } // 2) Details: inclusions, exclusions, additionalInfo if (product?.inclusions?.length || product?.exclusions?.length || product?.additionalInfo?.length) { let $detailItem = document.createElement('div'); $detailItem.classList.add('faq__item'); let $detailHeader = document.createElement('div'); $detailHeader.classList.add('faq__item-header'); $detailHeader.textContent = __('Details','viator'); let $detailBody = document.createElement('div'); $detailBody.classList.add('faq__item-body'); // Inclusions if (product?.inclusions?.length) { let $incTitle = document.createElement('h5'); $incTitle.textContent = __('What\'s Included','viator'); let $ulInc = document.createElement('ul'); $ulInc.classList.add('faq__item-list','faq__details'); product.inclusions.forEach((inc) => { let $li = document.createElement('li'); $li.classList.add('faq__detail','faq__detail-pass'); $li.textContent = inc?.otherDescription || inc?.description || ''; $ulInc.appendChild($li); }); $detailBody.appendChild($incTitle); $detailBody.appendChild($ulInc); } // Exclusions if (product?.exclusions?.length) { product.exclusions.forEach((exc) => { let $li = document.createElement('li'); $li.classList.add('faq__detail','faq__detail-off'); $li.textContent = exc?.otherDescription || exc?.description || ''; // We can append to the same UL or create a new <ul> }); } // Additional info if (product?.additionalInfo?.length) { let $infoTitle = document.createElement('h5'); $infoTitle.textContent = __('Additional Info','viator'); let $ulInfo = document.createElement('ul'); $ulInfo.classList.add('faq__item-list'); product.additionalInfo.forEach((ad) => { let $li = document.createElement('li'); $li.textContent = ad.description; $ulInfo.appendChild($li); }); $detailBody.appendChild($infoTitle); $detailBody.appendChild($ulInfo); // Operated by if (product?.supplier?.name) { let $pOp = document.createElement('p'); $pOp.innerHTML = __('Operated by','viator') + ` <span class="faq__item-supplier">${product.supplier.name}</span>`; $detailBody.appendChild($pOp); } } $detailItem.appendChild($detailHeader); $detailItem.appendChild($detailBody); $descContainer.appendChild($detailItem); } // 3) Cancellations if (product?.cancellationPolicy?.description || product?.cancellationPolicy?.refundEligibility) { let $cancelItem = document.createElement('div'); $cancelItem.classList.add('faq__item'); let $cancelHeader = document.createElement('div'); $cancelHeader.classList.add('faq__item-header'); $cancelHeader.textContent = __('Cancellations','viator'); let $cancelBody = document.createElement('div'); $cancelBody.classList.add('faq__item-body'); let $cancelUl = document.createElement('ul'); $cancelUl.classList.add('faq__item-list','faq__cancel-list'); if (product.cancellationPolicy.description) { // it might have <br> or not let descLines = product.cancellationPolicy.description.split('<br>'); descLines.forEach(dl => { let $li = document.createElement('li'); $li.textContent = dl.trim(); $cancelUl.appendChild($li); }); } if (product.cancellationPolicy.refundEligibility) { // Add lines for partial refunds etc } // etc. your existing logic $cancelBody.appendChild($cancelUl); $cancelItem.appendChild($cancelHeader); $cancelItem.appendChild($cancelBody); $descContainer.appendChild($cancelItem); } // 4) Meeting point, schedule, etc. // Similar expansions… // Finally, append to $cntBlock $cntBlock.appendChild($descContainer); }; // ACTUAL CALLS productSlider(); tags(); title(); buildInfoBox(); buildFaqContent(); }; /** * 3) MAIN getProduct() */ const getProduct = async () => { console.log('[DEBUG] getProduct() started'); const $contentWrap = $('.container .excursion__wrap'); if (!$contentWrap.length) { console.error('[DEBUG] No .excursion__wrap found inside .container. Abort.'); return; } // Build request let requestOptions = { action: 'get_product', type: 'viator', destName: viatorSingleObj?.dest_name ?? '', targetCurrency: localStorage.getItem('viatorCurrency') ?? viatorParameters?.default_currency, id: viatorSingleObj?.post_id ?? getPostId(), }; if (!requestOptions.id) { console.warn('[DEBUG] No product ID found, maybe getPostId() is missing?'); $contentWrap.html(__('Error', 'viator')); return; } // Possibly handle get_tags / get_destinations if (!localStorage.getItem('viatorTags') || Date.now() > localStorage.getItem('viatorTagsLastUp')) { requestOptions['get_tags'] = true; } if (!localStorage.getItem('viatorDest') || Date.now() > localStorage.getItem('viatorDestLastUp')) { requestOptions['get_destinations'] = true; } let viatorLang = sessionStorage.getItem('viatorLang'); if (viatorLang) { requestOptions['language'] = viatorLang; } console.log('[DEBUG] Sending get_product request:', requestOptions); // 1) get the main product info let response; try { response = await axios({ method: 'POST', data: new URLSearchParams(requestOptions), url: '/wp-admin/admin-ajax.php', }); } catch (err) { console.error('[ERROR] get_product request failed:', err); return; } console.log('[DEBUG] Raw product response:', response); // filterResponse let productData = filterResponse(response); console.log('[DEBUG] productData after filterResponse:', productData); // 2) get availability let availabilityData = await getAvailability(requestOptions.id); // 3) Merge them let finalResult = { ...productData, ...availabilityData }; console.log('[DEBUG] finalResult combined:', finalResult); // 4) Insert into DOM insertProduct($contentWrap[0], finalResult); }; // Export export { getProduct, insertProduct };