WIP Migrate to stimulus.js and tailwind
This commit is contained in:
@@ -30,18 +30,25 @@ namespace EP\EpProducts\Controller;
|
|||||||
use EP\EpProducts\Domain\Model\Date;
|
use EP\EpProducts\Domain\Model\Date;
|
||||||
use EP\EpProducts\Domain\Model\Hotel;
|
use EP\EpProducts\Domain\Model\Hotel;
|
||||||
use EP\EpProducts\Domain\Model\Product;
|
use EP\EpProducts\Domain\Model\Product;
|
||||||
|
use EP\EpProducts\Domain\Repository\ProductRepository;
|
||||||
use EP\EpProducts\Service\DateService;
|
use EP\EpProducts\Service\DateService;
|
||||||
use EP\EpProducts\Service\FilterService;
|
use EP\EpProducts\Service\FilterService;
|
||||||
use EP\EpProducts\Service\ResellerDataService;
|
use EP\EpProducts\Service\ResellerDataService;
|
||||||
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
|
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
|
||||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||||
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||||
|
|
||||||
class AjaxTableController extends ActionController
|
class AjaxTableController extends ActionController
|
||||||
{
|
{
|
||||||
|
|
||||||
use RequestArgumentTypeConversionTrait;
|
use RequestArgumentTypeConversionTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ProductRepository
|
||||||
|
*/
|
||||||
|
protected $productRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \EP\EpProducts\Service\FilterService
|
* @var \EP\EpProducts\Service\FilterService
|
||||||
*/
|
*/
|
||||||
@@ -64,17 +71,85 @@ class AjaxTableController extends ActionController
|
|||||||
*/
|
*/
|
||||||
public function __construct
|
public function __construct
|
||||||
(
|
(
|
||||||
|
ProductRepository $productRepository,
|
||||||
DateService $dateService,
|
DateService $dateService,
|
||||||
FilterService $filterService,
|
FilterService $filterService,
|
||||||
ResellerDataService $resellerDataService
|
ResellerDataService $resellerDataService
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
$this->productRepository = $productRepository;
|
||||||
$this->dateService = $dateService;
|
$this->dateService = $dateService;
|
||||||
$this->filterService = $filterService;
|
$this->filterService = $filterService;
|
||||||
$this->resellerDataService = $resellerDataService;
|
$this->resellerDataService = $resellerDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function initializeDatesAction()
|
||||||
|
{
|
||||||
|
if ($this->request->hasArgument('dateFrom')) {
|
||||||
|
$dateFrom = $this->request->getArgument('dateFrom');
|
||||||
|
$this->request->setArgument('dateFrom', $dateFrom === '' ? null : $dateFrom);
|
||||||
|
}
|
||||||
|
if ($this->request->hasArgument('dateTo')) {
|
||||||
|
$dateTo = $this->request->getArgument('dateTo');
|
||||||
|
$this->request->setArgument('dateTo', $dateTo === '' ? null : $dateTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Product $product
|
||||||
|
* @param Hotel $hotel
|
||||||
|
* @param string $paCode
|
||||||
|
* @param string $dateFrom
|
||||||
|
* @param string $dateTo
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \Doctrine\DBAL\DBALException
|
||||||
|
*/
|
||||||
|
public function datestableAction(Product $product, Hotel $hotel, $paCode = null, $dateFrom = null, $dateTo = null)
|
||||||
|
{
|
||||||
|
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||||
|
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
|
||||||
|
$filterSettings['dateFrom'] = $dateFrom;
|
||||||
|
$filterSettings['dateTo'] = $dateTo;
|
||||||
|
|
||||||
|
$altProductLink = null;
|
||||||
|
$altLabel = null;
|
||||||
|
if ($product->getAltProduct()) {
|
||||||
|
$altProduct = $product->getAltProduct();
|
||||||
|
if ($this->productRepository->hasDates($altProduct)) {
|
||||||
|
/** @var ContentObjectRenderer $contentObject */
|
||||||
|
$contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||||
|
$altProductLink = $contentObject->typoLink_URL(
|
||||||
|
[
|
||||||
|
'parameter' => $altProduct->getDetailPage(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$altLabel = $product->getAltLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($reseller = $this->resellerDataService->getResellerForCurrentDomain()) {
|
||||||
|
$paCode = $reseller['paCode'];
|
||||||
|
}
|
||||||
|
$datesTable = $this->dateService->getDatesTable([
|
||||||
|
'product' => $product,
|
||||||
|
'hotel' => $hotel,
|
||||||
|
'filterSettings' => $filterSettings,
|
||||||
|
'template' => $this->settings['bpnBookingUrlTemplateCode'],
|
||||||
|
'paCode' => $paCode,
|
||||||
|
]);
|
||||||
|
$hasSurchargeOrDiscount = $this->dateService->checkSurcharcheOrDiscount($datesTable);
|
||||||
|
|
||||||
|
$this->view->assignMultiple([
|
||||||
|
'dates' => $datesTable,
|
||||||
|
'bookable' => $product->getBookable(),
|
||||||
|
'hotel' => $hotel,
|
||||||
|
'altProductLink' => $altProductLink,
|
||||||
|
'altLabel' => $altLabel,
|
||||||
|
'hasSurcharge' => $hasSurchargeOrDiscount['surcharge'],
|
||||||
|
'hasDiscount' => $hasSurchargeOrDiscount['discount'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Product $product
|
* @param Product $product
|
||||||
* @param Hotel $hotel
|
* @param Hotel $hotel
|
||||||
@@ -88,7 +163,7 @@ class AjaxTableController extends ActionController
|
|||||||
(
|
(
|
||||||
Product $product,
|
Product $product,
|
||||||
Hotel $hotel,
|
Hotel $hotel,
|
||||||
Date $date = null,
|
Date $date,
|
||||||
$paCode = null
|
$paCode = null
|
||||||
) {
|
) {
|
||||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||||
@@ -106,16 +181,20 @@ class AjaxTableController extends ActionController
|
|||||||
'template' => $this->settings['bpnBookingUrlTemplateCode'],
|
'template' => $this->settings['bpnBookingUrlTemplateCode'],
|
||||||
'paCode' => $paCode,
|
'paCode' => $paCode,
|
||||||
]);
|
]);
|
||||||
|
$hasSurchargeOrDiscount = $this->dateService->checkSurcharcheOrDiscount($priceTable);
|
||||||
|
|
||||||
$dateStart = $date ? $date->getDateStart()->format('d.m.Y') : '';
|
$dateStart = $date ? $date->getDateStart()->format('d.m.Y') : '';
|
||||||
$dateEnd = $date ? $date->getDateEnd()->format('d.m.Y') : '';
|
$dateEnd = $date ? $date->getDateEnd()->format('d.m.Y') : '';
|
||||||
$dateRange = sprintf('%s - %s', $dateStart, $dateEnd);
|
$dateRange = sprintf('%s - %s', $dateStart, $dateEnd);
|
||||||
|
|
||||||
return json_encode([
|
$this->view->assignMultiple([
|
||||||
'dateRange' => $dateRange,
|
'dateRange' => $dateRange,
|
||||||
'servicesIncluded' => $date ? $date->getServicesIncluded() : [],
|
'hotel' => $hotel,
|
||||||
|
'servicesIncluded' => $date->getServicesIncluded(),
|
||||||
'prices' => $priceTable,
|
'prices' => $priceTable,
|
||||||
], JSON_THROW_ON_ERROR);
|
'hasSurcharge' => $hasSurchargeOrDiscount['surcharge'],
|
||||||
|
'hasDiscount' => $hasSurchargeOrDiscount['discount'],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class DateService implements SingletonInterface
|
|||||||
'template' => $template,
|
'template' => $template,
|
||||||
'paCode' => $paCode,
|
'paCode' => $paCode,
|
||||||
'isDayTrip' => $product->isDaytrip(),
|
'isDayTrip' => $product->isDaytrip(),
|
||||||
'isHideBookingButton' => $product->getHideBookingButton(),
|
'showBookingButton' => false === $product->getHideBookingButton(),
|
||||||
'nonBookableDateUids' => $nonBookableDateUids,
|
'nonBookableDateUids' => $nonBookableDateUids,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ class DateService implements SingletonInterface
|
|||||||
'template' => $template,
|
'template' => $template,
|
||||||
'paCode' => $paCode,
|
'paCode' => $paCode,
|
||||||
'isDayTrip' => $product->isDaytrip(),
|
'isDayTrip' => $product->isDaytrip(),
|
||||||
'isHideBookingButton' => $product->getHideBookingButton(),
|
'showBookingButton' => false === $product->getHideBookingButton(),
|
||||||
'nonBookableDateUids' => $nonBookableDateUids,
|
'nonBookableDateUids' => $nonBookableDateUids,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -198,10 +198,10 @@ class DateService implements SingletonInterface
|
|||||||
public function preprocessPriceTable(array $options): array
|
public function preprocessPriceTable(array $options): array
|
||||||
{
|
{
|
||||||
$resolver = new OptionsResolver();
|
$resolver = new OptionsResolver();
|
||||||
$resolver->setDefined(['paCode', 'isDayTrip', 'isHideBookingButton', 'nonBookableDateUids']);
|
$resolver->setDefined(['paCode', 'isDayTrip', 'showBookingButton', 'nonBookableDateUids']);
|
||||||
$resolver->setRequired(['priceTableData', 'template']);
|
$resolver->setRequired(['priceTableData', 'template']);
|
||||||
$resolver->setDefault('isDayTrip', false);
|
$resolver->setDefault('isDayTrip', false);
|
||||||
$resolver->setDefault('isHideBookingButton', false);
|
$resolver->setDefault('showBookingButton', true);
|
||||||
$resolvedOptions = $resolver->resolve($options);
|
$resolvedOptions = $resolver->resolve($options);
|
||||||
$priceTable = [];
|
$priceTable = [];
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ class DateService implements SingletonInterface
|
|||||||
$nonBookableDateUids = $resolvedOptions['nonBookableDateUids'];
|
$nonBookableDateUids = $resolvedOptions['nonBookableDateUids'];
|
||||||
|
|
||||||
foreach ($priceTableData as $row) {
|
foreach ($priceTableData as $row) {
|
||||||
$isHideBookingButton = $resolvedOptions['isHideBookingButton'] || in_array($row['dateBusProId'], $nonBookableDateUids, false);
|
$showBookingButton = $resolvedOptions['showBookingButton'] || !in_array($row['dateBusProId'], $nonBookableDateUids, false);
|
||||||
|
|
||||||
$dateStart = new \DateTime($row['dateStart']);
|
$dateStart = new \DateTime($row['dateStart']);
|
||||||
$dateEnd = new \DateTime($row['dateEnd']);
|
$dateEnd = new \DateTime($row['dateEnd']);
|
||||||
@@ -229,23 +229,23 @@ class DateService implements SingletonInterface
|
|||||||
'dateStart' => $dateStart->format('d.m.Y'),
|
'dateStart' => $dateStart->format('d.m.Y'),
|
||||||
'dateEnd' => $dateEnd->format('d.m.Y'),
|
'dateEnd' => $dateEnd->format('d.m.Y'),
|
||||||
'season' => $row['season'],
|
'season' => $row['season'],
|
||||||
'dateBusIncluded' => (bool) $row['dateBusIncluded'],
|
'summer' => $row['season'] === 's',
|
||||||
'dateSkipassIncluded' => (bool) $row['dateSkipassIncluded'],
|
'busIncluded' => (bool) $row['dateBusIncluded'],
|
||||||
|
'skipassIncluded' => (bool) $row['dateSkipassIncluded'],
|
||||||
'roomUid' => $row['roomUid'],
|
'roomUid' => $row['roomUid'],
|
||||||
'roomAvailable' => $row['roomAvailable'] && $row['roomPrice'],
|
|
||||||
'roomName' => $row['roomName'],
|
'roomName' => $row['roomName'],
|
||||||
'roomPrice' => $row['roomPrice'],
|
'available' => $row['roomAvailable'] && $row['roomPrice'],
|
||||||
'roomBusPrice' => $row['busPrice'],
|
'minPrice' => $row['roomPrice'],
|
||||||
'roomDiscount' => $row['roomDiscount'],
|
'busPrice' => $row['busPrice'],
|
||||||
'roomNights' => $row['roomNights'],
|
'discount' => $row['roomDiscount'],
|
||||||
'roomOptionalServices' => $optionalServices,
|
'nights' => $row['roomNights'],
|
||||||
'roomHasOptionalServices' => count($optionalServices) > 0,
|
'optionalServices' => $optionalServices,
|
||||||
|
'hasOptionalServices' => count($optionalServices) > 0,
|
||||||
'dateBusProId' => $row['dateBusProId'],
|
'dateBusProId' => $row['dateBusProId'],
|
||||||
'hotelBusProId' => $row['hotelBusProId'],
|
'hotelBusProId' => $row['hotelBusProId'],
|
||||||
'roomBusProId' => $row['roomBusProId'],
|
'roomBusProId' => $row['roomBusProId'],
|
||||||
'bookingUrl' => $bookingUrl,
|
'bookingUrl' => $bookingUrl,
|
||||||
'isHideBookingButton' => $isHideBookingButton,
|
'showBookingButton' => $showBookingButton,
|
||||||
'showBookingButton' => !$isHideBookingButton,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,10 +272,10 @@ class DateService implements SingletonInterface
|
|||||||
public function preprocessDatesTable(array $options): array
|
public function preprocessDatesTable(array $options): array
|
||||||
{
|
{
|
||||||
$resolver = new OptionsResolver();
|
$resolver = new OptionsResolver();
|
||||||
$resolver->setDefined(['paCode', 'isDayTrip', 'isHideBookingButton', 'nonBookableDateUids']);
|
$resolver->setDefined(['paCode', 'isDayTrip', 'showBookingButton', 'nonBookableDateUids']);
|
||||||
$resolver->setRequired(['dateTableData', 'template']);
|
$resolver->setRequired(['dateTableData', 'template']);
|
||||||
$resolver->setDefault('isDayTrip', false);
|
$resolver->setDefault('isDayTrip', false);
|
||||||
$resolver->setDefault('isHideBookingButton', false);
|
$resolver->setDefault('showBookingButton', true);
|
||||||
$resolver->setDefault('nonBookableDateUids', []);
|
$resolver->setDefault('nonBookableDateUids', []);
|
||||||
$resolvedOptions = $resolver->resolve($options);
|
$resolvedOptions = $resolver->resolve($options);
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ class DateService implements SingletonInterface
|
|||||||
$nonBookableDateUids = $resolvedOptions['nonBookableDateUids'];
|
$nonBookableDateUids = $resolvedOptions['nonBookableDateUids'];
|
||||||
|
|
||||||
foreach ($dateTableData as $row) {
|
foreach ($dateTableData as $row) {
|
||||||
$isHideBookingButton = $resolvedOptions['isHideBookingButton'] || in_array($row['dateBusProId'], $nonBookableDateUids, false);
|
$showBookingButton = $resolvedOptions['showBookingButton'] || !in_array($row['dateBusProId'], $nonBookableDateUids, false);
|
||||||
|
|
||||||
$dateStart = new \DateTime($row['dateStart']);
|
$dateStart = new \DateTime($row['dateStart']);
|
||||||
$dateEnd = new \DateTime($row['dateEnd']);
|
$dateEnd = new \DateTime($row['dateEnd']);
|
||||||
@@ -304,21 +304,21 @@ class DateService implements SingletonInterface
|
|||||||
'dateUid' => $row['dateUid'],
|
'dateUid' => $row['dateUid'],
|
||||||
'dateStart' => $dateStart->format('d.m.y'),
|
'dateStart' => $dateStart->format('d.m.y'),
|
||||||
'dateEnd' => $dateEnd->format('d.m.y'),
|
'dateEnd' => $dateEnd->format('d.m.y'),
|
||||||
'dateMinPrice' => $row['dateMinPrice'],
|
'minPrice' => $row['dateMinPrice'],
|
||||||
'datePseudoPrice' => $row['datePseudoPrice'],
|
'pseudoPrice' => $row['datePseudoPrice'],
|
||||||
'dateBusPrice' => $row['dateBusPrice'] ?? 0,
|
'busPrice' => $row['dateBusPrice'] ?? 0,
|
||||||
'dateDiscount' => $row['dateDiscount'] ?? 0,
|
'discount' => $row['dateDiscount'] ?? 0,
|
||||||
'dateNights' => $row['dateNights'],
|
'nights' => $row['dateNights'],
|
||||||
'dateBusIncluded' => (bool) $row['dateBusIncluded'],
|
'busIncluded' => (bool) $row['dateBusIncluded'],
|
||||||
'dateSkipassIncluded' => (bool) $row['dateSkipassIncluded'],
|
'skipassIncluded' => (bool) $row['dateSkipassIncluded'],
|
||||||
'dateServicesIncluded' => unserialize($row['dateServicesIncluded'], ['allowed_classes' => false]),
|
'servicesIncluded' => unserialize($row['dateServicesIncluded'], ['allowed_classes' => false]),
|
||||||
'dateServicesGeneral' => unserialize($row['dateServicesGeneral'], ['allowed_classes' => false]),
|
'servicesGeneral' => unserialize($row['dateServicesGeneral'], ['allowed_classes' => false]),
|
||||||
'season' => $row['season'],
|
'season' => $row['season'],
|
||||||
|
'summer' => $row['season'] === 's',
|
||||||
'available' => $row['available'],
|
'available' => $row['available'],
|
||||||
'unavailable' => $row['available'] === 0,
|
'unavailable' => $row['available'] === 0,
|
||||||
'bookingUrl' => $bookingUrl,
|
'bookingUrl' => $bookingUrl,
|
||||||
'isHideBookingButton' => $isHideBookingButton,
|
'showBookingButton' => $showBookingButton,
|
||||||
'showBookingButton' => !$isHideBookingButton,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,18 +420,18 @@ class DateService implements SingletonInterface
|
|||||||
return $priceTable;
|
return $priceTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkSurcharcheOrDiscount(array $datesTableData): array
|
public function checkSurcharcheOrDiscount(array $tableData): array
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'surcharge' => false,
|
'surcharge' => false,
|
||||||
'discount' => false,
|
'discount' => false,
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($datesTableData as $row) {
|
foreach ($tableData as $row) {
|
||||||
if ($row['dateBusPrice'] && $row['dateBusPrice'] > 0) {
|
if ($row['busPrice'] && $row['busPrice'] > 0) {
|
||||||
$data['surcharge'] = true;
|
$data['surcharge'] = true;
|
||||||
}
|
}
|
||||||
if ($row['dateDiscount'] && $row['dateDiscount'] < 0) {
|
if ($row['discount'] && $row['discount'] < 0) {
|
||||||
$data['discount'] = true;
|
$data['discount'] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,9 +159,10 @@ tx_epproducts_ajax_json {
|
|||||||
1 = update
|
1 = update
|
||||||
}
|
}
|
||||||
AjaxTable {
|
AjaxTable {
|
||||||
1 = pricetable
|
1 = datestable
|
||||||
2 = pricetableHtml
|
2 = pricetable
|
||||||
3 = eventPricetable
|
3 = pricetableHtml
|
||||||
|
4 = eventPricetable
|
||||||
}
|
}
|
||||||
AjaxCalendar {
|
AjaxCalendar {
|
||||||
1 = range
|
1 = range
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ $iconRegistry->registerIcon(
|
|||||||
'AjaxDate' => 'dates',
|
'AjaxDate' => 'dates',
|
||||||
'AjaxSearchbar' => 'update',
|
'AjaxSearchbar' => 'update',
|
||||||
'AjaxContingent' => 'list,rooms',
|
'AjaxContingent' => 'list,rooms',
|
||||||
'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable',
|
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
||||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||||
'AjaxWatchlist' => 'list',
|
'AjaxWatchlist' => 'list',
|
||||||
'AjaxGroupsPrice' => 'processForm',
|
'AjaxGroupsPrice' => 'processForm',
|
||||||
@@ -383,7 +383,7 @@ $iconRegistry->registerIcon(
|
|||||||
'AjaxDate' => 'dates',
|
'AjaxDate' => 'dates',
|
||||||
'AjaxSearchbar' => 'update',
|
'AjaxSearchbar' => 'update',
|
||||||
'AjaxContingent' => 'list,rooms',
|
'AjaxContingent' => 'list,rooms',
|
||||||
'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable',
|
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
||||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||||
'AjaxWatchlist' => 'list',
|
'AjaxWatchlist' => 'list',
|
||||||
'AjaxGroupsPrice' => 'processForm',
|
'AjaxGroupsPrice' => 'processForm',
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ smoothscroll.polyfill()
|
|||||||
import Alpine from 'alpinejs'
|
import Alpine from 'alpinejs'
|
||||||
import parseAppConfig from './components/appconfig'
|
import parseAppConfig from './components/appconfig'
|
||||||
import appendPaCode from './components/pacode'
|
import appendPaCode from './components/pacode'
|
||||||
import productDetail from './components/product-detail'
|
|
||||||
import daytripDateSelect from './components/daytrip-date-select'
|
import daytripDateSelect from './components/daytrip-date-select'
|
||||||
import searchBar from './components/search-bar'
|
import searchBar from './components/search-bar'
|
||||||
import calendar from './components/calendar'
|
import calendar from './components/calendar'
|
||||||
@@ -22,7 +21,6 @@ import contactForm from './components/contact-form'
|
|||||||
const appConfig = parseAppConfig()
|
const appConfig = parseAppConfig()
|
||||||
appendPaCode(appConfig)
|
appendPaCode(appConfig)
|
||||||
Alpine.store('appconfig', appConfig)
|
Alpine.store('appconfig', appConfig)
|
||||||
Alpine.data('productDetail', productDetail)
|
|
||||||
Alpine.data('daytripDateSelect', daytripDateSelect)
|
Alpine.data('daytripDateSelect', daytripDateSelect)
|
||||||
Alpine.data('searchBar', searchBar)
|
Alpine.data('searchBar', searchBar)
|
||||||
Alpine.data('calendar', calendar)
|
Alpine.data('calendar', calendar)
|
||||||
|
|||||||
-98
@@ -1,98 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default props => ({
|
|
||||||
uris: props.uris,
|
|
||||||
show: props.hotelOnTop ? 'hotel' : 'dates',
|
|
||||||
tableMode: 'dates',
|
|
||||||
datesRows: [],
|
|
||||||
pricesRows: [],
|
|
||||||
servicesIncluded: [],
|
|
||||||
dateRange: '',
|
|
||||||
available: false,
|
|
||||||
singleDate: false,
|
|
||||||
bookable: true,
|
|
||||||
hasSurcharge: false,
|
|
||||||
hasDiscount: false,
|
|
||||||
altLabel: null,
|
|
||||||
altProductLink: null,
|
|
||||||
loading: false,
|
|
||||||
loaded: false,
|
|
||||||
addBus: false,
|
|
||||||
discountBus: false,
|
|
||||||
showAll: false,
|
|
||||||
init() {
|
|
||||||
// Load dates instantly when initial view is dates
|
|
||||||
if ('dates' === this.show) {
|
|
||||||
this.loadDates()
|
|
||||||
}
|
|
||||||
// Scroll to top when switching table mode
|
|
||||||
this.$watch('tableMode', () => {
|
|
||||||
this.$el.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
|
||||||
})
|
|
||||||
// Load dates (once) when switching to dates view
|
|
||||||
this.$watch('show', () => {
|
|
||||||
if ('dates' === this.show && false === this.loaded) {
|
|
||||||
this.loadDates()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadDates() {
|
|
||||||
this.loading = true
|
|
||||||
axios.get(this.uris.dates)
|
|
||||||
.then(response => {
|
|
||||||
const json = response.data
|
|
||||||
if (json.dates.length === 1) {
|
|
||||||
this.singleDate = true
|
|
||||||
const dateUid = json.dates[0].dateUid
|
|
||||||
this.loadPrices(dateUid)
|
|
||||||
} else {
|
|
||||||
this.singleDate = false
|
|
||||||
this.datesRows = json.dates
|
|
||||||
this.bookable = json.bookable
|
|
||||||
this.altLabel = json.altLabel
|
|
||||||
this.altProductLink = json.altProductLink
|
|
||||||
this.hasSurcharge = json.hasSurcharge
|
|
||||||
this.hasDiscount = json.hasDiscount
|
|
||||||
this.available = json.dates.length > 0
|
|
||||||
this.tableMode = 'dates'
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
this.loaded = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadPrices(dateUid) {
|
|
||||||
this.loading = true
|
|
||||||
const data = new URLSearchParams({
|
|
||||||
'tx_epproducts_ajax[date]': dateUid,
|
|
||||||
'tx_epproducts_ajax[paCode]': this.$store.appconfig.paCode,
|
|
||||||
})
|
|
||||||
axios.post(this.uris.pricetable, data)
|
|
||||||
.then(response => {
|
|
||||||
const json = response.data
|
|
||||||
this.pricesRows = json.prices
|
|
||||||
this.servicesIncluded = json.servicesIncluded
|
|
||||||
this.dateRange = json.dateRange
|
|
||||||
this.tableMode = 'prices'
|
|
||||||
}).catch(() => {
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
calculatePrice(minPrice, busPrice, discount) {
|
|
||||||
if (this.addBus) {
|
|
||||||
return minPrice + busPrice
|
|
||||||
}
|
|
||||||
if (this.discountBus) {
|
|
||||||
return minPrice + discount
|
|
||||||
}
|
|
||||||
return minPrice
|
|
||||||
},
|
|
||||||
showBusIcon(busPrice, busIncluded) {
|
|
||||||
return (
|
|
||||||
busPrice && this.addBus ||
|
|
||||||
busIncluded && !this.discountBus
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
import { Controller } from 'stimulus'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
|
||||||
|
static targets = [ 'content' ]
|
||||||
|
|
||||||
|
open(e) {
|
||||||
|
this.contentTargets.forEach(element => {
|
||||||
|
let uid = element.dataset.uid
|
||||||
|
element.classList.toggle('hidden', e.detail !== uid)
|
||||||
|
})
|
||||||
|
this.element.classList.remove('hidden')
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.element.classList.add('hidden', true)
|
||||||
|
}
|
||||||
|
}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
import { Controller } from 'stimulus'
|
||||||
|
import { useDispatch } from 'stimulus-use'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
|
||||||
|
static targets = [
|
||||||
|
'row',
|
||||||
|
'price',
|
||||||
|
'busIcon',
|
||||||
|
'surchargeToggleLabel',
|
||||||
|
'discountToggleLabel',
|
||||||
|
'rowToggleLabel',
|
||||||
|
'rowToggleIcon',
|
||||||
|
]
|
||||||
|
|
||||||
|
static values = {
|
||||||
|
mode: String,
|
||||||
|
showAllRows: Boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
useDispatch(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSurcharge() {
|
||||||
|
this.modeValue = this.modeValue === 'surcharge' ? '' : 'surcharge'
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDiscount() {
|
||||||
|
this.modeValue = this.modeValue === 'discount' ? '' : 'discount'
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleRows() {
|
||||||
|
this.showAllRowsValue = !this.showAllRowsValue
|
||||||
|
}
|
||||||
|
|
||||||
|
openModal(e) {
|
||||||
|
let eventName = `${e.currentTarget.dataset.type}-modal:open`
|
||||||
|
this.dispatch(eventName, e.currentTarget.dataset.uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
modeValueChanged() {
|
||||||
|
this.priceTargets.forEach(price => {
|
||||||
|
let room = parseInt(price.dataset.room)
|
||||||
|
let bus = parseInt(price.dataset.bus)
|
||||||
|
let discount = parseInt(price.dataset.discount)
|
||||||
|
price.innerText = `${this.calculatePrice(room, bus, discount)} €`
|
||||||
|
})
|
||||||
|
this.busIconTargets.forEach(icon => {
|
||||||
|
let busPrice = parseInt(icon.dataset.busPrice)
|
||||||
|
let busIncluded = !!icon.dataset.busIncluded
|
||||||
|
icon.classList.toggle('hidden', this.hideBusIcon(busPrice, busIncluded))
|
||||||
|
})
|
||||||
|
if (this.hasSurchargeToggleLabelTarget) {
|
||||||
|
let labelDefault = this.surchargeToggleLabelTarget.dataset.labelDefault
|
||||||
|
let labelAlt = this.surchargeToggleLabelTarget.dataset.labelAlt
|
||||||
|
this.surchargeToggleLabelTarget.innerText = this.modeValue === 'surcharge' ? labelDefault : labelAlt
|
||||||
|
}
|
||||||
|
if (this.hasDiscountToggleLabelTarget) {
|
||||||
|
let labelDefault = this.discountToggleLabelTarget.dataset.labelDefault
|
||||||
|
let labelAlt = this.discountToggleLabelTarget.dataset.labelAlt
|
||||||
|
this.discountToggleLabelTarget.innerText = this.modeValue === 'discount' ? labelDefault : labelAlt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showAllRowsValueChanged() {
|
||||||
|
let additionalRows = this.rowTargets.slice(5)
|
||||||
|
if (additionalRows.length) {
|
||||||
|
additionalRows.forEach(row => {
|
||||||
|
row.classList.toggle('hidden', this.showAllRowsValue === false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (this.hasRowToggleLabelTarget && this.hasRowToggleIconTarget) {
|
||||||
|
this.rowToggleLabelTarget.innerText = this.showAllRowsValue ? 'weniger Termine anzeigen' : 'alle Termine anzeigen'
|
||||||
|
this.rowToggleIconTarget.setAttribute('href', this.showAllRowsValue ? '#icon-minus-circle' : '#icon-plus-circle')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatePrice(minPrice, busPrice, discount) {
|
||||||
|
if ('surcharge' === this.modeValue) {
|
||||||
|
return minPrice + busPrice
|
||||||
|
}
|
||||||
|
if ('discount' === this.modeValue) {
|
||||||
|
return minPrice + discount
|
||||||
|
}
|
||||||
|
return minPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
hideBusIcon(busPrice, busIncluded) {
|
||||||
|
return (
|
||||||
|
busPrice && this.modeValue !== 'surcharge' ||
|
||||||
|
busIncluded && this.modeValue === 'discount'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
import { Controller } from 'stimulus'
|
||||||
|
import { useFetch } from '../mixins/use_fetch'
|
||||||
|
import { useConfig } from '../mixins/use_config'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
|
||||||
|
static targets = [
|
||||||
|
'loadingIndicator',
|
||||||
|
'loadingMessage',
|
||||||
|
'datesTableContainer',
|
||||||
|
'priceTableContainer',
|
||||||
|
]
|
||||||
|
|
||||||
|
static values = {
|
||||||
|
datestableUri: String,
|
||||||
|
pricetableUri: String,
|
||||||
|
travelAlertUri: String,
|
||||||
|
loading: Boolean,
|
||||||
|
loaded: Boolean,
|
||||||
|
mode: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
useFetch(this)
|
||||||
|
useConfig(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
if ('dates' === this.modeValue) {
|
||||||
|
this.loadDates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showDates() {
|
||||||
|
this.modeValue = 'dates'
|
||||||
|
}
|
||||||
|
|
||||||
|
showPrices() {
|
||||||
|
this.modeValue = 'prices'
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDates() {
|
||||||
|
if (true === this.loadedValue) {
|
||||||
|
this.modeValue = 'dates'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loadingValue = true
|
||||||
|
this.datesTableContainerTarget.innerHTML = ''
|
||||||
|
this.modeValue = 'dates'
|
||||||
|
fetch(this.datestableUriValue)
|
||||||
|
.then(this.checkStatus)
|
||||||
|
.then(this.parseHTML)
|
||||||
|
.then(html => {
|
||||||
|
this.datesTableContainerTarget.innerHTML = html
|
||||||
|
}).catch(() => {
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadingValue = false
|
||||||
|
this.loadedValue = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPrices(e) {
|
||||||
|
this.loadingValue = true
|
||||||
|
this.priceTableContainerTarget.innerHTML = ''
|
||||||
|
this.modeValue = 'prices'
|
||||||
|
let paCode = this.getConfig().paCode
|
||||||
|
let dateUid = e.currentTarget.dataset.dateUid
|
||||||
|
const data = new URLSearchParams({
|
||||||
|
'tx_epproducts_ajax[date]': dateUid,
|
||||||
|
'tx_epproducts_ajax[paCode]': paCode,
|
||||||
|
})
|
||||||
|
fetch(this.pricetableUriValue, { method: 'POST', body: data })
|
||||||
|
.then(this.checkStatus)
|
||||||
|
.then(this.parseHTML)
|
||||||
|
.then(html => {
|
||||||
|
this.priceTableContainerTarget.innerHTML = html
|
||||||
|
}).catch(() => {
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadingValue = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingValueChanged() {
|
||||||
|
this.loadingMessageTarget.innerText = this.modeValue === 'prices' ? 'Lade Buchungsptionen...' : 'Lade Termine...'
|
||||||
|
this.loadingIndicatorTarget.classList.toggle('hidden', this.loadingValue === false)
|
||||||
|
}
|
||||||
|
|
||||||
|
modeValueChanged() {
|
||||||
|
this.datesTableContainerTarget.classList.toggle('hidden', this.modeValue === 'prices')
|
||||||
|
this.priceTableContainerTarget.classList.toggle('hidden', this.modeValue === 'dates')
|
||||||
|
this.element.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
import { Controller } from 'stimulus'
|
||||||
|
import { useDispatch } from 'stimulus-use'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
|
||||||
|
static targets = [ 'tab', 'button' ]
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
useDispatch(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
select(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
let selectedTab = e.currentTarget.dataset.tab
|
||||||
|
this.buttonTargets.forEach(element => {
|
||||||
|
let currentTab = element.dataset.tab
|
||||||
|
element.classList.toggle('button--active', selectedTab === currentTab)
|
||||||
|
})
|
||||||
|
this.tabTargets.forEach(element => {
|
||||||
|
let currentTab = element.dataset.tab
|
||||||
|
element.classList.toggle('hidden', selectedTab !== currentTab)
|
||||||
|
})
|
||||||
|
this.dispatch('selected', selectedTab)
|
||||||
|
this.element.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+9
-2
@@ -8,7 +8,11 @@ export default class extends Controller {
|
|||||||
onList: Boolean
|
onList: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
static targets = [ 'label', 'icon' ]
|
static targets = [
|
||||||
|
'label',
|
||||||
|
'icon',
|
||||||
|
'ariaLabel',
|
||||||
|
]
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
useDispatch(this)
|
useDispatch(this)
|
||||||
@@ -28,7 +32,10 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onListValueChanged() {
|
onListValueChanged() {
|
||||||
this.labelTarget.setAttribute('aria-label', true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen')
|
this.labelTarget.innerText = true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'
|
||||||
this.iconTarget.setAttribute('href', true === this.onListValue ? '#icon-minus-circle' : '#icon-plus-circle')
|
this.iconTarget.setAttribute('href', true === this.onListValue ? '#icon-minus-circle' : '#icon-plus-circle')
|
||||||
|
if (this.hasAriaLabelTarget) {
|
||||||
|
this.ariaLabelTarget.setAttribute('aria-label', true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
.button {
|
.button {
|
||||||
@apply inline-flex items-center justify-center px-4 py-2 md:px-8;
|
@apply inline-flex items-center justify-center space-x-2 px-4 py-2 md:px-8;
|
||||||
@apply uppercase leading-none;
|
@apply uppercase leading-none;
|
||||||
@apply transition-colors outline-none focus:outline-none;
|
@apply transition-colors outline-none focus:outline-none;
|
||||||
@apply bg-ep-secondary border-2 border-ep-secondary text-white;
|
@apply bg-ep-secondary border-2 border-ep-secondary text-white;
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
@apply font-bold;
|
@apply font-bold;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&.active {
|
&.button--active {
|
||||||
@apply bg-white border-ep-secondary text-ep-secondary;
|
@apply bg-white border-ep-secondary text-ep-secondary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@
|
|||||||
<span data-microtip-position="top"
|
<span data-microtip-position="top"
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
aria-label="auf die Merkliste setzen"
|
aria-label="auf die Merkliste setzen"
|
||||||
data-watchlist-toggle-target="label">
|
data-watchlist-toggle-target="ariaLabel">
|
||||||
<svg class="w-5 h-5 text-ep-secondary">
|
<svg class="w-5 h-5 text-ep-secondary">
|
||||||
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -0,0 +1,266 @@
|
|||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
|
||||||
|
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
|
||||||
|
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
|
||||||
|
|
||||||
|
<f:layout name="Default"/>
|
||||||
|
|
||||||
|
<f:section name="Main">
|
||||||
|
<div class="headerbar">
|
||||||
|
Buchungsoptionen {hotel.name}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-controller="pricetable">
|
||||||
|
|
||||||
|
<f:if condition="{hasSurcharge} || {hasDiscount}">
|
||||||
|
<div class="w-full flex justify-end mb-2">
|
||||||
|
<f:if condition="{hasSurcharge}">
|
||||||
|
<button data-action="pricetable#toggleSurcharge"
|
||||||
|
class="flex items-center space-x-1 focus:outline-none text-ep-primary">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-arrow-circle-right"></use>
|
||||||
|
</svg>
|
||||||
|
<span data-pricetable-target="surchargeToggleLabel"
|
||||||
|
data-label-default="Preise für Eigenanreise anzeigen"
|
||||||
|
data-label-alt="Preise für Busanreise anzeigen">
|
||||||
|
Preise für Eigenanreise anzeigen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{hasDiscount}">
|
||||||
|
<button data-action="pricetable#toggleDiscount"
|
||||||
|
class="flex items-center space-x-1 focus:outline-none text-ep-primary">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-arrow-circle-right"></use>
|
||||||
|
</svg>
|
||||||
|
<span data-pricetable-target="discountToggleLabel"
|
||||||
|
data-label-default="Preise für Busanreise anzeigen"
|
||||||
|
data-label-alt="Preise für Eigenanreise anzeigen">
|
||||||
|
Preise für Busanreise anzeigen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<div class="w-full max-w-full overflow-x-scroll mb-4">
|
||||||
|
<table class="min-w-full border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="p-1 text-left">
|
||||||
|
Termin
|
||||||
|
</th>
|
||||||
|
<th class="p-1 text-left">
|
||||||
|
<span data-microtip-position="bottom" aria-label="Nächte" role="tooltip">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-bed"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<th class="p-1 text-left whitespace-nowrap">
|
||||||
|
Preis ab
|
||||||
|
</th>
|
||||||
|
<th class="p-1 text-left">
|
||||||
|
inkl.
|
||||||
|
</th>
|
||||||
|
<th class="hidden lg:table-cell"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<f:for each="{dates}" as="row" iteration="iteration">
|
||||||
|
<tr class="even:bg-gray-200{f:if(condition: '{iteration.cycle} > 5', then: ' hidden')}"
|
||||||
|
data-pricetable-target="row">
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
<f:if condition="{bookable} && {row.available} && {row.showBookingButton}">
|
||||||
|
<f:then>
|
||||||
|
<button class="text-ep-primary-light"
|
||||||
|
data-action="product-details#loadPrices"
|
||||||
|
data-date-uid="{row.dateUid}">
|
||||||
|
{row.dateStart} - {row.dateEnd}
|
||||||
|
</button>
|
||||||
|
<div class="md:hidden">
|
||||||
|
<button class="button--small"
|
||||||
|
data-action="product-details#loadPrices"
|
||||||
|
data-date-uid="{row.dateUid}">
|
||||||
|
Details
|
||||||
|
</button>
|
||||||
|
<a href="{row.bookingUrl}" target="_blank"
|
||||||
|
class="button button--small bg-green-500 border-green-500">
|
||||||
|
Buchen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<span>
|
||||||
|
{row.dateStart} - {row.dateEnd}
|
||||||
|
</span>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
{row.nights}
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
<f:if condition="{row.pseudoPrice}">
|
||||||
|
<span class="text-sm line-through">
|
||||||
|
{row.pseudoPrice} €
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
<span class="font-bold"
|
||||||
|
data-pricetable-target="price"
|
||||||
|
data-room="{row.minPrice}"
|
||||||
|
data-bus="{row.busPrice}"
|
||||||
|
data-discount="{row.discount}">
|
||||||
|
{row.minPrice} €
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
<div class="hidden md:flex items-center space-x-2">
|
||||||
|
<f:if condition="{row.busIncluded}">
|
||||||
|
<span data-microtip-position="top"
|
||||||
|
aria-label="Busfahrt"
|
||||||
|
role="tooltip"
|
||||||
|
data-pricetable-target="busIcon"
|
||||||
|
data-bus-included="{row.busIncluded}"
|
||||||
|
data-bus-price="{row.busPrice}">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-bus"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{row.skipassIncluded}">
|
||||||
|
<span data-microtip-position="top"
|
||||||
|
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
|
||||||
|
role="tooltip">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-tag"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
<button class="button button--light button--small hidden md:block"
|
||||||
|
data-action="pricetable#openModal"
|
||||||
|
data-type="services"
|
||||||
|
data-uid="{row.dateUid}"
|
||||||
|
data-microtip-position="top"
|
||||||
|
aria-label="Alle Inklusivleistungen anzeigen"
|
||||||
|
role="tooltip">
|
||||||
|
Leistungen
|
||||||
|
</button>
|
||||||
|
<button class="md:hidden">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-info-circle"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1 hidden sm:table-cell">
|
||||||
|
<button class="button button--small"
|
||||||
|
data-action="product-details#loadPrices"
|
||||||
|
data-date-uid="{row.dateUid}">
|
||||||
|
Details
|
||||||
|
</button>
|
||||||
|
<f:if condition="{row.available} && {row.showBookingButton}">
|
||||||
|
<a href="{row.bookingUrl}"
|
||||||
|
target="_blank"
|
||||||
|
class="button button--small bg-green-500 border-green-500">
|
||||||
|
Buchen
|
||||||
|
</a>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{row.showBookingButton}">
|
||||||
|
<f:else>
|
||||||
|
<a href="#"
|
||||||
|
class="button button--small bg-gray-300">
|
||||||
|
Reisen-Alert
|
||||||
|
</a>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{row.available} && {row.showBookingButton}">
|
||||||
|
<f:else>
|
||||||
|
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
|
||||||
|
ausgebucht
|
||||||
|
</span>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</f:for>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<f:if condition="{dates -> f:count()} > 5 || {altProductLink}">
|
||||||
|
<div class="w-full flex justify-between pb-4">
|
||||||
|
<f:if condition="{dates -> f:count()} > 5">
|
||||||
|
<button class="flex items-center space-x-1 focus:outline-none text-ep-primary"
|
||||||
|
data-action="pricetable#toggleRows">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-plus-circle"
|
||||||
|
data-pricetable-target="rowToggleIcon"></use>
|
||||||
|
</svg>
|
||||||
|
<span data-pricetable-target="rowToggleLabel">
|
||||||
|
Alle Termine anzeigen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{altProductLink}">
|
||||||
|
<a href="{altProductLink}"
|
||||||
|
title="{altLabel}"
|
||||||
|
class="flex items-center space-x-1 focus:outline-none text-ep-primary">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-arrow-circle-right"></use>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{altLabel}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden fixed inset-0 w-full h-full z-50"
|
||||||
|
data-controller="modal"
|
||||||
|
data-action="pricetable:services-modal:open@window->modal#open">
|
||||||
|
<div class="absolute inset-0 w-full h-full bg-overlay-black z-25 flex items-center justify-center"
|
||||||
|
data-action="click->modal#close"></div>
|
||||||
|
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white shadow w-full max-w-xl rounded">
|
||||||
|
<div class="shadow flex items-center justify-between p-4 bg-ep-primary text-white rounded-t">
|
||||||
|
<span class="text-xl font-bold">
|
||||||
|
Inklusivleistungen {hotel.name}
|
||||||
|
</span>
|
||||||
|
<button class="inline-block"
|
||||||
|
data-action="modal#close">
|
||||||
|
<svg class="w-8 h-8">
|
||||||
|
<use href="#icon-close"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="p-4">
|
||||||
|
<f:for each="{dates}" as="row">
|
||||||
|
<table class="hidden table table-striped"
|
||||||
|
data-modal-target="content"
|
||||||
|
data-uid="{row.dateUid}">
|
||||||
|
<tbody>
|
||||||
|
<f:for each="{row.servicesIncluded}" as="service">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<svg class="w-6 h-6 text-ep-primary">
|
||||||
|
<use href="#icon-check"></use>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{service}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</f:for>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:section>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
|
||||||
|
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
|
||||||
|
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
|
||||||
|
|
||||||
|
<f:layout name="Default"/>
|
||||||
|
|
||||||
|
<f:section name="Main">
|
||||||
|
<div class="headerbar flex items-center justify-between">
|
||||||
|
<span>Buchungsoptionen</span>
|
||||||
|
<span>{hotel.name} {dateRange}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-controller="pricetable"
|
||||||
|
data-pricetable-show-all-rows-value="true">
|
||||||
|
|
||||||
|
<f:if condition="{hasSurcharge} || {hasDiscount}">
|
||||||
|
<div class="w-full flex justify-end mb-2">
|
||||||
|
<f:if condition="{hasSurcharge}">
|
||||||
|
<button data-action="pricetable#toggleSurcharge"
|
||||||
|
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-arrow-circle-right"></use>
|
||||||
|
</svg>
|
||||||
|
<span data-pricetable-target="surchargeToggleLabel"
|
||||||
|
data-label-default="Preise für Eigenanreise anzeigen"
|
||||||
|
data-label-alt="Preise für Busanreise anzeigen">
|
||||||
|
Preise für Eigenanreise anzeigen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{hasDiscount}">
|
||||||
|
<button data-action="pricetable#toggleDiscount"
|
||||||
|
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-arrow-circle-right"></use>
|
||||||
|
</svg>
|
||||||
|
<span data-pricetable-target="discountToggleLabel"
|
||||||
|
data-label-default="Preise für Busanreise anzeigen"
|
||||||
|
data-label-alt="Preise für Eigenanreise anzeigen">
|
||||||
|
Preise für Busanreise anzeigen
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</f:if>
|
||||||
|
|
||||||
|
<div class="w-full max-w-full overflow-x-scroll mb-4">
|
||||||
|
<table class="min-w-full border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-left">
|
||||||
|
Zimmertyp
|
||||||
|
</th>
|
||||||
|
<th class="text-left">
|
||||||
|
Preis
|
||||||
|
</th>
|
||||||
|
<th class="text-left">
|
||||||
|
inkl.
|
||||||
|
</th>
|
||||||
|
<th></th>
|
||||||
|
<th class="hidden lg:table-cell"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<f:for each="{prices}" as="row">
|
||||||
|
<tr class="even:bg-gray-200"
|
||||||
|
data-pricetable-target="row">
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
<f:if condition="{row.showBookingButton} && {row.available}">
|
||||||
|
<f:then>
|
||||||
|
<a href="{row.bookingUrl}"
|
||||||
|
target="_blank">
|
||||||
|
{row.roomName}
|
||||||
|
</a>
|
||||||
|
<a class="button button--small button--action lg:hidden"
|
||||||
|
href="{row.bookingUrl}"
|
||||||
|
target="_blank">
|
||||||
|
Jetzt buchen
|
||||||
|
</a>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<span>
|
||||||
|
{row.roomName}
|
||||||
|
</span>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{row.available}">
|
||||||
|
<f:else>
|
||||||
|
<div class="lg:hidden">
|
||||||
|
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
|
||||||
|
ausgebucht
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1 whitespace-nowrap"
|
||||||
|
data-pricetable-target="price"
|
||||||
|
data-room="{row.minPrice}"
|
||||||
|
data-bus="{row.busPrice}"
|
||||||
|
data-discount="{row.discount}">
|
||||||
|
{row.minPrice} €
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
<div class="flex items-center space-x-1">
|
||||||
|
<f:if condition="{row.busIncluded}">
|
||||||
|
<span data-microtip-position="top"
|
||||||
|
aria-label="Busfahrt"
|
||||||
|
role="tooltip"
|
||||||
|
data-pricetable-target="busIcon"
|
||||||
|
data-bus-included="{row.busIncluded}"
|
||||||
|
data-bus-price="{row.busPrice}">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-bus"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
<f:if condition="{row.skipassIncluded}">
|
||||||
|
<span data-microtip-position="top"
|
||||||
|
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
|
||||||
|
role="tooltip">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-tag"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</f:if>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-1">
|
||||||
|
<f:if condition="{row.hasOptionalServices} && {row.available}">
|
||||||
|
<button class="hidden sm:block button button--light button--small"
|
||||||
|
data-action="pricetable#openModal"
|
||||||
|
data-type="options"
|
||||||
|
data-uid="{row.roomUid}"
|
||||||
|
data-microtip-position="top"
|
||||||
|
aria-label="Alle Zusatzleistungen anzeigen"
|
||||||
|
role="tooltip">
|
||||||
|
Optionen
|
||||||
|
</button>
|
||||||
|
<button class="sm:hidden text-ep-primary">
|
||||||
|
<svg class="w-4 h-4">
|
||||||
|
<use href="#icon-info-circle"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</f:if>
|
||||||
|
</td>
|
||||||
|
<td class="hidden lg:table-cell px-2 py-1">
|
||||||
|
<f:if condition="{row.isHideBookingButton}">
|
||||||
|
<f:then>
|
||||||
|
<a class="button button--small button--mute"
|
||||||
|
href="#">
|
||||||
|
Reisen-Alert
|
||||||
|
</a>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<f:if condition="{row.available}">
|
||||||
|
<f:then>
|
||||||
|
<a class="button button--small button--action"
|
||||||
|
href="{row.bookingUrl}" target="_blank">
|
||||||
|
Buchen
|
||||||
|
</a>
|
||||||
|
</f:then>
|
||||||
|
<f:else>
|
||||||
|
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
|
||||||
|
ausgebucht
|
||||||
|
</span>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</f:for>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<f:if condition="{isSingleDate}">
|
||||||
|
<f:else>
|
||||||
|
<button class="button button--small mb-4"
|
||||||
|
data-action="product-details#showDates">
|
||||||
|
<svg class="w-4 h-4 text-white">
|
||||||
|
<use href="#icon-chevron-left"></use>
|
||||||
|
</svg>
|
||||||
|
<span>zurück zur Terminübersicht</span>
|
||||||
|
</button>
|
||||||
|
</f:else>
|
||||||
|
</f:if>
|
||||||
|
<div class="headerbar">
|
||||||
|
Inklusivleistungen
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-200 p-4 mb-8">
|
||||||
|
<ul class="list-none m-0 p-0">
|
||||||
|
<f:for each="{servicesIncluded}" as="service">
|
||||||
|
<li class="flex items-center space-x-2">
|
||||||
|
<svg class="w-6 h-6 text-ep-primary">
|
||||||
|
<use href="#icon-check"></use>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{service}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</f:for>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden fixed inset-0 w-full h-full z-50"
|
||||||
|
data-controller="modal"
|
||||||
|
data-action="pricetable:options-modal:open@window->modal#open">
|
||||||
|
<div class="absolute inset-0 w-full h-full bg-overlay-black z-25 flex items-center justify-center"
|
||||||
|
data-action="click->modal#close"></div>
|
||||||
|
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white shadow w-full max-w-xl rounded">
|
||||||
|
<div class="absolute top-0 left-0 inset-x-0 shadow flex items-center justify-between p-4 bg-ep-primary text-white rounded-t">
|
||||||
|
<span class="text-xl font-bold">
|
||||||
|
Optionale Zusatzleistungen
|
||||||
|
</span>
|
||||||
|
<button class="inline-block"
|
||||||
|
data-action="modal#close">
|
||||||
|
<svg class="w-8 h-8">
|
||||||
|
<use href="#icon-close"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="h-128 p-4 mt-12 overflow-y-scroll">
|
||||||
|
<f:for each="{prices}" as="row">
|
||||||
|
<div class="hidden"
|
||||||
|
data-modal-target="content"
|
||||||
|
data-uid="{row.roomUid}">
|
||||||
|
<f:for each="{row.optionalServices}" as="section">
|
||||||
|
<div class="py-2 -mx-1">
|
||||||
|
<div class="font-bold text-xl px-1">
|
||||||
|
{section.label}
|
||||||
|
</div>
|
||||||
|
<f:for each="{section.services}" as="service">
|
||||||
|
<dl class="flex items-start justify-between p-1 hover:bg-gray-200">
|
||||||
|
<dt class="font-normal">
|
||||||
|
{service.label}:
|
||||||
|
</dt>
|
||||||
|
<dd class="whitespace-nowrap">
|
||||||
|
{service.price} €
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</f:for>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:section>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -9,54 +9,71 @@
|
|||||||
<article>
|
<article>
|
||||||
<f:render partial="Product/HeaderDetail" arguments="{_all}"/>
|
<f:render partial="Product/HeaderDetail" arguments="{_all}"/>
|
||||||
<div class="container"
|
<div class="container"
|
||||||
id="detail"
|
data-controller="product-details"
|
||||||
x-data='productDetail({
|
data-action="tabs:selected->product-details#loadDates"
|
||||||
uris: {
|
<f:if condition="{settings.hotelOnTop}">
|
||||||
dates: "<ep:uri.ajax action="dates" controller="AjaxDate" arguments="{product: product, hotel: hotel}" format="json" pageUid="{settings.defaultAjaxUid}"/>",
|
<f:else>
|
||||||
pricetable: "<ep:uri.ajax action="pricetable" controller="AjaxTable" arguments="{product: product, hotel: hotel}" format="html" pageUid="{settings.defaultAjaxUid}"/>",
|
data-product-details-mode-value="dates"
|
||||||
travelAlert: "<f:uri.typolink parameter="{settings.travelAlertPageUid}"/>"
|
</f:else>
|
||||||
},
|
</f:if>
|
||||||
hotelOnTop: <f:if condition="{settings.hotelOnTop}"><f:then>true</f:then><f:else>false</f:else></f:if>
|
data-product-details-datestable-uri-value="{ep:uri.ajax(action: 'datestable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'html', pageUid: settings.defaultAjaxUid)}"
|
||||||
})'>
|
data-product-details-pricetable-uri-value="{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'html', pageUid: settings.defaultAjaxUid)}"
|
||||||
|
data-product-details-travel-alert-uri-value="{f:uri.typolink(parameter: settings.travelAlertPageUid)}">
|
||||||
<f:render partial="Header/Header" arguments="{layout: 8, subheader: '{product.region.name} / {product.name}', header: product.headline}"/>
|
<f:render partial="Header/Header" arguments="{layout: 8, subheader: '{product.region.name} / {product.name}', header: product.headline}"/>
|
||||||
<div class="flex flex-col lg:flex-row-reverse lg:items-start">
|
<div class="flex flex-col lg:flex-row-reverse lg:items-start"
|
||||||
|
data-controller="tabs">
|
||||||
<div class="w-full lg:w-1/3">
|
<div class="w-full lg:w-1/3">
|
||||||
<div class="lg:pl-4">
|
<div class="lg:pl-4">
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<f:if condition="{settings.hotelOnTop}">
|
<f:if condition="{settings.hotelOnTop}">
|
||||||
<f:then>
|
<f:then>
|
||||||
|
<button class="button button--light button--active col-span-1 md:col-span-2"
|
||||||
|
data-action="tabs#select"
|
||||||
|
data-tabs-target="button"
|
||||||
|
data-tab="hotel">
|
||||||
|
Unterkunft
|
||||||
|
</button>
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
<button class="button button--light col-span-1 md:col-span-2"
|
||||||
x-bind:class="{ 'button--active': show === 'hotel'}"
|
data-action="tabs#select"
|
||||||
x-on:click.prevent="show = 'hotel'">Unterkunft</button>
|
data-tabs-target="button"
|
||||||
|
data-tab="dates">
|
||||||
|
Termine/Preise
|
||||||
|
</button>
|
||||||
</f:then>
|
</f:then>
|
||||||
<f:else>
|
<f:else>
|
||||||
|
<button class="button button--light button--active col-span-1 md:col-span-2"
|
||||||
|
data-action="tabs#select"
|
||||||
|
data-tabs-target="button"
|
||||||
|
data-tab="dates">
|
||||||
|
Termine/Preise
|
||||||
|
</button>
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
<button class="button button--light col-span-1 md:col-span-2"
|
||||||
x-bind:class="{ 'button--active': show === 'dates'}"
|
data-action="tabs#select"
|
||||||
x-on:click.prevent="show = 'dates'">Termine/Preise</button>
|
data-tabs-target="button"
|
||||||
</f:else>
|
data-tab="hotel">
|
||||||
</f:if>
|
Unterkunft
|
||||||
<f:if condition="{settings.hotelOnTop}">
|
</button>
|
||||||
<f:then>
|
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
|
||||||
x-bind:class="{ 'button--active': show === 'dates'}"
|
|
||||||
x-on:click.prevent="show = 'dates'">Termine/Preise</button>
|
|
||||||
</f:then>
|
|
||||||
<f:else>
|
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
|
||||||
x-bind:class="{ 'button--active': show === 'hotel'}"
|
|
||||||
x-on:click.prevent="show = 'hotel'">Unterkunft</button>
|
|
||||||
</f:else>
|
</f:else>
|
||||||
</f:if>
|
</f:if>
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
<button class="button button--light col-span-1 md:col-span-2"
|
||||||
x-bind:class="{ 'button--active': show === 'region'}"
|
data-action="tabs#select"
|
||||||
x-on:click.prevent="show = 'region'">Gebiet</button>
|
data-tabs-target="button"
|
||||||
|
data-tab="region">
|
||||||
|
Gebiet
|
||||||
|
</button>
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
<button class="button button--light col-span-1 md:col-span-2"
|
||||||
x-bind:class="{ 'button--active': show === 'city'}"
|
data-action="tabs#select"
|
||||||
x-on:click.prevent="show = 'city'">Ort</button>
|
data-tabs-target="button"
|
||||||
|
data-tab="city">
|
||||||
|
Ort
|
||||||
|
</button>
|
||||||
<f:if condition="{product.journey}">
|
<f:if condition="{product.journey}">
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
<button class="button button--light col-span-1 md:col-span-2"
|
||||||
x-bind:class="{ 'button--active': show === 'journey'}"
|
data-action="tabs#select"
|
||||||
x-on:click.prevent="show = 'journey'">Anreise</button>
|
data-tabs-target="button"
|
||||||
|
data-tab="journey">
|
||||||
|
Anreise
|
||||||
|
</button>
|
||||||
</f:if>
|
</f:if>
|
||||||
<f:if condition="{product.faqs}">
|
<f:if condition="{product.faqs}">
|
||||||
<button class="button button--light col-span-1 md:col-span-2"
|
<button class="button button--light col-span-1 md:col-span-2"
|
||||||
@@ -68,17 +85,17 @@
|
|||||||
</f:if>
|
</f:if>
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<p class="mb-4">
|
<p class="mb-4">
|
||||||
<button class="flex items-center space-x-2"
|
<button class="button button--light w-full"
|
||||||
data-controller="watchlist-toggle"
|
data-controller="watchlist-toggle"
|
||||||
data-watchlist-toggle-key-value="{hotel.uid}:{product.uid}"
|
data-watchlist-toggle-key-value="{hotel.uid}:{product.uid}"
|
||||||
data-action="watchlist-toggle#toggle">
|
data-action="watchlist-toggle#toggle">
|
||||||
<span data-microtip-position="top" aria-label="" role="tooltip" data-watchlist-toggle-target="label">
|
<svg class="w-5 h-5">
|
||||||
<svg class="w-5 h-5">
|
<use href="#icon-plus-circle"
|
||||||
<use href="#icon-plus-circle"
|
data-watchlist-toggle-target="icon"></use>
|
||||||
data-watchlist-toggle-target="icon"></use>
|
</svg>
|
||||||
</svg>
|
<span data-watchlist-toggle-target="label">
|
||||||
|
Auf die Merkliste setzen
|
||||||
</span>
|
</span>
|
||||||
<span></span>
|
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<p class="mb-4">
|
<p class="mb-4">
|
||||||
@@ -98,17 +115,18 @@
|
|||||||
<div id="main" class="w-full lg:w-2/3">
|
<div id="main" class="w-full lg:w-2/3">
|
||||||
<div class="lg:pr-4">
|
<div class="lg:pr-4">
|
||||||
<!-- Loader -->
|
<!-- Loader -->
|
||||||
<div x-show="loading"
|
<div class="flex items-center space-x-2 mb-4 hidden border border-ep-primary p-2"
|
||||||
x-cloak
|
data-product-details-target="loadingIndicator">
|
||||||
class="flex items-center space-x-2 mb-4">
|
|
||||||
<f:image class="block w-4 h-4" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
<f:image class="block w-4 h-4" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
||||||
<span>Loading...</span>
|
<span data-product-details-target="loadingMessage">
|
||||||
|
Loading...
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- /Loader -->
|
<!-- /Loader -->
|
||||||
<!-- Dates -->
|
<!-- Dates -->
|
||||||
<div x-show="show === 'dates' && !loading"
|
<div class="{f:if(condition: settings.hotelOnTop, then: 'hidden')}"
|
||||||
x-transition.opacity
|
data-tabs-target="tab"
|
||||||
x-cloak>
|
data-tab="dates">
|
||||||
<f:if condition="{isProductWithExcludedConcept}">
|
<f:if condition="{isProductWithExcludedConcept}">
|
||||||
<f:if condition="{product.region.season} == 'w'">
|
<f:if condition="{product.region.season} == 'w'">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
@@ -125,12 +143,8 @@
|
|||||||
<f:render section="DaytripDateSelect" arguments="{_all}"/>
|
<f:render section="DaytripDateSelect" arguments="{_all}"/>
|
||||||
</f:then>
|
</f:then>
|
||||||
<f:else>
|
<f:else>
|
||||||
<div x-show="tableMode === 'dates'">
|
<div class="hidden" data-product-details-target="datesTableContainer"></div>
|
||||||
<f:render section="DatesTable" arguments="{_all}"/>
|
<div class="hidden" data-product-details-target="priceTableContainer"></div>
|
||||||
</div>
|
|
||||||
<div x-show="tableMode === 'prices'">
|
|
||||||
<f:render section="PriceTable" arguments="{_all}"/>
|
|
||||||
</div>
|
|
||||||
<f:if condition="{isProductWithNoInfoConcept}">
|
<f:if condition="{isProductWithNoInfoConcept}">
|
||||||
<f:else>
|
<f:else>
|
||||||
<template x-if="!available">
|
<template x-if="!available">
|
||||||
@@ -140,28 +154,34 @@
|
|||||||
</f:if>
|
</f:if>
|
||||||
</f:else>
|
</f:else>
|
||||||
</f:if>
|
</f:if>
|
||||||
</div>
|
|
||||||
<!-- /Dates -->
|
|
||||||
<div x-show="show === 'dates' || show === 'prices'" x-cloak>
|
|
||||||
<f:render partial="Product/Details" arguments="{_all}"/>
|
<f:render partial="Product/Details" arguments="{_all}"/>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- /Dates -->
|
||||||
<!-- Hotel -->
|
<!-- Hotel -->
|
||||||
<div x-show="show === 'hotel'" x-transition.opacity x-cloak>
|
<div class="{f:if(condition: settings.hotelOnTop, else: 'hidden')}"
|
||||||
|
data-tabs-target="tab"
|
||||||
|
data-tab="hotel">
|
||||||
<f:render partial="Hotel/Details" arguments="{hotel: hotel, bars: 1, showNonBookableHint: 0}"/>
|
<f:render partial="Hotel/Details" arguments="{hotel: hotel, bars: 1, showNonBookableHint: 0}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- /Hotel -->
|
<!-- /Hotel -->
|
||||||
<!-- City -->
|
<!-- City -->
|
||||||
<div x-show="show === 'city'" x-transition.opacity x-cloak>
|
<div class="hidden"
|
||||||
|
data-tabs-target="tab"
|
||||||
|
data-tab="city">
|
||||||
<f:render partial="City/Details" arguments="{city: product.city, bars: 1}"/>
|
<f:render partial="City/Details" arguments="{city: product.city, bars: 1}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- /City -->
|
<!-- /City -->
|
||||||
<!-- Region -->
|
<!-- Region -->
|
||||||
<div x-show="show === 'region'" x-transition.opacity x-cloak>
|
<div class="hidden"
|
||||||
|
data-tabs-target="tab"
|
||||||
|
data-tab="region">
|
||||||
<f:render partial="Region/Details" arguments="{region: product.region, bars: 1}"/>
|
<f:render partial="Region/Details" arguments="{region: product.region, bars: 1}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- /Region -->
|
<!-- /Region -->
|
||||||
<!-- Journey -->
|
<!-- Journey -->
|
||||||
<div x-show="show === 'journey'" x-transition.opacity x-cloak>
|
<div class="hidden"
|
||||||
|
data-tabs-target="tab"
|
||||||
|
data-tab="journey">
|
||||||
<f:render partial="Journey/Details" arguments="{journey: product.journey, bars: 1}"/>
|
<f:render partial="Journey/Details" arguments="{journey: product.journey, bars: 1}"/>
|
||||||
</div>
|
</div>
|
||||||
<!-- /Journey -->
|
<!-- /Journey -->
|
||||||
@@ -173,8 +193,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<div x-data x-init='fbq("track", "ViewContent", { "content_ids": [ {product.busProId -> f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" })'></div>
|
<div data-controller="fbpixel"
|
||||||
<f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/>
|
data-fbpixel-props-value='{ "content_ids": [ {product.busProId -> f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
|
||||||
|
<div id="faq">
|
||||||
|
<f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/>
|
||||||
|
</div>
|
||||||
</f:section>
|
</f:section>
|
||||||
|
|
||||||
<f:section name="Sidebar">
|
<f:section name="Sidebar">
|
||||||
@@ -219,360 +242,8 @@
|
|||||||
</f:if>
|
</f:if>
|
||||||
</f:section>
|
</f:section>
|
||||||
|
|
||||||
<f:section name="DatesTable">
|
|
||||||
<div x-show="bookable"
|
|
||||||
x-cloak>
|
|
||||||
<div class="headerbar">
|
|
||||||
Buchungsoptionen {hotel.name}
|
|
||||||
</div>
|
|
||||||
<f:render section="PriceToggle"/>
|
|
||||||
<div class="w-full max-w-full overflow-x-scroll mb-4">
|
|
||||||
<table class="min-w-full border-collapse">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="p-1 text-left">
|
|
||||||
Termin
|
|
||||||
</th>
|
|
||||||
<th class="p-1 text-left">
|
|
||||||
<span data-microtip-position="bottom" aria-label="Nächte" role="tooltip">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-bed"></use>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
<th class="p-1 text-left whitespace-nowrap">
|
|
||||||
Preis ab
|
|
||||||
</th>
|
|
||||||
<th class="p-1 text-left">
|
|
||||||
inkl.
|
|
||||||
</th>
|
|
||||||
<th class="hidden lg:table-cell"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<template x-for="(row, index) in datesRows" x-bind:key="row.dateUid">
|
|
||||||
<tr class="even:bg-gray-200" x-bind:class="{ 'hidden': index > 4 && !showAll }">
|
|
||||||
<td class="px-2 py-1">
|
|
||||||
<template x-if="bookable && row.available && !row.isHideBookingButton">
|
|
||||||
<a class="text-ep-primary-light" href="#"
|
|
||||||
x-on:click.prevent="loadPrices(row.dateUid)"
|
|
||||||
x-text="row.dateStart + ' - ' + row.dateEnd"></a>
|
|
||||||
<div class="md:hidden">
|
|
||||||
<button x-on:click.prevent="loadPrices(row.dateUid)" class="button button--small">
|
|
||||||
Details
|
|
||||||
</button>
|
|
||||||
<a x-bind:href="row.bookingUrl" target="_blank" class="button button--small bg-green-500 border-green-500">
|
|
||||||
Buchen
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template x-if="!bookable || !row.available || row.isHideBookingButton">
|
|
||||||
<span x-text="row.dateStart + ' - ' + row.dateEnd"></span>
|
|
||||||
</template>
|
|
||||||
</td>
|
|
||||||
<td class="px-2 py-1"
|
|
||||||
x-text="row.dateNights"></td>
|
|
||||||
<td class="px-2 py-1">
|
|
||||||
<span class="text-sm line-through" x-show="row.datePseudoPrice" x-text="row.datePseudoPrice + ' €'"></span>
|
|
||||||
<strong x-text="calculatePrice(row.dateMinPrice, row.dateBusPrice, row.dateDiscount) + ' €'"></strong>
|
|
||||||
</td>
|
|
||||||
<td class="px-2 py-1">
|
|
||||||
<div class="hidden md:flex items-center space-x-2">
|
|
||||||
<div x-show="showBusIcon(row.dateBusPrice, row.dateBusIncluded)" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-bus"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div x-show="row.dateSkipassIncluded" data-microtip-position="top" x-bind:aria-label="row.season === 's' ? 'Bergbahnticket inklusive' : 'Skipass inklusive'" role="tooltip">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-tag"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<button class="button button--light button--small hidden md:block"
|
|
||||||
data-microtip-position="top" aria-label="Alle Inklusivleistungen anzeigen" role="tooltip">
|
|
||||||
Leistungen
|
|
||||||
</button>
|
|
||||||
<button class="md:hidden">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-info-circle"></use>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-2 py-1 hidden sm:table-cell">
|
|
||||||
<button x-show="row.available || row.isHideBookingButton" class="button button--small"
|
|
||||||
x-on:click.prevent="loadPrices(row.dateUid)">
|
|
||||||
Details
|
|
||||||
</button>
|
|
||||||
<a x-show="row.available && !row.isHideBookingButton" x-bind:href="row.bookingUrl"
|
|
||||||
target="_blank"
|
|
||||||
class="button button--small bg-green-500 border-green-500">
|
|
||||||
Buchen
|
|
||||||
</a>
|
|
||||||
<a x-show="row.isHideBookingButton" x-bind:href="uris.travelAlertUrl"
|
|
||||||
class="button button--small bg-gray-300">
|
|
||||||
Reisen-Alert
|
|
||||||
</a>
|
|
||||||
<span x-show="!row.available && !row.isHideBookingButton"
|
|
||||||
class="inline-block px-2 mx-1 text-xs text-gray-500">
|
|
||||||
ausgebucht
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<template x-if="datesRows.length > 5 || altProductLink">
|
|
||||||
<div class="grid grid-cols-2 gap-8 pb-4">
|
|
||||||
<button x-show="datesRows.length > 5"
|
|
||||||
x-on:click.prevent="showAll = !showAll"
|
|
||||||
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use x-bind:href="showAll ? '#icon-minus-circle' : '#icon-plus-circle'"></use>
|
|
||||||
</svg>
|
|
||||||
<span x-text="showAll ? 'Weniger Termine anzeigen' : 'Alle Termine anzeigen'"></span>
|
|
||||||
</button>
|
|
||||||
<template x-if="altProductLink">
|
|
||||||
<a x-bind:href="altProductLink"
|
|
||||||
x-bind:title="altLabel"
|
|
||||||
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-arrow-circle-right"></use>
|
|
||||||
</svg>
|
|
||||||
<span x-text="altLabel"></span>
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template x-for="row in datesRows">
|
|
||||||
<div x-bind:id="'services-' + row.dateUid"
|
|
||||||
x-transition:enter="transition duration-200 ease-in-out"
|
|
||||||
x-transition:enter-start="opacity-0"
|
|
||||||
x-transition:enter-end="opacity-100"
|
|
||||||
x-transition:leave="transition duration-200 ease-in-out"
|
|
||||||
x-transition:leave-start="opacity-100"
|
|
||||||
x-transition:leave-end="opacity-0"
|
|
||||||
x-cloak
|
|
||||||
class="hidden fixed inset-0 w-full h-full z-50">
|
|
||||||
<div class="absolute inset-0 w-full h-full bg-overlay-black"></div>
|
|
||||||
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white p-8 shadow w-full max-w-3xl rounded">
|
|
||||||
<div class="flex justify-between border-b border-gray-200 pb-2 mb-4">
|
|
||||||
<span class="text-lg font-bold">
|
|
||||||
Inklusivleistungen
|
|
||||||
</span>
|
|
||||||
<button class="inline-block">
|
|
||||||
<svg class="w-8 h-8">
|
|
||||||
<use href="#icon-close"></use>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tbody>
|
|
||||||
<template x-for="service in row.dateServicesIncluded">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
|
||||||
<span x-text="service"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</f:section>
|
|
||||||
|
|
||||||
<f:section name="PriceTable">
|
|
||||||
<div class="headerbar flex items-center justify-between">
|
|
||||||
<span>Buchungsoptionen</span>
|
|
||||||
<span x-text="dateRange"></span>
|
|
||||||
</div>
|
|
||||||
<f:render section="PriceToggle"/>
|
|
||||||
<div class="w-full max-w-full overflow-x-scroll mb-4">
|
|
||||||
<table class="min-w-full border-collapse">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-left">
|
|
||||||
Zimmertyp
|
|
||||||
</th>
|
|
||||||
<th class="text-left">
|
|
||||||
Preis
|
|
||||||
</th>
|
|
||||||
<th class="text-left">
|
|
||||||
inkl.
|
|
||||||
</th>
|
|
||||||
<th></th>
|
|
||||||
<th class="hidden lg:table-cell"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<template x-for="(row, index) in pricesRows" x-bind:key="row.roomBusProId">
|
|
||||||
<tr class="even:bg-gray-200">
|
|
||||||
<td class="px-2 py-1">
|
|
||||||
<span x-show="row.isHideBookingButton || !row.roomAvailable" x-text="row.roomName"></span>
|
|
||||||
<a x-show="!row.isHideBookingButton && row.roomAvailable"
|
|
||||||
x-bind:href="row.bookingUrl" target="_blank"
|
|
||||||
x-text="row.roomName"></a>
|
|
||||||
<a class="button button--small button--action lg:hidden"
|
|
||||||
x-show="!row.isHideBookingButton && row.roomAvailable"
|
|
||||||
x-bind:href="row.bookingUrl" target="_blank">
|
|
||||||
Jetzt buchen
|
|
||||||
</a>
|
|
||||||
<div class="lg:hidden"
|
|
||||||
x-show="!row.roomAvailable">
|
|
||||||
<span class="inline-block px-2 mx-1 text-xs text-gray-500">
|
|
||||||
ausgebucht
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-2 py-1 whitespace-nowrap" x-bind:class="!row.roomAvailable && !row.isHideBookingButton && 'line-through'"
|
|
||||||
x-text="calculatePrice(row.roomPrice, row.roomBusPrice, row.roomDiscount) + ' €'"></td>
|
|
||||||
<td class="px-2 py-1">
|
|
||||||
<div class="flex items-center space-x-1">
|
|
||||||
<span x-show="showBusIcon(row.roomBusPrice, row.dateBusIncluded)" data-microtip-position="top" aria-label="Busfahrt inklusive" role="tooltip">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-bus"></use>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span x-show="row.dateSkipassIncluded" data-microtip-position="top" x-bind:aria-label="row.season === 's' ? 'Bergbahnticket inklusive' : 'Skipass inklusive'" role="tooltip">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-tag"></use>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-2 py-1">
|
|
||||||
<button x-show="row.roomHasOptionalServices && row.roomAvailable"
|
|
||||||
class="hidden sm:block button button--light button--small"
|
|
||||||
data-microtip-position="top" aria-label="Alle Zusatzleistungen anzeigen" role="tooltip">
|
|
||||||
Optionen
|
|
||||||
</button>
|
|
||||||
<button x-show="row.roomHasOptionalServices && row.roomAvailable"
|
|
||||||
class="sm:hidden ep:text-ep-primary">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-info-circle"></use>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td class="hidden lg:table-cell px-2 py-1">
|
|
||||||
<a class="button button--small button--action"
|
|
||||||
x-show="row.roomAvailable && !row.isHideBookingButton"
|
|
||||||
x-bind:href="row.bookingUrl" target="_blank">
|
|
||||||
Buchen
|
|
||||||
</a>
|
|
||||||
<a class="button button--small button--mute"
|
|
||||||
x-show="row.isHideBookingButton"
|
|
||||||
x-bind:href="uris.travelAlertUrl">
|
|
||||||
Reisen-Alert
|
|
||||||
</a>
|
|
||||||
<span x-show="!row.roomAvailable && !row.isHideBookingButton"
|
|
||||||
class="inline-block px-2 mx-1 text-xs text-gray-500">
|
|
||||||
ausgebucht
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<template x-for="row in pricesRows">
|
|
||||||
<div x-bind:id="'optional-services-' + row.roomUid"
|
|
||||||
x-transition:enter="transition duration-200 ease-in-out"
|
|
||||||
x-transition:enter-start="opacity-0"
|
|
||||||
x-transition:enter-end="opacity-100"
|
|
||||||
x-transition:leave="transition duration-200 ease-in-out"
|
|
||||||
x-transition:leave-start="opacity-100"
|
|
||||||
x-transition:leave-end="opacity-0"
|
|
||||||
class="hidden fixed inset-0 w-full h-full z-50">
|
|
||||||
<div class="absolute inset-0 w-full h-full bg-overlay-black z-25 flex items-center justify-center"></div>
|
|
||||||
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 bg-white p-8 shadow w-full max-w-3xl h-128 overflow-y-scroll rounded">
|
|
||||||
<div class="flex justify-between pb-2 mb-4">
|
|
||||||
<span class="text-2xl font-bold">
|
|
||||||
Optionale Zusatzleistungen
|
|
||||||
</span>
|
|
||||||
<button class="inline-block">
|
|
||||||
<svg class="w-8 h-8">
|
|
||||||
<use href="#icon-close"></use>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<template x-for="section in row.roomOptionalServices">
|
|
||||||
<div class="py-2 -mx-1">
|
|
||||||
<div class="font-bold text-xl px-1" x-text="section.label"></div>
|
|
||||||
<template x-for="optionalService in section.services">
|
|
||||||
<dl class="flex items-start justify-between p-1 hover:bg-gray-200">
|
|
||||||
<dt class="font-normal" x-text="optionalService.label + ':'"></dt>
|
|
||||||
<dd class="whitespace-nowrap" x-text="optionalService.price + ' €'"></dd>
|
|
||||||
</dl>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template x-if="!singleDate">
|
|
||||||
<button class="button button--small mb-4"
|
|
||||||
x-on:click.prevent="tableMode = 'dates'">
|
|
||||||
<svg class="w-4 h-4 text-white">
|
|
||||||
<use href="#icon-chevron-left"></use>
|
|
||||||
</svg>
|
|
||||||
<span>zurück zur Terminübersicht</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
<div class="headerbar">
|
|
||||||
Inklusivleistungen
|
|
||||||
</div>
|
|
||||||
<div class="bg-gray-200 p-4 mb-8">
|
|
||||||
<ul class="list-none m-0 p-0">
|
|
||||||
<template x-for="service in servicesIncluded">
|
|
||||||
<li class="flex items-center space-x-2">
|
|
||||||
<svg class="w-6 h-6 text-ep-primary">
|
|
||||||
<use href="#icon-check"></use>
|
|
||||||
</svg>
|
|
||||||
<span x-text="service"></span>
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</f:section>
|
|
||||||
|
|
||||||
<f:section name="PriceToggle">
|
|
||||||
<div class="w-full flex justify-end mb-2" x-show="hasSurcharge || hasDiscount">
|
|
||||||
<template x-if="hasSurcharge">
|
|
||||||
<button x-on:click.prevent="addBus = !addBus"
|
|
||||||
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-arrow-circle-right"></use>
|
|
||||||
</svg>
|
|
||||||
<span x-show="addBus">
|
|
||||||
Preise für Eigenanreise anzeigen
|
|
||||||
</span>
|
|
||||||
<span x-show="! addBus">
|
|
||||||
Preise für Busanreise anzeigen
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
<template x-if="hasDiscount">
|
|
||||||
<button x-on:click.prevent="discountBus = !discountBus"
|
|
||||||
class="flex items-center space-x-1 focus:outline-none ep:text-ep-primary">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-arrow-circle-right"></use>
|
|
||||||
</svg>
|
|
||||||
<span x-show="discountBus">
|
|
||||||
Preise für Busanreise anzeigen
|
|
||||||
</span>
|
|
||||||
<span x-show="!discountBus">
|
|
||||||
Preise für Eigenanreise anzeigen
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</f:section>
|
|
||||||
|
|
||||||
<f:section name="DaytripDateSelect">
|
<f:section name="DaytripDateSelect">
|
||||||
|
<f:comment><!--
|
||||||
<div x-data='daytripDateSelect({
|
<div x-data='daytripDateSelect({
|
||||||
<f:format.raw>
|
<f:format.raw>
|
||||||
hotelName: "{hotel.name}",
|
hotelName: "{hotel.name}",
|
||||||
@@ -657,6 +328,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
--></f:comment>
|
||||||
</f:section>
|
</f:section>
|
||||||
|
|
||||||
<f:section name="Calendar">
|
<f:section name="Calendar">
|
||||||
@@ -673,23 +345,19 @@
|
|||||||
<span class="block font-bold mb-2">
|
<span class="block font-bold mb-2">
|
||||||
Belegungskalender
|
Belegungskalender
|
||||||
</span>
|
</span>
|
||||||
<div x-data='calendar({
|
<div data-controller="calendar"
|
||||||
<f:format.raw>
|
data-calendar-uri-value="{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, format: 'html')}"
|
||||||
url: "{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, format: 'html')}",
|
data-calendar-hotel-uid-value="{product.calendarHotel.uid}">
|
||||||
hotel: {product.calendarHotel.uid}
|
|
||||||
</f:format.raw>
|
|
||||||
})'>
|
|
||||||
<select class="w-full border-gray-400 focus:border-gray-400"
|
<select class="w-full border-gray-400 focus:border-gray-400"
|
||||||
x-model="selectedIndex"
|
data-action="calendar#selectMonth">
|
||||||
x-on:change="load">
|
|
||||||
<template x-for="(month, index) in selectableMonths">
|
<template x-for="(month, index) in selectableMonths">
|
||||||
<option x-bind:value="index" x-text="month.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'})"></option>
|
<option value="index" data-text="month.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'})"></option>
|
||||||
</template>
|
</template>
|
||||||
</select>
|
</select>
|
||||||
<div class="mt-4"
|
<div class="mt-4"
|
||||||
x-ref="calendarWrapper"
|
data-calendar-target="container"></div>
|
||||||
x-cloak></div>
|
<div class="absolute top-0 left-0 inset-0 w-full h-full bg-overlay-white"
|
||||||
<div class="absolute top-0 left-0 inset-0 w-full h-full bg-overlay-white" x-show="loading">
|
data-calendar-target="loadingIndicator">
|
||||||
<f:image class="absolute top-1/2 left-1/2 -translate-xy-1/2" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
<f:image class="absolute top-1/2 left-1/2 -translate-xy-1/2" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+39
-41
@@ -3,8 +3,6 @@ const tinycolor = require('tinycolor2')
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
purge: {
|
purge: {
|
||||||
mode: 'layers',
|
|
||||||
layers: ['components', 'utilities'],
|
|
||||||
content: [
|
content: [
|
||||||
'./public/typo3conf/ext/ep_theme/Configuration/TypoScript/**/*.typoscript',
|
'./public/typo3conf/ext/ep_theme/Configuration/TypoScript/**/*.typoscript',
|
||||||
'./public/typo3conf/ext/ep_theme/Resources/Private/Layouts/**/*.html',
|
'./public/typo3conf/ext/ep_theme/Resources/Private/Layouts/**/*.html',
|
||||||
@@ -34,27 +32,27 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
'overlay': {
|
overlay: {
|
||||||
'black': tinycolor('#000000').setAlpha(0.85).toRgbString(),
|
black: tinycolor('#000000').setAlpha(0.85).toRgbString(),
|
||||||
'white': tinycolor('#ffffff').setAlpha(0.85).toRgbString(),
|
white: tinycolor('#ffffff').setAlpha(0.85).toRgbString(),
|
||||||
},
|
},
|
||||||
'concept': {
|
concept: {
|
||||||
'default': '#005ca9',
|
default: '#005ca9',
|
||||||
'bdg': '#005ca9',
|
bdg: '#005ca9',
|
||||||
'skr': '#005ca9',
|
skr: '#005ca9',
|
||||||
'evt': '#ed5454',
|
evt: '#ed5454',
|
||||||
'fam': '#7ca7d5',
|
fam: '#7ca7d5',
|
||||||
'grp': '#ffa300',
|
grp: '#ffa300',
|
||||||
'prg': '#005ca9',
|
prg: '#005ca9',
|
||||||
'ind': '#009fe3',
|
ind: '#009fe3',
|
||||||
'wkd': '#44d04e',
|
wkd: '#44d04e',
|
||||||
'spz': '#436a8a',
|
spz: '#436a8a',
|
||||||
'epe': '#436a8a',
|
epe: '#436a8a',
|
||||||
'sfl': '#999',
|
sfl: '#999',
|
||||||
},
|
},
|
||||||
'ep': {
|
ep: {
|
||||||
'primary': '#3d8ccb',
|
primary: '#3d8ccb',
|
||||||
'secondary': '#f9c700',
|
secondary: '#f9c700',
|
||||||
'primary-light': '#009fff',
|
'primary-light': '#009fff',
|
||||||
'primary-medium': '#165883',
|
'primary-medium': '#165883',
|
||||||
'primary-dark': '#18527B',
|
'primary-dark': '#18527B',
|
||||||
@@ -64,25 +62,25 @@ module.exports = {
|
|||||||
'secondary-10': tinycolor('#f0bc44').setAlpha(0.1).toRgbString(),
|
'secondary-10': tinycolor('#f0bc44').setAlpha(0.1).toRgbString(),
|
||||||
'secondary-80': tinycolor('#f0bc44').setAlpha(0.8).toRgbString(),
|
'secondary-80': tinycolor('#f0bc44').setAlpha(0.8).toRgbString(),
|
||||||
},
|
},
|
||||||
'sbw': {
|
sbw: {
|
||||||
'primary': '#f0bc44',
|
primary: '#f0bc44',
|
||||||
'secondary': '#ee525a',
|
secondary: '#ee525a',
|
||||||
'primary-dark': '#c7a20c',
|
'primary-dark': '#c7a20c',
|
||||||
'primary-10': tinycolor('#f0bc44').setAlpha(0.1).toRgbString(),
|
'primary-10': tinycolor('#f0bc44').setAlpha(0.1).toRgbString(),
|
||||||
'primary-50': tinycolor('#f0bc44').setAlpha(0.5).toRgbString(),
|
'primary-50': tinycolor('#f0bc44').setAlpha(0.5).toRgbString(),
|
||||||
'primary-80': tinycolor('#f0bc44').setAlpha(0.8).toRgbString(),
|
'primary-80': tinycolor('#f0bc44').setAlpha(0.8).toRgbString(),
|
||||||
'overlay-secondary': tinycolor('#c20000').setAlpha(0.55).toRgbString(),
|
'overlay-secondary': tinycolor('#c20000').setAlpha(0.55).toRgbString(),
|
||||||
},
|
},
|
||||||
'snz': {
|
snz: {
|
||||||
'primary': '#82C3C6',
|
primary: '#82C3C6',
|
||||||
'secondary': '#f9c33d',
|
secondary: '#f9c33d',
|
||||||
'primary-10': tinycolor('#82C3C6').setAlpha(0.1).toRgbString(),
|
'primary-10': tinycolor('#82C3C6').setAlpha(0.1).toRgbString(),
|
||||||
'primary-50': tinycolor('#82C3C6').setAlpha(0.5).toRgbString(),
|
'primary-50': tinycolor('#82C3C6').setAlpha(0.5).toRgbString(),
|
||||||
'primary-80': tinycolor('#82C3C6').setAlpha(0.8).toRgbString(),
|
'primary-80': tinycolor('#82C3C6').setAlpha(0.8).toRgbString(),
|
||||||
},
|
},
|
||||||
'uch': {
|
uch: {
|
||||||
'primary': '#b5e400',
|
primary: '#b5e400',
|
||||||
'secondary': '#19a3dc',
|
secondary: '#19a3dc',
|
||||||
'primary-dark': '#b5e400',
|
'primary-dark': '#b5e400',
|
||||||
'primary-10': tinycolor('#b5e400').setAlpha(0.1).toRgbString(),
|
'primary-10': tinycolor('#b5e400').setAlpha(0.1).toRgbString(),
|
||||||
'primary-50': tinycolor('#b5e400').setAlpha(0.5).toRgbString(),
|
'primary-50': tinycolor('#b5e400').setAlpha(0.5).toRgbString(),
|
||||||
@@ -110,23 +108,23 @@ module.exports = {
|
|||||||
textColor: [ 'sbw', 'sbw-hover', 'snz', 'snz-hover', 'uch', 'uch-hover' ],
|
textColor: [ 'sbw', 'sbw-hover', 'snz', 'snz-hover', 'uch', 'uch-hover' ],
|
||||||
borderColor: [ 'sbw','snz', 'uch' ],
|
borderColor: [ 'sbw','snz', 'uch' ],
|
||||||
display: ['group-hover', 'sticky'],
|
display: ['group-hover', 'sticky'],
|
||||||
visibility: ['group-hover'],
|
visibility: [ 'group-hover' ],
|
||||||
opacity: ['group-hover'],
|
opacity: [ 'group-hover' ],
|
||||||
rotate: ['group-hover'],
|
rotate: [ 'group-hover' ],
|
||||||
flex: ['sticky'],
|
flex: [ 'sticky' ],
|
||||||
flexDirection: ['even', 'odd', 'sticky'],
|
flexDirection: [ 'even', 'odd', 'sticky' ],
|
||||||
alignItems: ['sticky'],
|
alignItems: [ 'sticky' ],
|
||||||
padding: ['even', 'odd', 'first', 'last', 'sticky'],
|
padding: [ 'even', 'odd', 'first', 'last', 'sticky' ],
|
||||||
margin: ['even', 'odd', 'first', 'last', 'sticky'],
|
margin: [ 'even', 'odd', 'first', 'last', 'sticky' ],
|
||||||
maxWidth: ['sticky'],
|
maxWidth: [ 'sticky' ],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
require('@tailwindcss/aspect-ratio'),
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
require('tailwindcss-alt')({
|
require('tailwindcss-alt')({
|
||||||
variants: ['sbw', 'snz', 'uch'],
|
variants: ['sbw', 'snz', 'uch'],
|
||||||
}),
|
}),
|
||||||
require('@tailwindcss/aspect-ratio'),
|
|
||||||
require('@tailwindcss/forms'),
|
|
||||||
plugin(function ({addVariant, e}) {
|
plugin(function ({addVariant, e}) {
|
||||||
addVariant('first-type', ({modifySelectors, separator}) => {
|
addVariant('first-type', ({modifySelectors, separator}) => {
|
||||||
modifySelectors(({className}) => {
|
modifySelectors(({className}) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user