feat: render price table rows separately in regard of availability
closes #8698te15z
This commit is contained in:
@@ -191,7 +191,7 @@ class ProductRepository extends AbstractRepository
|
||||
;
|
||||
}
|
||||
|
||||
public function getPricetable(Product $product, Hotel $hotel, array $filterSettings, Date $date = null, bool $sortByRoom = true): array
|
||||
public function getPricetable(Product $product, Hotel $hotel, array $filterSettings, Date $date = null): array
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
@@ -207,18 +207,11 @@ class ProductRepository extends AbstractRepository
|
||||
)
|
||||
->from('tx_epproducts_domain_model_date', 'date')
|
||||
->innerJoin('date', 'tx_epproducts_domain_model_room', 'room', 'date.uid = room.date')
|
||||
->orderBy('room.available', 'DESC')
|
||||
->orderBy('room.pax', 'ASC')
|
||||
->addOrderBy('room.price', 'ASC')
|
||||
->addOrderBy('room.available', 'DESC')
|
||||
;
|
||||
|
||||
if (true === $sortByRoom) {
|
||||
$qb->addOrderBy('room.name', 'ASC');
|
||||
} else {
|
||||
$qb
|
||||
->addOrderBy('room.pax', 'ASC')
|
||||
->addOrderBy('room.price', 'ASC')
|
||||
;
|
||||
}
|
||||
|
||||
$qb
|
||||
->where('date.date_start >= NOW()')
|
||||
->andWhere($qb->expr()->eq(
|
||||
|
||||
@@ -109,7 +109,6 @@ class DateService implements SingletonInterface
|
||||
$date = $resolvedOptions['date'];
|
||||
$template = $resolvedOptions['template'];
|
||||
$paCode = $resolvedOptions['paCode'];
|
||||
$sortByRoom = $resolvedOptions['sortByRoom'];
|
||||
|
||||
if ($filterSettings === null) {
|
||||
$filterSettings = $this->filterService->getNormalizedFilterSettings();
|
||||
@@ -118,7 +117,7 @@ class DateService implements SingletonInterface
|
||||
$nonBookableDateUids = GeneralUtility::trimExplode(',', $product->getNonBookableDates(), true);
|
||||
|
||||
return $this->preprocessPriceTable([
|
||||
'priceTableData' => $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date, $sortByRoom),
|
||||
'priceTableData' => $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date),
|
||||
'template' => $template,
|
||||
'paCode' => $paCode,
|
||||
'isDayTrip' => $product->isDaytrip(),
|
||||
@@ -188,7 +187,11 @@ class DateService implements SingletonInterface
|
||||
$resolver->setDefault('isDayTrip', false);
|
||||
$resolver->setDefault('showBookingButton', true);
|
||||
$resolvedOptions = $resolver->resolve($options);
|
||||
$priceTable = [];
|
||||
|
||||
$priceTable = [
|
||||
'available' => [],
|
||||
'unavailable' => [],
|
||||
];
|
||||
|
||||
$priceTableData = $resolvedOptions['priceTableData'];
|
||||
$template = $resolvedOptions['template'];
|
||||
@@ -210,7 +213,10 @@ class DateService implements SingletonInterface
|
||||
'isDayTrip' => $isDayTrip,
|
||||
]);
|
||||
$optionalServices = $this->preprocessOptionalServices(unserialize($row['roomOptionalServices'], ['allowed_classes' => false]));
|
||||
$priceTable[] = [
|
||||
|
||||
$key = $row['roomAvailable'] && $row['roomPrice'] ? 'available' : 'unavailable';
|
||||
|
||||
$priceTable[$key][] = [
|
||||
'dateStart' => $dateStart->format('d.m.Y'),
|
||||
'dateEnd' => $dateEnd->format('d.m.Y'),
|
||||
'season' => $row['season'],
|
||||
|
||||
+133
-123
@@ -47,129 +47,17 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<f:for each="{prices}" as="row">
|
||||
<tr class="odd:bg-zinc-50 hover:bg-zinc-100 odd:hover:bg-zinc-100"
|
||||
data-pricetable-target="row">
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
|
||||
<f:if condition="{row.showBookingButton} && {row.available}">
|
||||
<f:then>
|
||||
<a class="inline-block text-ep-primary-light text-xs sm:text-sm md:text-base leading-tight"
|
||||
href="{row.bookingUrl}"
|
||||
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-datalayer-action="trigger"
|
||||
target="_blank"
|
||||
title="{row.roomName}, {hotel.name}, {dateRange} buchen">
|
||||
{row.roomName}
|
||||
</a>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="inline-block text-xs sm:text-sm md:text-base line-through leading-tight">
|
||||
{row.roomName}
|
||||
</span>
|
||||
<f:if condition="{row.available}">
|
||||
<f:else>
|
||||
<span class="inline-block text-xs sm:text-sm md:text-base text-sm leading-tight lg:ml-2">ausgebucht</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
<f:if condition="{hasIncludedServices}">
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
|
||||
<div class="flex items-center justify-around space-x-1">
|
||||
<div class="grid grid-cols-2">
|
||||
<f:if condition="{row.busIncluded}">
|
||||
<f:then>
|
||||
<span class="block p-1"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="Busfahrt inklusive"
|
||||
role="tooltip"
|
||||
data-pricetable-target="busIcon"
|
||||
data-bus-included="{row.busIncluded}"
|
||||
data-bus-price="{row.busPrice}">
|
||||
<svg class="w-4 h-4 md:w-5 md:h-5">
|
||||
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-bus"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="block"></span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<f:if condition="{row.skipassIncluded}">
|
||||
<f:then>
|
||||
<span class="block p-1"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
|
||||
role="tooltip">
|
||||
<svg class="w-4 h-4 md:w-5 md:h-5">
|
||||
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-tag"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="block"></span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</f:if>
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2 text-center">
|
||||
<f:if condition="{row.hasOptionalServices} && {row.available}">
|
||||
<button type="button"
|
||||
class="text-ep-primary-light text-sm md:text-base text-left leading-none p-1"
|
||||
data-action="pricetable#openModal"
|
||||
data-pricetable-type-param="options"
|
||||
data-pricetable-uid-param="{row.roomUid}"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="Alle Zusatzleistungen anzeigen"
|
||||
role="tooltip">
|
||||
Zusatz­leistungen
|
||||
</button>
|
||||
</f:if>
|
||||
</td>
|
||||
<td class="px-2 py-1 md:p-2 whitespace-nowrap">
|
||||
<f:if condition="{row.showBookingButton}">
|
||||
<f:then>
|
||||
<f:if condition="{row.available}">
|
||||
<f:then>
|
||||
<a class="button bg-button button--small w-full"
|
||||
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-action="datalayer#trigger"
|
||||
href="{row.bookingUrl}"
|
||||
title="{row.roomName}, {hotel.name}, {dateRange} buchen"
|
||||
target="_blank">
|
||||
<span class="font-bold normal-case whitespace-nowrap"
|
||||
data-pricetable-target="price"
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice}
|
||||
</span>
|
||||
</a>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="button bg-button bg-button--muted button--small block cursor-not-allowed font-bold normal-case whitespace-nowrap"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="Leider ausgebucht"
|
||||
role="tooltip"
|
||||
data-pricetable-target="price"
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice} €
|
||||
</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:link.typolink parameter="{settings.travelAlertPageUid}"
|
||||
class="button bg-button button--small">
|
||||
Reisen-Alert
|
||||
</f:link.typolink>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
<f:comment><!--
|
||||
Render available and unavailable rooms separately to maintain order:
|
||||
1. by pax
|
||||
2. by price
|
||||
3. by availability
|
||||
--></f:comment>
|
||||
<f:for each="{prices.available}" as="row">
|
||||
<f:render section="Row" arguments="{_all}"/>
|
||||
</f:for>
|
||||
<f:for each="{prices.unavailable}" as="row">
|
||||
<f:render section="Row" arguments="{_all}"/>
|
||||
</f:for>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -307,4 +195,126 @@
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
<f:section name="Row">
|
||||
<tr class="odd:bg-zinc-50 hover:bg-zinc-100 odd:hover:bg-zinc-100"
|
||||
data-pricetable-target="row">
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
|
||||
<f:if condition="{row.showBookingButton} && {row.available}">
|
||||
<f:then>
|
||||
<a class="inline-block text-ep-primary-light text-xs sm:text-sm md:text-base leading-tight"
|
||||
href="{row.bookingUrl}"
|
||||
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-datalayer-action="trigger"
|
||||
target="_blank"
|
||||
title="{row.roomName}, {hotel.name}, {dateRange} buchen">
|
||||
{row.roomName}
|
||||
</a>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="inline-block text-xs sm:text-sm md:text-base line-through leading-tight">{row.roomName}</span>
|
||||
<f:if condition="{row.available}">
|
||||
<f:else>
|
||||
<span class="inline-block text-xs sm:text-sm md:text-base text-sm leading-tight lg:ml-2">ausgebucht</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
<f:if condition="{hasIncludedServices}">
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
|
||||
<div class="flex items-center justify-around space-x-1">
|
||||
<div class="grid grid-cols-2">
|
||||
<f:if condition="{row.busIncluded}">
|
||||
<f:then>
|
||||
<span class="block p-1"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="Busfahrt inklusive"
|
||||
role="tooltip"
|
||||
data-pricetable-target="busIcon"
|
||||
data-bus-included="{row.busIncluded}"
|
||||
data-bus-price="{row.busPrice}">
|
||||
<svg class="w-4 h-4 md:w-5 md:h-5">
|
||||
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-bus"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="block"></span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<f:if condition="{row.skipassIncluded}">
|
||||
<f:then>
|
||||
<span class="block p-1"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
|
||||
role="tooltip">
|
||||
<svg class="w-4 h-4 md:w-5 md:h-5">
|
||||
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-tag"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="block"></span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</f:if>
|
||||
<td class="border-r border-zinc-200 px-2 py-1 md:p-2 text-center">
|
||||
<f:if condition="{row.hasOptionalServices} && {row.available}">
|
||||
<button type="button"
|
||||
class="text-ep-primary-light text-sm md:text-base text-left leading-none p-1"
|
||||
data-action="pricetable#openModal"
|
||||
data-pricetable-type-param="options"
|
||||
data-pricetable-uid-param="{row.roomUid}"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="Alle Zusatzleistungen anzeigen"
|
||||
role="tooltip">
|
||||
Zusatz­leistungen
|
||||
</button>
|
||||
</f:if>
|
||||
</td>
|
||||
<td class="px-2 py-1 md:p-2 whitespace-nowrap">
|
||||
<f:if condition="{row.showBookingButton}">
|
||||
<f:then>
|
||||
<f:if condition="{row.available}">
|
||||
<f:then>
|
||||
<a class="button bg-button button--small w-full"
|
||||
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-action="datalayer#trigger"
|
||||
href="{row.bookingUrl}"
|
||||
title="{row.roomName}, {hotel.name}, {dateRange} buchen"
|
||||
target="_blank">
|
||||
<span class="font-bold normal-case whitespace-nowrap"
|
||||
data-pricetable-target="price"
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice}
|
||||
</span>
|
||||
</a>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<span class="button bg-button bg-button--muted button--small block cursor-not-allowed font-bold normal-case whitespace-nowrap"
|
||||
data-cooltipz-dir="top"
|
||||
aria-label="Leider ausgebucht"
|
||||
role="tooltip"
|
||||
data-pricetable-target="price"
|
||||
data-room="{row.minPrice}"
|
||||
data-bus="{row.busPrice}"
|
||||
data-discount="{row.discount}">
|
||||
{row.minPrice} €
|
||||
</span>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:link.typolink parameter="{settings.travelAlertPageUid}"
|
||||
class="button bg-button button--small">
|
||||
Reisen-Alert
|
||||
</f:link.typolink>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</td>
|
||||
</tr>
|
||||
</f:section>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user