diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/ProductController.php b/public/typo3conf/ext/ep_products/Classes/Controller/ProductController.php index 91a0e133..efb74112 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/ProductController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/ProductController.php @@ -36,6 +36,7 @@ use EP\EpProducts\Service\ContingentDataService; use EP\EpProducts\Service\DateService; use EP\EpProducts\Service\FilterService; use EP\EpProducts\Service\HotelDataService; +use EP\EpProducts\Service\PickupOverviewService; use EP\EpProducts\Service\ProductDataService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; @@ -82,6 +83,11 @@ class ProductController extends ActionController */ private $infoboxRepository; + /** + * @var PickupOverviewService + */ + private $pickupOverviewService; + /** * @param ProductRepository $productRepository * @param ProductDataService $productDataService @@ -91,6 +97,7 @@ class ProductController extends ActionController * @param DateService $dateService * @param FilterService $filterService * @param InfoboxRepository $infoboxRepository + * @param PickupOverviewService $pickupOverviewService */ public function __construct( ProductRepository $productRepository, @@ -100,7 +107,8 @@ class ProductController extends ActionController ContingentDataService $contingentDataService, DateService $dateService, FilterService $filterService, - InfoboxRepository $infoboxRepository + InfoboxRepository $infoboxRepository, + PickupOverviewService $pickupOverviewService ) { $this->productRepository = $productRepository; $this->productDataService = $productDataService; @@ -110,6 +118,7 @@ class ProductController extends ActionController $this->dateService = $dateService; $this->filterService = $filterService; $this->infoboxRepository = $infoboxRepository; + $this->pickupOverviewService = $pickupOverviewService; } /** @@ -196,6 +205,7 @@ class ProductController extends ActionController 'referringUrl' => $backlinkUrl, 'backlinkLabel' => $backLinkLabel, 'nonBookableInfo' => $nonBookableInfo, + 'pickupsOverview' => $this->pickupOverviewService->getOverview($product, $hotel), ]); } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php index 145fc725..086b563a 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php @@ -3,6 +3,7 @@ namespace EP\EpProducts\Domain\Repository; use EP\EpProducts\Domain\Model\Product; +use TYPO3\CMS\Core\Database\Connection; class DateRepository extends AbstractRepository { @@ -18,4 +19,26 @@ class DateRepository extends AbstractRepository ->execute() ; } + + /** + * @return string[] serialized pickups of each upcoming date, latest first + */ + public function findPickupsForProductAndHotel(int $productUid, int $hotelUid): array + { + $qb = $this->getDbConnection()->createQueryBuilder(); + + return $qb + ->select('date.pickups') + ->from('tx_epproducts_domain_model_date', 'date') + ->where( + $qb->expr()->eq('date.product', $qb->createNamedParameter($productUid, Connection::PARAM_INT)), + $qb->expr()->eq('date.hotel', $qb->createNamedParameter($hotelUid, Connection::PARAM_INT)), + 'date.date_start >= NOW()' + ) + ->orderBy('date.date_start', 'DESC') + ->addOrderBy('date.uid', 'DESC') + ->execute() + ->fetchFirstColumn() + ; + } } diff --git a/public/typo3conf/ext/ep_products/Classes/Service/PickupOverviewService.php b/public/typo3conf/ext/ep_products/Classes/Service/PickupOverviewService.php new file mode 100644 index 00000000..f1a4f608 --- /dev/null +++ b/public/typo3conf/ext/ep_products/Classes/Service/PickupOverviewService.php @@ -0,0 +1,80 @@ +dateRepository = $dateRepository; + } + + /** + * @return array{pickups: array} + */ + public function getOverview(Product $product, ?Hotel $hotel): array + { + $overview = ['pickups' => []]; + if ($hotel === null || $product->isDaytrip()) { + return $overview; + } + + $dates = $this->dateRepository->findPickupsForProductAndHotel((int) $product->getUid(), (int) $hotel->getUid()); + foreach ($dates as $serializedPickups) { + $pickups = $this->readPickups((string) $serializedPickups); + if ($pickups !== []) { + $overview['pickups'] = $pickups; + break; + } + } + + return $overview; + } + + /** + * Keeps the order of the import, which is the boarding order. + */ + private function readPickups(string $serializedPickups): array + { + $datePickups = @unserialize($serializedPickups, ['allowed_classes' => false]); + if (!is_array($datePickups)) { + return []; + } + + $pickups = []; + foreach ($datePickups as $pickup) { + $busProId = (int) ($pickup['bus_pro_id'] ?? 0); + if ($busProId === 0 || isset($pickups[$busProId])) { + continue; + } + $pickups[$busProId] = [ + 'busProId' => $busProId, + 'city' => (string) ($pickup['city'] ?? ''), + 'street' => (string) ($pickup['street'] ?? ''), + // A pickup's price is one leg; the surcharge is split evenly between out- and inbound. + 'price' => 2 * (float) ($pickup['price'] ?? 0.0), + 'time' => (string) ($pickup['time'] ?? ''), + ]; + } + + return array_values($pickups); + } +} diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/autocollapse_controller.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/autocollapse_controller.js index 5a0ca436..a43c527c 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/autocollapse_controller.js +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/controllers/autocollapse_controller.js @@ -2,7 +2,7 @@ import { Controller } from '@hotwired/stimulus' export default class extends Controller { - static targets = [ 'sizer', 'container', 'button' ] + static targets = [ 'sizer', 'container', 'button', 'bar' ] static values = { offset: { @@ -33,6 +33,12 @@ export default class extends Controller { // Set initial expanded/collapsed status and button visibility this.expandedValue = this.contentHeight < this.maxHeight this.buttonTarget.classList.toggle('hidden', this.expandedValue) + + // Drop the space reserved for the toggle bar when the content fits anyway + if (this.expandedValue && this.hasBarTarget) { + this.barTarget.classList.add('hidden') + this.containerTarget.style.paddingBottom = '0' + } } toggle() { diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Product/Pickups.html b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Product/Pickups.html new file mode 100644 index 00000000..6cc08bfc --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Partials/Product/Pickups.html @@ -0,0 +1,81 @@ + + +
+

+ Bus-Zustiege +

+ + + + + +
+
+ +
+ +
+
+
+
+
+

+ Abfahrtszeiten und -orte unter Vorbehalt. Die genauen Zeiten werden euch kurz vor der Anreise mitgeteilt. +

+
+ + + + + + + + + + + + + + + +
+ Zustieg + + Abfahrt + + Zuschlag +
+ {pickup.city}
{pickup.street} +
+ + {pickup.time} Uhr + - + + + + {pickup.price -> f:format.currency(currencySign: '€', decimalSeparator: ',', thousandsSeparator: '.')} + - + +
+
+ + + + + + + + + + + + + + diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html index 46f34496..7eb2e054 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html @@ -42,7 +42,7 @@ - + @@ -158,11 +158,16 @@ - +