{#
Carte croisière (chargée en lazy-loading via /api/cruise/card/list_card/{id}).
Contexte attendu :
- cruise : entité Cruise (obligatoire)
- content : { isFloa: bool } (optionnel)
#}
{% import '_snippets/components/macros/cruise.html.twig' as cruiseMacros %}
{% import '_snippets/components/macros/date.html.twig' as dateMacros %}
{% macro cruise_image_tags(cruise) %}
<div class="cruise-image-tags">
{% for tagCruise in cruise.tagCruises %}
{% set tag = tagCruise.tag %}
{% if tag %}
<div class="tag br1 text-sm-md" style="background-color:{{ tag.colorFill|default('transparent') }}; border:{{ tag.colorStroke ? '1px solid ' ~ tag.colorStroke|default('transparent') : 'none' }}; color:{{ tag.colorFont|default('#000000') }}">
{{ getLucidIcon(tag.icon|default('')) }}
{{ tag.title|default('Tag')|raw }}
</div>
{% endif %}
{% endfor %}
</div>
{% endmacro %}
{% import _self as listCard %}
{% if cruise is defined and cruise is not null %}
<div class="cruise animation3 br3" data-cruise-id="{{ cruise.id }}" data-index="{{ loop.index0|default(0) }}">
<div class="top-section row">
{# ─────────── Colonne images + modales ─────────── #}
<div class="images col-lg-3 col-md-4 col-sm-12">
<div class="thumbnails br2">
{% set shipMedia = crea_media_get_media(['main'], cruise.ship, null, { width: 400 }) %}
<img
src="{{ shipMedia ? shipMedia.cdnUrl : '' }}"
alt="{{ cruise.ship.name|default('') }}"
loading="lazy"
decoding="async"
>
{{ listCard.cruise_image_tags(cruise) }}
</div>
<div class="carousel-wrapper">
<div class="carousel br2">
{{ listCard.cruise_image_tags(cruise) }}
<div class="arrow left-arrow">
{{ getLucidIcon('chevron-left') }}
</div>
<div class="elements">
{% for media in getCruiseStopoversMediaFiles(cruise) %}
{% set cdnMedia = crea_media_get_cdn_media(media, { width: 650 }) %}
<img class="carousel-image" src="{{ cdnMedia.cdnUrl }}" alt="">
{% endfor %}
{# {% for i in 1 .. random(3, 10) %}#}
{# <img class="carousel-image" src="{{ '/img/photos-temporaires/Photos-escales/' ~ random(1, 17) ~ '.jpg' }}" alt="">#}
{# {% endfor %}#}
</div>
<div class="arrow right-arrow">
{{ getLucidIcon('chevron-right') }}
</div>
<div class="carousel-bottom-icons">
<div class="itinerary-icon-wrapper">
<div class="itinerary-icon" data-modal-target="itinerary-modal-{{ cruise.id }}">
{{ getLucidIcon('map-pin') }}
</div>
</div>
<div class="map-icon-wrapper">
{# <div class="map-icon" data-modal-target="map-modal-{{ cruise.id }}">
{{ cruiseMacros.leaflet_map(cruise, { staticMap: true, disableZoom: true }) }}
</div> #}
<div class="map-icon" data-modal-target="map-modal-{{ cruise.id }}">
{{ getLucidIcon('map') }}
</div>
</div>
</div>
</div>
{# ─────────── Modale Itinéraire ─────────── #}
<div class="itinerary-modal" id="itinerary-modal-{{ cruise.id }}">
<div class="h4">{{ 'list.cruise_card.discover_itinerary'|trans }}</div>
<div class="itinerary-table-wrapper">
<table class="itinerary-table">
<thead>
<tr>
<th>{{ 'ddd.summary.date'|trans }}</th>
<th>{{ 'details.itinerary.stopover'|trans }}</th>
<th>{{ 'ddd.summary.arrival'|trans }}</th>
<th>{{ 'ddd.summary.departure'|trans }}</th>
</tr>
</thead>
<tbody>
{% for segment in cruise.itinerary.itinerarySegments|default([]) %}
{% set isSea = segment.segmentType.value == 'day_at_sea' %}
<tr>
<td>{{ getSegmentDateForCruise(cruise, segment)|date('d/m/Y') }}</td>
<td>{{ isSea ? 'Journée en mer' : segment.stopover.name|default('') }}</td>
<td>{{ isSea ? '-' : (segment.arrivalTime is not null ? segment.arrivalTime|date('H:i') : '') }}</td>
<td>{{ isSea ? '-' : (segment.departureTime is not null ? segment.departureTime|date('H:i') : '') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{# ─────────── Modale Carte (Leaflet injecté à l'ouverture) ─────────── #}
{{ cruiseMacros.map_modal(cruise, 'map-modal-' ~ cruise.id) }}
{# <div class="map-modal" id="map-modal-{{ cruise.id }}">
{{ cruiseMacros.leaflet_map(cruise) }}
</div> #}
</div>
</div>
{# ─────────── Colonne contenu ─────────── #}
<div class="content col-lg-6 col-md-4 col-sm-12">
<div class="company-name text-xs-rg color-grey-text">{{ cruise.ship.company.name|default('') }}</div>
<div class="title">
{% set cruiseName = getCruiseName(cruise)|default('') %}
{% if cruiseName is empty %}{% set cruiseName = 'snippets.cruise.title_fallback'|trans %}{% endif %}
{% set MAX_TITLE_LENGTH = 80 %}
{% set isTitleTruncated = cruiseName|length > MAX_TITLE_LENGTH %}
<h2 class="text-lg-bd">
{{ isTitleTruncated ? (cruiseName|slice(0, MAX_TITLE_LENGTH)|trim ~ '…') : cruiseName }}
{% if isTitleTruncated %}
<span class="cruise-title-info" tabindex="0" role="button" aria-label="{{ cruiseName }}">
{{ getLucidIcon('info') }}
<div class="popup popup--cruise-name">
<div class="popup__content">
<div class="popup__title text-sm-md">{{ cruiseName }}</div>
</div>
</div>
</span>
{% endif %}
</h2>
{% if cruise.ship.company is defined %}
<div class="logo_company br1">
<img
src="{{ crea_media_get_media(['logo'], cruise.ship.company).cdnUrl|default('/img/placeholder-company.svg') }}"
alt="{{ 'common.company'|trans }} {{ cruise.ship.company.name|default('Compagnie') }}"
loading="lazy"
decoding="async"
>
</div>
{% endif %}
</div>
<div class="practices_informations text-sm-rg color-grey-text">
<span class="text-sm-bd">
{% if cruise.itinerary.secondaryDestinations is iterable %}
{{ cruise.itinerary.secondaryDestinations.first.secondaryDestination.name|default('') }}
{% else %}
{{ cruise.itinerary.destination|default('') }}
{% endif %}
</span>
{{ 'list.cruise_card.departure_from'|trans }}
<span class="text-sm-bd">{{ get_departure_port(cruise.itinerary).name|default('') }}</span>
<br>
<span class="text-sm-bd">{{ cruise.itinerary.numberOfDays|default(0) }}</span>
{{ 'list.cruise_card.days_on_ship'|trans }}
<span class="text-sm-bd">{{ cruise.ship.name|default('list.placeholder.ship_fallback'|trans) }}</span>
<br>
{{ 'details.main_information.from'|trans|lower }}
<span class="text-sm-bd">{{ cruise.departureDate|date('d/m/Y') }}</span>
{{ 'details.main_information.to'|trans }}
<span class="text-sm-bd">{{ cruise.arrivalDate|date('d/m/Y') }}</span>
</div>
{% if getAdvantagesForCruise(cruise) is iterable %}
<div class="benefits-block">
<div class="subtitle-category">{{ 'details.main_information.onboard_benefits'|trans }}</div>
<div class="benefits br1">
{% set totalBenefits = getAdvantagesForCruise(cruise)|length %}
{% for advantage in getAdvantagesForCruise(cruise)|slice(0, 6) %}
<div class="benefit text-xs-rg">
<span class="icon">{{ getLucidIcon(advantage.icon|default('star'), {'width': '14px', 'height': '14px'}) }}</span>
<span class="text">{{ advantage.name|default(advantage.title|default('')) }}</span>
{% include 'pages/_includes/partials/_benefit_popup.html.twig' with {'benefit': advantage} %}
</div>
{% endfor %}
{% if totalBenefits > 6 %}
<div class="more-benefits">
{% for advantage in getAdvantagesForCruise(cruise)|slice(6) %}
<div class="benefit text-xs-rg">
<span class="icon">{{ getLucidIcon(advantage.icon|default('star'), {'width': '14px', 'height': '14px'}) }}</span>
<span class="text">{{ advantage.name|default(advantage.title|default('')) }}</span>
{% include 'pages/_includes/partials/_benefit_popup.html.twig' with {'benefit': advantage} %}
</div>
{% endfor %}
</div>
<button
type="button"
class="toggle-benefits benefit text-xs-rg"
aria-expanded="false"
data-label-more="+{{ totalBenefits - 6 }}"
data-label-less="{{ 'common.see_less'|trans|e('html_attr') }}"
>+{{ totalBenefits - 6 }}</button>
{% endif %}
</div>
</div>
{% endif %}
{% set cruiseOffers = getCruiseSpecialOffers(cruise) %}
{% if cruiseOffers is not empty %}
<div class="special_offers-block">
<div class="subtitle-category">{{ 'list.cruise_card.promotional_offers'|trans }}</div>
<div class="special_offers">
{% for offer in cruiseOffers %}
{% include 'pages/_includes/partials/_offer_with_popup.html.twig' with { offer: offer } only %}
{% endfor %}
</div>
</div>
{% endif %}
{% set otherDepartures = get_active_cruises(cruise.itinerary) %}
{% if otherDepartures is not empty %}
<div class="other-departures-block">
<div class="other-departures">
<div class="subtitle-category">{{ 'details.cabin_choice.other_departures'|trans }}</div>
<div class="departures-carousel-wrapper">
<div class="arrow left-arrow color-primary-dark br1">
<div class="icon">{{ getLucidIcon('chevron-left') }}</div>
</div>
<div class="departures-carousel-container">
<div class="departures-carousel">
{% for subCruise in otherDepartures %}
<a href="{{ getUrlWithQueryParams(constant('App\\Controller\\Website\\DetailController::BASE_PATH'),{'croisiere': subCruise.id}) }}" class="departure-item br1">
<div class="date text-sm-md color-grey-text">
{% if subCruise.departureDate is defined and subCruise.departureDate is not empty %}
{{ subCruise.departureDate|date('d') }}
{{ dateMacros.short_month_fr(subCruise.departureDate|date('n')|number_format(0, '', '')) ?: subCruise.departureDate|date('M') }}
{{ subCruise.departureDate|date('Y') }}
{% else %}
{{ subCruise.date }}
{% endif %}
</div>
<div class="price text-2xs-bd color-grey">
{{ 'common.from'|trans|lower }}
{# {{ priceLabel }} #}
{{ getCruiseBestPrice(subCruise)|number_format(0, ',', ' ') }} €
</div>
</a>
{% endfor %}
</div>
</div>
<div class="arrow right-arrow color-primary-dark br1">
<div class="icon">{{ getLucidIcon('chevron-right') }}</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
{# ─────────── Colonne aside (partner card + CTA) ─────────── #}
<div class="aside desktop br2 col-lg-3 col-md-4 col-sm-12">
{% set partner = getCurrentPartner() %}
{% set company = cruise.ship.company|default(null) %}
{% set discountPercent = 0 %}
{% if partner and company %}
{% set discount = get_partner_company_discount(company) %}
{% set discountPercent = discount ? discount.rate|default(0) : 0 %}
{% endif %}
{% if partner and discountPercent > 0 and partner.name|default('') is not empty %}
<div class="partner-card br2 shimmered">
<div class="icon">
{{ getLucidIcon('star') }}
</div>
<div class="tags">
<div class="tag text-xs-bd color-price-tags-primary br1 ">{{ 'details.main_information.partner_tag'|trans }}</div>
<div class="tag text-xs-bd br1">{{ partner.name }}</div>
</div>
<div class="discount color-card-background">
<div class="reduction-value text-4xl-sebd">-{{ discountPercent }}%</div>
<div class="text text-sm-bd">{{ 'details.main_information.exclusive_discount'|trans }}</div>
</div>
<div class=" text text-xs-md br1 color-card-background">
{{ getLucidIcon('check') }}
{{ 'details.main_information.partner_discount_applied'|trans }}
</div>
</div>
{% endif %}
<div class="floa_and_call_to_action">
<div class="price">
<div class="text-sm-md color-grey-text">
{{ cruise.bestPrice.cabinCategory.cabinSubType.name|default('') ?: 'ddd.step.cabin'|trans }}
{# {{ 'ddd.step.cabin'|trans }} #}
{{ 'common.from'|trans|lower }}
</div>
<a href="">
<div class="base br1">
<span class="price">{{ getCruiseBestPrice(cruise)|number_format(0, ',', ' ') }}
€</span>
<span class="text-sm-lt color-secondary-dark">{{ 'common.per_person_short'|trans }}</span>
</div>
</a>
<div class="service-fees">
<div class="label text-3xs-md color-grey-text">
{{ 'list.cruise_card.service_fees_notice'|trans }}
{{ getLucidIcon('info') }}
</div>
<div class="wrapper"></div>
</div>
</div>
<div class="floa text-sm-md color-grey-text">
{% if content.isFloa|default(false) %}
{{ 'list.cruise_card.floa_compatible'|trans }}
<img
src="https://www.croisiland.com/_imgs/floa/4X-bleu.svg?1710340929"
alt="{{ 'list.placeholder.floa_alt'|trans }}"
loading="lazy"
decoding="async"
>
{% endif %}
</div>
<a href="{{ getUrlWithQueryParams(constant('App\\Controller\\Website\\DetailController::BASE_PATH'),{'croisiere': cruise.id}) }}" class="call_to_action btn medium style1 bg-w w-100 text-md-sbd">{{ 'common.see_offers'|trans }}</a>
</div>
</div>
</div>
</div>
{% endif %}