WIP Migrate to stimulus.js and tailwind
This commit is contained in:
@@ -71,19 +71,6 @@ class AjaxContingentController extends ActionController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Hotel $hotel
|
|
||||||
* @param Product $product
|
|
||||||
* @return string
|
|
||||||
* @throws \Doctrine\DBAL\DBALException
|
|
||||||
*/
|
|
||||||
public function listAction(Hotel $hotel, Product $product)
|
|
||||||
{
|
|
||||||
$enabled = $this->contingentDataService->getAvailableContingents($hotel, $product);
|
|
||||||
|
|
||||||
return \json_encode($enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Hotel $hotel
|
* @param Hotel $hotel
|
||||||
* @param Product $product
|
* @param Product $product
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ 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\Domain\Repository\ProductRepository;
|
||||||
|
use EP\EpProducts\Service\ContingentDataService;
|
||||||
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;
|
||||||
@@ -65,22 +66,31 @@ class AjaxTableController extends ActionController
|
|||||||
protected $resellerDataService;
|
protected $resellerDataService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @var ContingentDataService
|
||||||
|
*/
|
||||||
|
protected $contingentDataService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ProductRepository $productRepository
|
||||||
* @param DateService $dateService
|
* @param DateService $dateService
|
||||||
* @param FilterService $filterService
|
* @param FilterService $filterService
|
||||||
* @param ResellerDataService $resellerDataService
|
* @param ResellerDataService $resellerDataService
|
||||||
|
* @param ContingentDataService $contingentDataService
|
||||||
*/
|
*/
|
||||||
public function __construct
|
public function __construct
|
||||||
(
|
(
|
||||||
ProductRepository $productRepository,
|
ProductRepository $productRepository,
|
||||||
DateService $dateService,
|
DateService $dateService,
|
||||||
FilterService $filterService,
|
FilterService $filterService,
|
||||||
ResellerDataService $resellerDataService
|
ResellerDataService $resellerDataService,
|
||||||
|
ContingentDataService $contingentDataService
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->productRepository = $productRepository;
|
$this->productRepository = $productRepository;
|
||||||
$this->dateService = $dateService;
|
$this->dateService = $dateService;
|
||||||
$this->filterService = $filterService;
|
$this->filterService = $filterService;
|
||||||
$this->resellerDataService = $resellerDataService;
|
$this->resellerDataService = $resellerDataService;
|
||||||
|
$this->contingentDataService = $contingentDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initializeDatesAction()
|
public function initializeDatesAction()
|
||||||
@@ -252,4 +262,20 @@ class AjaxTableController extends ActionController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Hotel $hotel
|
||||||
|
* @param Product $product
|
||||||
|
* @param string $dateFrom
|
||||||
|
* @param string $dateTo
|
||||||
|
*/
|
||||||
|
public function daytripRoomsAction(Hotel $hotel, Product $product, $dateFrom, $dateTo)
|
||||||
|
{
|
||||||
|
$template = $this->settings['bpnBookingUrlTemplateCode'];
|
||||||
|
$rooms = $this
|
||||||
|
->contingentDataService
|
||||||
|
->getAvailableRooms($dateFrom, $dateTo, $hotel, $product, $template);
|
||||||
|
|
||||||
|
$this->view->assign('rooms', $rooms);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use EP\EpProducts\Domain\Model\Hotel;
|
|||||||
use EP\EpProducts\Domain\Model\Product;
|
use EP\EpProducts\Domain\Model\Product;
|
||||||
use EP\EpProducts\Domain\Repository\HotelRepository;
|
use EP\EpProducts\Domain\Repository\HotelRepository;
|
||||||
use EP\EpProducts\Domain\Repository\ProductRepository;
|
use EP\EpProducts\Domain\Repository\ProductRepository;
|
||||||
|
use EP\EpProducts\Service\ContingentDataService;
|
||||||
use EP\EpProducts\Service\DateService;
|
use EP\EpProducts\Service\DateService;
|
||||||
use EP\EpProducts\Service\FilterService;
|
use EP\EpProducts\Service\FilterService;
|
||||||
use EP\EpProducts\Service\HotelDataService;
|
use EP\EpProducts\Service\HotelDataService;
|
||||||
@@ -70,12 +71,17 @@ class ProductController extends ActionController
|
|||||||
* @var HotelDataService
|
* @var HotelDataService
|
||||||
*/
|
*/
|
||||||
protected $hotelDataService;
|
protected $hotelDataService;
|
||||||
|
/**
|
||||||
|
* @var ContingentDataService
|
||||||
|
*/
|
||||||
|
private $contingentDataService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ProductRepository $productRepository
|
* @param ProductRepository $productRepository
|
||||||
* @param ProductDataService $productDataService
|
* @param ProductDataService $productDataService
|
||||||
* @param HotelRepository $hotelRepository
|
* @param HotelRepository $hotelRepository
|
||||||
* @param HotelDataService $hotelDataService
|
* @param HotelDataService $hotelDataService
|
||||||
|
* @param ContingentDataService $contingentDataService
|
||||||
* @param DateService $dateService
|
* @param DateService $dateService
|
||||||
* @param FilterService $filterService
|
* @param FilterService $filterService
|
||||||
*/
|
*/
|
||||||
@@ -84,6 +90,7 @@ class ProductController extends ActionController
|
|||||||
ProductDataService $productDataService,
|
ProductDataService $productDataService,
|
||||||
HotelRepository $hotelRepository,
|
HotelRepository $hotelRepository,
|
||||||
HotelDataService $hotelDataService,
|
HotelDataService $hotelDataService,
|
||||||
|
ContingentDataService $contingentDataService,
|
||||||
DateService $dateService,
|
DateService $dateService,
|
||||||
FilterService $filterService
|
FilterService $filterService
|
||||||
) {
|
) {
|
||||||
@@ -91,8 +98,9 @@ class ProductController extends ActionController
|
|||||||
$this->productRepository = $productRepository;
|
$this->productRepository = $productRepository;
|
||||||
$this->productDataService = $productDataService;
|
$this->productDataService = $productDataService;
|
||||||
$this->hotelRepository = $hotelRepository;
|
$this->hotelRepository = $hotelRepository;
|
||||||
$this->dateService = $dateService;
|
|
||||||
$this->hotelDataService = $hotelDataService;
|
$this->hotelDataService = $hotelDataService;
|
||||||
|
$this->contingentDataService = $contingentDataService;
|
||||||
|
$this->dateService = $dateService;
|
||||||
$this->filterService = $filterService;
|
$this->filterService = $filterService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +154,14 @@ class ProductController extends ActionController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($product->isDaytrip()) {
|
||||||
|
$availableContingents = $this
|
||||||
|
->contingentDataService
|
||||||
|
->getAvailableContingents($hotel, $product)
|
||||||
|
;
|
||||||
|
$enabledDates = array_keys($availableContingents);
|
||||||
|
}
|
||||||
|
|
||||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
||||||
$isProductWithExcludedConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(),
|
$isProductWithExcludedConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(),
|
||||||
$excludedConceptUids, false);
|
$excludedConceptUids, false);
|
||||||
@@ -157,29 +173,10 @@ class ProductController extends ActionController
|
|||||||
$backlinkUrl = GeneralUtility::_GET('ref');
|
$backlinkUrl = GeneralUtility::_GET('ref');
|
||||||
$backLinkLabel = strpos($backlinkUrl, 'suche') !== -1 ? 'zurück zum Suchergebnis' : 'zurück';
|
$backLinkLabel = strpos($backlinkUrl, 'suche') !== -1 ? 'zurück zum Suchergebnis' : 'zurück';
|
||||||
|
|
||||||
// Generate dates uri
|
|
||||||
$datesArguments = [
|
|
||||||
'dateFrom' => $dateFrom,
|
|
||||||
'dateTo' => $dateTo,
|
|
||||||
'product' => $product,
|
|
||||||
'hotel' => $hotel
|
|
||||||
];
|
|
||||||
$datesUri = $this->uriBuilder
|
|
||||||
->reset()
|
|
||||||
->setCreateAbsoluteUri(true)
|
|
||||||
->setTargetPageType(1702)
|
|
||||||
->setTargetPageUid(855)
|
|
||||||
->uriFor(
|
|
||||||
'dates',
|
|
||||||
$datesArguments,
|
|
||||||
'AjaxDate',
|
|
||||||
'EpProducts',
|
|
||||||
'ajax'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->view->assignMultiple([
|
$this->view->assignMultiple([
|
||||||
'hotel' => $hotel,
|
'hotel' => $hotel,
|
||||||
'product' => $product,
|
'product' => $product,
|
||||||
|
'enabledDates' => $enabledDates ?? [],
|
||||||
'isProductWithExcludedConcept' => $isProductWithExcludedConcept,
|
'isProductWithExcludedConcept' => $isProductWithExcludedConcept,
|
||||||
'isProductWithNoInfoConcept' => $isProductWithNoInfoConcept,
|
'isProductWithNoInfoConcept' => $isProductWithNoInfoConcept,
|
||||||
'currentPageUid' => $GLOBALS['TSFE']->id,
|
'currentPageUid' => $GLOBALS['TSFE']->id,
|
||||||
@@ -187,7 +184,6 @@ class ProductController extends ActionController
|
|||||||
'dateTo' => $dateTo,
|
'dateTo' => $dateTo,
|
||||||
'referringUrl' => $backlinkUrl,
|
'referringUrl' => $backlinkUrl,
|
||||||
'backlinkLabel' => $backLinkLabel,
|
'backlinkLabel' => $backLinkLabel,
|
||||||
'datesUri' => $datesUri,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,14 +140,14 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \DateTime $dateFrom
|
* @param string $dateFrom
|
||||||
* @param \DateTime $dateTo
|
* @param string $dateTo
|
||||||
* @param Hotel $hotel
|
* @param Hotel $hotel
|
||||||
* @param Product $product
|
* @param Product $product
|
||||||
* @return array
|
* @return array
|
||||||
* @throws DBALException
|
* @throws DBALException
|
||||||
*/
|
*/
|
||||||
public function getAvailableRooms(\DateTime $dateFrom, \DateTime $dateTo, Hotel $hotel, Product $product)
|
public function getAvailableRooms(string $dateFrom, string $dateTo, Hotel $hotel, Product $product)
|
||||||
{
|
{
|
||||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
|
|||||||
'c.additional_night_min_price as additionalNightPrice',
|
'c.additional_night_min_price as additionalNightPrice',
|
||||||
'c.additional_night_min_nights as additionalNightMinNights', 'd.bus_pro_id as busProId',
|
'c.additional_night_min_nights as additionalNightMinNights', 'd.bus_pro_id as busProId',
|
||||||
'd.services_included as servicesIncluded', 'd.services_general as servicesOptional',
|
'd.services_included as servicesIncluded', 'd.services_general as servicesOptional',
|
||||||
'd.skipass_included as skipassIncluded')
|
'd.skipass_included as skipassIncluded', 'd.season as season')
|
||||||
->from('tx_epproducts_domain_model_contingent', 'c')
|
->from('tx_epproducts_domain_model_contingent', 'c')
|
||||||
->innerJoin('c', 'tx_epproducts_domain_model_date', 'd', 'c.date = d.date_start AND c.hotel_code = d.hotel_code')
|
->innerJoin('c', 'tx_epproducts_domain_model_date', 'd', 'c.date = d.date_start AND c.hotel_code = d.hotel_code')
|
||||||
->where('c.hotel = :hotelUid')
|
->where('c.hotel = :hotelUid')
|
||||||
@@ -168,8 +168,8 @@ class ContingentRepository extends AbstractRepository implements ProviderInterfa
|
|||||||
->setParameters([
|
->setParameters([
|
||||||
'hotelUid' => $hotel->getUid(),
|
'hotelUid' => $hotel->getUid(),
|
||||||
'productUid' => $product->getUid(),
|
'productUid' => $product->getUid(),
|
||||||
'dateFrom' => $dateFrom->format('Y-m-d'),
|
'dateFrom' => $dateFrom,
|
||||||
'dateTo' => $dateTo->format('Y-m-d'),
|
'dateTo' => $dateTo,
|
||||||
])
|
])
|
||||||
->groupBy('c.room_code')
|
->groupBy('c.room_code')
|
||||||
->orderBy('c.pax')
|
->orderBy('c.pax')
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use EP\EpProducts\Domain\Model\Hotel;
|
|||||||
use EP\EpProducts\Domain\Model\Product;
|
use EP\EpProducts\Domain\Model\Product;
|
||||||
use EP\EpProducts\Domain\Repository\ContingentRepository;
|
use EP\EpProducts\Domain\Repository\ContingentRepository;
|
||||||
use EP\EpProducts\Utility\BookingUrlUtility;
|
use EP\EpProducts\Utility\BookingUrlUtility;
|
||||||
|
use League\Period\Period;
|
||||||
|
|
||||||
class ContingentDataService
|
class ContingentDataService
|
||||||
{
|
{
|
||||||
@@ -82,27 +83,32 @@ class ContingentDataService
|
|||||||
* @throws \Doctrine\DBAL\DBALException
|
* @throws \Doctrine\DBAL\DBALException
|
||||||
*/
|
*/
|
||||||
public function getAvailableRooms(
|
public function getAvailableRooms(
|
||||||
\DateTime $dateFrom,
|
string $dateFrom,
|
||||||
\DateTime $dateTo,
|
string $dateTo,
|
||||||
Hotel $hotel,
|
Hotel $hotel,
|
||||||
Product $product,
|
Product $product,
|
||||||
$template = 'base'
|
$template = 'base'
|
||||||
) {
|
) {
|
||||||
$data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product);
|
$data = $this->contingentRepository->getAvailableRooms($dateFrom, $dateTo, $hotel, $product);
|
||||||
|
$period = new Period($dateFrom, $dateTo);
|
||||||
|
$nights = $period->getDateInterval()->format('%a');
|
||||||
|
|
||||||
$rooms = [];
|
$rooms = [];
|
||||||
|
|
||||||
foreach ($data as $row)
|
foreach ($data as $row) {
|
||||||
{
|
|
||||||
$row['bookingUrl'] = BookingUrlUtility::generateUrl([
|
$row['bookingUrl'] = BookingUrlUtility::generateUrl([
|
||||||
'bookingId' => $row['busProId'],
|
'bookingId' => $row['busProId'],
|
||||||
'hotelId' => $row['hotelBusProId'],
|
'hotelId' => $row['hotelBusProId'],
|
||||||
'dateEnd' => $dateTo->format('Y-m-d'),
|
'dateEnd' => $dateTo,
|
||||||
'template' => $template,
|
'template' => $template,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$row['servicesIncluded'] = unserialize($row['servicesIncluded']);
|
$row['summer'] = $row['season'] === 's';
|
||||||
$row['servicesOptional'] = unserialize($row['servicesOptional']);
|
|
||||||
|
$row['servicesIncluded'] = unserialize($row['servicesIncluded'], ['allowed_classes' => false]);
|
||||||
|
$row['servicesOptional'] = unserialize($row['servicesOptional'], ['allowed_classes' => false]);
|
||||||
|
|
||||||
|
$row['priceForSelection'] = $row['price'] + ($nights - $row['minNights']) * $row['additionalNightPrice'];
|
||||||
|
|
||||||
$rooms[] = $row;
|
$rooms[] = $row;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ tx_epproducts_ajax_json {
|
|||||||
2 = pricetable
|
2 = pricetable
|
||||||
3 = pricetableHtml
|
3 = pricetableHtml
|
||||||
4 = eventPricetable
|
4 = eventPricetable
|
||||||
|
5 = daytripRooms
|
||||||
}
|
}
|
||||||
AjaxCalendar {
|
AjaxCalendar {
|
||||||
1 = range
|
1 = range
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ $iconRegistry->registerIcon(
|
|||||||
'AjaxSearch' => 'searchresult',
|
'AjaxSearch' => 'searchresult',
|
||||||
'AjaxDate' => 'dates',
|
'AjaxDate' => 'dates',
|
||||||
'AjaxContingent' => 'list,rooms',
|
'AjaxContingent' => 'list,rooms',
|
||||||
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable,daytripRooms',
|
||||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||||
'AjaxWatchlist' => 'list',
|
'AjaxWatchlist' => 'list',
|
||||||
'AjaxGroupsPrice' => 'processForm',
|
'AjaxGroupsPrice' => 'processForm',
|
||||||
|
|||||||
@@ -10,12 +10,8 @@ smoothscroll.polyfill()
|
|||||||
|
|
||||||
// Alpine stuff
|
// Alpine stuff
|
||||||
import Alpine from 'alpinejs'
|
import Alpine from 'alpinejs'
|
||||||
import daytripDateSelect from './components/daytrip-date-select'
|
|
||||||
import searchBar from './components/search-bar'
|
|
||||||
import contactForm from './components/contact-form'
|
import contactForm from './components/contact-form'
|
||||||
|
|
||||||
Alpine.data('daytripDateSelect', daytripDateSelect)
|
|
||||||
Alpine.data('searchBar', searchBar)
|
|
||||||
Alpine.data('contactForm', contactForm)
|
Alpine.data('contactForm', contactForm)
|
||||||
Alpine.start()
|
Alpine.start()
|
||||||
|
|
||||||
|
|||||||
-153
@@ -1,153 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import flatpickr from 'flatpickr'
|
|
||||||
import { German } from 'flatpickr/dist/l10n/de'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import 'dayjs/locale/de'
|
|
||||||
|
|
||||||
export default props => ({
|
|
||||||
hotelName: props.hotelName,
|
|
||||||
hotelUid: props.hotelUid,
|
|
||||||
productUid: props.productUid,
|
|
||||||
contingentEndpointUri: props.contingentEndpointUri,
|
|
||||||
roomsEndpointUri: props.roomsEndpointUri,
|
|
||||||
loading: false,
|
|
||||||
picker: null,
|
|
||||||
displayedFrom: dayjs().startOf('month'),
|
|
||||||
displayedTo: dayjs().endOf('month'),
|
|
||||||
selectedFrom: null,
|
|
||||||
selectedTo: null,
|
|
||||||
selectedNumberOfNights: 0,
|
|
||||||
minNightsForSelectedDate: 0,
|
|
||||||
availableDates: [],
|
|
||||||
availableRooms: [],
|
|
||||||
init() {
|
|
||||||
this.picker = flatpickr(this.$refs.field, {
|
|
||||||
mode: 'range',
|
|
||||||
dateFormat: 'Y-m-d',
|
|
||||||
locale: German,
|
|
||||||
inline: true,
|
|
||||||
onChange: selectedDates => {
|
|
||||||
this.availableRooms = []
|
|
||||||
if (selectedDates.length === 2) {
|
|
||||||
this.selectedFrom = dayjs(selectedDates[0])
|
|
||||||
this.selectedTo = dayjs(selectedDates[1])
|
|
||||||
this.selectedNumberOfNights = this.getSelectedNumberOfNights()
|
|
||||||
this.minNightsForSelectedDate = this.getMinNightsForSelectedDate()
|
|
||||||
if (this.selectedNumberOfNights >= this.minNightsForSelectedDate) {
|
|
||||||
this.fetchAvailableRooms()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDayCreate: (dObj, dStr, fp, dayElem) => {
|
|
||||||
const day = dayElem.dateObj.getFullYear() + '-' + ('0' + (dayElem.dateObj.getMonth()+1)).slice(-2)
|
|
||||||
+ '-' + ('0' + dayElem.dateObj.getDate()).slice(-2)
|
|
||||||
if (day in this.availableDates) {
|
|
||||||
dayElem.classList.add('available')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.fetchEnabledDates()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
headerLabel () {
|
|
||||||
let label = 'Buchungsoptionen'
|
|
||||||
if (this.hotelName) {
|
|
||||||
label += ' ' + this.hotelName
|
|
||||||
}
|
|
||||||
if (this.selectedFrom && this.selectedTo) {
|
|
||||||
label += ' ' + this.selectedFrom.format('DD.MM.YY') + ' - ' + this.selectedTo.format('DD.MM.YY')
|
|
||||||
label += ' (' + this.selectedNumberOfNights + ' Nächte)'
|
|
||||||
}
|
|
||||||
return label
|
|
||||||
},
|
|
||||||
message() {
|
|
||||||
if (this.loading) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
if (this.selectedFrom === null || this.selectedTo === null) {
|
|
||||||
return 'Bitte einen Zeitraum auswählen.'
|
|
||||||
}
|
|
||||||
if (this.selectedNumberOfNights < this.minNightsForSelectedDate) {
|
|
||||||
return 'Bitte mindestens ' + this.minNightsForSelectedDate + ' Nächte auswählen.'
|
|
||||||
}
|
|
||||||
if (this.availableRooms.length === 0) {
|
|
||||||
return 'Im gewählten Zeitraum sind leider keine Zimmer verfügbar.'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
},
|
|
||||||
onDateSelected(date) {
|
|
||||||
this.selectedDate = dayjs(date)
|
|
||||||
},
|
|
||||||
fetchEnabledDates() {
|
|
||||||
this.loading = true
|
|
||||||
const data = new FormData()
|
|
||||||
data.set('tx_epproducts_ajax[hotel]', this.hotelUid)
|
|
||||||
data.set('tx_epproducts_ajax[product]', this.productUid)
|
|
||||||
axios({
|
|
||||||
url: this.contingentEndpointUri,
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
}).then(response => {
|
|
||||||
const json = response.data
|
|
||||||
if (json) {
|
|
||||||
this.availableDates = json
|
|
||||||
const enabledDates = []
|
|
||||||
for (const date in this.availableDates) {
|
|
||||||
if (this.availableDates.hasOwnProperty(date)) {
|
|
||||||
enabledDates.push(this.availableDates[date].date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.picker.set('enable', enabledDates)
|
|
||||||
this.picker.set('minDate', enabledDates[0])
|
|
||||||
this.picker.set('maxDate', enabledDates[enabledDates.length - 1])
|
|
||||||
this.picker.jumpToDate(enabledDates[0])
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchAvailableRooms() {
|
|
||||||
this.loading = true
|
|
||||||
const data = new FormData()
|
|
||||||
data.set('tx_epproducts_ajax[hotel]', this.hotelUid)
|
|
||||||
data.set('tx_epproducts_ajax[product]', this.productUid)
|
|
||||||
data.set('tx_epproducts_ajax[dateFrom]', this.selectedFrom.format('YYYY-MM-DD'))
|
|
||||||
data.set('tx_epproducts_ajax[dateTo]', this.selectedTo.format('YYYY-MM-DD'))
|
|
||||||
axios({
|
|
||||||
url: this.roomsEndpointUri,
|
|
||||||
method: 'post',
|
|
||||||
data
|
|
||||||
}).then(response => {
|
|
||||||
this.availableRooms = response.data
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
calculatePrice (roomCode) {
|
|
||||||
const room = this.availableRooms.find(obj => {
|
|
||||||
return obj.code === roomCode
|
|
||||||
});
|
|
||||||
if (room) {
|
|
||||||
const nights = this.getSelectedNumberOfNights()
|
|
||||||
return room.price + (nights - room.minNights) * room.additionalNightPrice
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
getMinNightsForSelectedDate () {
|
|
||||||
if (this.selectedFrom === null) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
const key = this.selectedFrom.format('YYYY-MM-DD')
|
|
||||||
if (!key in this.availableDates) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return this.availableDates[key].minNights
|
|
||||||
},
|
|
||||||
getSelectedNumberOfNights () {
|
|
||||||
if (this.selectedFrom === null || this.selectedTo === null) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return this.selectedTo.diff(this.selectedFrom, 'days');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import flatpickr from 'flatpickr'
|
|
||||||
import { German } from 'flatpickr/dist/l10n/de'
|
|
||||||
|
|
||||||
export default props => ({
|
|
||||||
loading: false,
|
|
||||||
picker: null,
|
|
||||||
uri: props.uri,
|
|
||||||
sticky: props.sticky || false,
|
|
||||||
searchParams: props.initialSearchParams,
|
|
||||||
filterOptions: props.initialFilterOptions,
|
|
||||||
datePresets: props.datePresets,
|
|
||||||
show: null,
|
|
||||||
destinationName: '',
|
|
||||||
conceptSelected: false,
|
|
||||||
destinationSelected: false,
|
|
||||||
dateRangeSelected: { from: null, to: null },
|
|
||||||
dateRangeLabel: '',
|
|
||||||
init() {
|
|
||||||
this.$watch('dateRangeSelected', () => {
|
|
||||||
if (null === this.dateRangeSelected.from && null === this.dateRangeSelected.to) {
|
|
||||||
this.searchParams.dateFrom = null
|
|
||||||
this.searchParams.dateTo = null
|
|
||||||
} else {
|
|
||||||
let dateFrom = this.dateRangeSelected.from
|
|
||||||
let dateTo = this.dateRangeSelected.to
|
|
||||||
this.searchParams.dateFrom = flatpickr.formatDate(dateFrom, 'Y-m-d')
|
|
||||||
this.searchParams.dateTo = flatpickr.formatDate(dateTo, 'Y-m-d')
|
|
||||||
}
|
|
||||||
this.updateDateRangeLabel()
|
|
||||||
this.load()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
load() {
|
|
||||||
this.loading = true
|
|
||||||
const data = new FormData()
|
|
||||||
data.set('tx_epproducts_ajax[searchParams]', JSON.stringify(this.searchParams))
|
|
||||||
axios.post(this.uri, data)
|
|
||||||
.then(response => {
|
|
||||||
this.applyOptions(response.data)
|
|
||||||
this.show = null
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
applyOptions(json) {
|
|
||||||
this.filterOptions.totalResults = json.totalResults
|
|
||||||
const filterOptions = json.filterOptions
|
|
||||||
this.filterOptions = {
|
|
||||||
...this.filterOptions,
|
|
||||||
totalResults: json.totalResults,
|
|
||||||
priceRanges: filterOptions.priceRanges,
|
|
||||||
pax: filterOptions.pax,
|
|
||||||
destinations: filterOptions.destinations
|
|
||||||
}
|
|
||||||
if (filterOptions.dateFrom !== null) {
|
|
||||||
this.filterOptions.selectableRangeFrom = filterOptions.dateFrom
|
|
||||||
if (this.picker !== null) {
|
|
||||||
this.picker.set('minDate', new Date(filterOptions.dateFrom))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filterOptions.dateTo !== null) {
|
|
||||||
this.filterOptions.selectableRangeTo = filterOptions.dateTo
|
|
||||||
if (this.picker !== null) {
|
|
||||||
this.picker.set('maxDate', new Date(filterOptions.dateTo))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateSearchParam(param, value) {
|
|
||||||
this.searchParams = {...this.searchParams, [param]: value}
|
|
||||||
this.load()
|
|
||||||
},
|
|
||||||
selectDateRangePreset(preset) {
|
|
||||||
let dateFrom = new Date(preset.dateFrom)
|
|
||||||
let dateTo = new Date(preset.dateTo)
|
|
||||||
this.dateRangeSelected = { from: dateFrom, to: dateTo }
|
|
||||||
this.picker.setDate([ dateFrom, dateTo ])
|
|
||||||
},
|
|
||||||
resetDateRange () {
|
|
||||||
this.dateRangeSelected = { from: null, to: null }
|
|
||||||
this.picker.clear()
|
|
||||||
this.picker.jumpToDate()
|
|
||||||
},
|
|
||||||
selectDestination(uid, name, type) {
|
|
||||||
this.destinationName = name
|
|
||||||
this.destinationSelected = type !== 'concept'
|
|
||||||
this.conceptSelected = type === 'concept'
|
|
||||||
this.searchParams = {...this.searchParams, destinationUid: uid, destinationType: type}
|
|
||||||
this.load()
|
|
||||||
},
|
|
||||||
resetDestination() {
|
|
||||||
this.destinationName = ''
|
|
||||||
this.destinationSelected = false
|
|
||||||
this.conceptSelected = false
|
|
||||||
this.searchParams = {...this.searchParams, destinationUid: null, destinationType: null}
|
|
||||||
this.load()
|
|
||||||
},
|
|
||||||
initDatePicker() {
|
|
||||||
this.picker = flatpickr(this.$refs.picker, {
|
|
||||||
mode: 'range',
|
|
||||||
locale: German,
|
|
||||||
inline: true,
|
|
||||||
onChange: selectedDates => {
|
|
||||||
if (selectedDates.length === 2) {
|
|
||||||
this.dateRangeSelected = { from: selectedDates[0], to: selectedDates[1] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let minDate = this.filterOptions.dateFrom ? new Date(this.filterOptions.dateFrom) : new Date
|
|
||||||
this.picker.set('minDate', minDate)
|
|
||||||
if (this.filterOptions.dateTo) {
|
|
||||||
this.picker.set('maxDate', new Date(this.filterOptions.dateTo))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
priceRangeLabel() {
|
|
||||||
if (null === this.searchParams.priceRange) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
let range = this.filterOptions.priceRanges[this.searchParams.priceRange]
|
|
||||||
return this.priceRangeFormatted(range)
|
|
||||||
},
|
|
||||||
priceRangeFormatted(range) {
|
|
||||||
let min = range.min;
|
|
||||||
let max = range.max;
|
|
||||||
if (min === 1) {
|
|
||||||
return 'bis ' + max + ' €'
|
|
||||||
}
|
|
||||||
if (max === 999) {
|
|
||||||
return 'ab ' + min + ' €'
|
|
||||||
}
|
|
||||||
return min + ' - ' + max + ' €'
|
|
||||||
},
|
|
||||||
updateDateRangeLabel() {
|
|
||||||
let dateRangeLabel = ''
|
|
||||||
if (null !== this.dateRangeSelected.from && null !== this.dateRangeSelected.to) {
|
|
||||||
let dateFrom = this.dateRangeSelected.from
|
|
||||||
let dateTo = this.dateRangeSelected.to
|
|
||||||
dateRangeLabel = `${flatpickr.formatDate(dateFrom, 'd.m.Y')} - ${flatpickr.formatDate(dateTo, 'd.m.Y')}`
|
|
||||||
}
|
|
||||||
this.dateRangeLabel = dateRangeLabel
|
|
||||||
},
|
|
||||||
})
|
|
||||||
+19
@@ -15,6 +15,7 @@ export default class extends Controller {
|
|||||||
minDate: String,
|
minDate: String,
|
||||||
dateFrom: String,
|
dateFrom: String,
|
||||||
dateTo: String,
|
dateTo: String,
|
||||||
|
enabledDates: Array,
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
@@ -33,6 +34,24 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if (this.hasEnabledDatesValue) {
|
||||||
|
let enabledDates = this.enabledDatesValue
|
||||||
|
let minDate = enabledDates[0]
|
||||||
|
let maxDate = enabledDates[enabledDates.length - 1]
|
||||||
|
if (this.hasMinDateValue && this.minDateValue > minDate) {
|
||||||
|
minDate = this.minDateValue
|
||||||
|
}
|
||||||
|
this.picker.set('enable', enabledDates)
|
||||||
|
this.picker.set('minDate', minDate)
|
||||||
|
this.picker.set('maxDate', maxDate)
|
||||||
|
this.picker.jumpToDate(minDate)
|
||||||
|
this.picker.set('onDayCreate', (dObj, dStr, fp, dayElem) => {
|
||||||
|
let day = flatpickr.formatDate(dayElem.dateObj, 'Y-m-d')
|
||||||
|
if (day >= minDate && -1 !== this.enabledDatesValue.indexOf(day)) {
|
||||||
|
dayElem.classList.add('available')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
import { Controller } from 'stimulus'
|
||||||
|
import { useDispatch } from 'stimulus-use'
|
||||||
|
import { useFetch } from '../mixins/use_fetch'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
|
||||||
|
static targets = [
|
||||||
|
'headerLabel',
|
||||||
|
'container',
|
||||||
|
'loadingIndicator',
|
||||||
|
'message',
|
||||||
|
]
|
||||||
|
|
||||||
|
static values = {
|
||||||
|
loading: Boolean,
|
||||||
|
contingentsUri: String,
|
||||||
|
roomsUri: String,
|
||||||
|
hotelUid: Number,
|
||||||
|
productUid: Number,
|
||||||
|
dateFrom: String,
|
||||||
|
dateTo: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
useDispatch(this)
|
||||||
|
useFetch(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
openModal(e) {
|
||||||
|
let eventName = `${e.currentTarget.dataset.type}-modal:open`
|
||||||
|
this.dispatch(eventName, e.currentTarget.dataset.uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
load(e) {
|
||||||
|
this.loadingValue = true
|
||||||
|
this.dateFromValue = e.detail.from
|
||||||
|
this.dateToValue = e.detail.to
|
||||||
|
let data = new FormData()
|
||||||
|
data.set('tx_epproducts_ajax[hotel]', this.hotelUidValue)
|
||||||
|
data.set('tx_epproducts_ajax[product]', this.productUidValue)
|
||||||
|
data.set('tx_epproducts_ajax[dateFrom]', this.dateFromValue)
|
||||||
|
data.set('tx_epproducts_ajax[dateTo]', this.dateToValue)
|
||||||
|
fetch(this.roomsUriValue, { method: 'POST', body: data })
|
||||||
|
.then(this.checkStatus)
|
||||||
|
.then(this.parseHTML)
|
||||||
|
.then(html => {
|
||||||
|
this.containerTarget.innerHTML = html
|
||||||
|
this.messageTarget.classList.add('hidden')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loadingValue = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingValueChanged() {
|
||||||
|
this.loadingIndicatorTarget.classList.toggle('hidden', this.loadingValue === false)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-1
@@ -18,6 +18,7 @@ export default class extends Controller {
|
|||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
loaded: Boolean,
|
loaded: Boolean,
|
||||||
mode: String,
|
mode: String,
|
||||||
|
daytrip: Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
@@ -26,7 +27,7 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
if ('dates' === this.modeValue) {
|
if (false === this.dayTripValue && 'dates' === this.modeValue) {
|
||||||
this.loadDates()
|
this.loadDates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +87,11 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modeValueChanged() {
|
modeValueChanged() {
|
||||||
|
if (this.hasDatesTableContainerTarget) {
|
||||||
this.datesTableContainerTarget.classList.toggle('hidden', this.modeValue === 'prices')
|
this.datesTableContainerTarget.classList.toggle('hidden', this.modeValue === 'prices')
|
||||||
|
}
|
||||||
|
if (this.hasPriceTableContainerTarget) {
|
||||||
this.priceTableContainerTarget.classList.toggle('hidden', this.modeValue === 'dates')
|
this.priceTableContainerTarget.classList.toggle('hidden', this.modeValue === 'dates')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-5
@@ -24,7 +24,7 @@ export default class extends Controller {
|
|||||||
this.dateRangeLabelTarget.value = e.detail.label
|
this.dateRangeLabelTarget.value = e.detail.label
|
||||||
this.dateFromTarget.value = e.detail.from
|
this.dateFromTarget.value = e.detail.from
|
||||||
this.dateToTarget.value = e.detail.to
|
this.dateToTarget.value = e.detail.to
|
||||||
this.dispatch('input')
|
this.dispatch('input', this.getDetails())
|
||||||
}
|
}
|
||||||
|
|
||||||
setPax(e) {
|
setPax(e) {
|
||||||
@@ -32,7 +32,7 @@ export default class extends Controller {
|
|||||||
let pax = e.currentTarget.dataset.option
|
let pax = e.currentTarget.dataset.option
|
||||||
this.paxTarget.value = pax
|
this.paxTarget.value = pax
|
||||||
this.paxLabelTarget.value = pax
|
this.paxLabelTarget.value = pax
|
||||||
this.dispatch('input')
|
this.dispatch('input', this.getDetails())
|
||||||
}
|
}
|
||||||
|
|
||||||
setDestination(e) {
|
setDestination(e) {
|
||||||
@@ -40,7 +40,7 @@ export default class extends Controller {
|
|||||||
this.destinationUidTarget.value = e.currentTarget.dataset.optionUid
|
this.destinationUidTarget.value = e.currentTarget.dataset.optionUid
|
||||||
this.destinationTypeTarget.value = e.currentTarget.dataset.optionType
|
this.destinationTypeTarget.value = e.currentTarget.dataset.optionType
|
||||||
this.destinationLabelTarget.value = e.currentTarget.dataset.optionLabel
|
this.destinationLabelTarget.value = e.currentTarget.dataset.optionLabel
|
||||||
this.dispatch('input')
|
this.dispatch('input', this.getDetails())
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDestination(e) {
|
resetDestination(e) {
|
||||||
@@ -48,13 +48,24 @@ export default class extends Controller {
|
|||||||
this.destinationUidTarget.value = ''
|
this.destinationUidTarget.value = ''
|
||||||
this.destinationTypeTarget.value = ''
|
this.destinationTypeTarget.value = ''
|
||||||
this.destinationLabelTarget.value = ''
|
this.destinationLabelTarget.value = ''
|
||||||
this.dispatch('input')
|
this.dispatch('input', this.getDetails())
|
||||||
}
|
}
|
||||||
|
|
||||||
setPriceRange(e) {
|
setPriceRange(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.priceRangeTarget.value = e.currentTarget.dataset.optionValue
|
this.priceRangeTarget.value = e.currentTarget.dataset.optionValue
|
||||||
this.priceRangeLabelTarget.value = e.currentTarget.dataset.labelValue
|
this.priceRangeLabelTarget.value = e.currentTarget.dataset.labelValue
|
||||||
this.dispatch('input')
|
this.dispatch('input', this.getDetails())
|
||||||
|
}
|
||||||
|
|
||||||
|
getDetails() {
|
||||||
|
return {
|
||||||
|
dateFrom: this.dateFromTarget.value,
|
||||||
|
dateTo: this.dateToTarget.value,
|
||||||
|
pax: this.paxTarget.value,
|
||||||
|
destinationUid: this.destinationUidTarget.value,
|
||||||
|
destinationType: this.destinationTypeTarget.value,
|
||||||
|
priceRange: this.priceRangeTarget.value,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -33,6 +33,7 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stickyValueChanged() {
|
stickyValueChanged() {
|
||||||
this.element.classList.toggle(this.stickyClass, this.stickyValue)
|
let stickyClass = this.hasStickyClass ? this.stickyClass : 'sticky'
|
||||||
|
this.element.classList.toggle(stickyClass, this.stickyValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<html data-namespace-typo3-fluid="true" lang="en"
|
||||||
|
xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<html data-namespace-typo3-fluid="true" lang="en"
|
||||||
|
xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||||
|
|
||||||
|
<f:form id="contactform" object="{contactForm}" name="contactForm"
|
||||||
|
action="processForm" controller="AjaxForm" pluginName="Ajax" pageType="1803"
|
||||||
|
pageUid="{settings.defaultAjaxUid}"
|
||||||
|
data="{'contactform-target': 'form', 'action': 'contactform#submit'}">
|
||||||
|
<f:form.hidden property="pageUrl"/>
|
||||||
|
<label class="block mb-4 hidden">
|
||||||
|
<span class="text-gray-700">Betreff*</span>
|
||||||
|
<f:form.textfield class="border-gray-400 focus:border-gray-800 mt-1 w-full" property="subject"/>
|
||||||
|
</label>
|
||||||
|
<label class="block mb-4" x-bind:class="'has-validation-error' && formErrors.gender">
|
||||||
|
<span x-bind:class="formErrors.gender ? 'text-red-500' : 'text-gray-700'">Gender*</span>
|
||||||
|
<f:form.select class="border-gray-400 focus:border-gray-800 mt-1 w-full" property="gender" options="{M: 'M', W: 'W', D: 'D'}"/>
|
||||||
|
<div class="text-red-500" x-show="formErrors.gender" x-html="formErrors.gender"></div>
|
||||||
|
</label>
|
||||||
|
<label class="block mb-4" x-bind:class="'has-validation-error' && formErrors.name">
|
||||||
|
<span x-bind:class="formErrors.name ? 'text-red-500' : 'text-gray-700'">Name*</span>
|
||||||
|
<f:form.textfield class="border-gray-400 focus:border-gray-800 mt-1 w-full" property="name"/>
|
||||||
|
<div class="text-red-500" x-show="formErrors.name" x-html="formErrors.name"></div>
|
||||||
|
</label>
|
||||||
|
<label class="block mb-4" x-bind:class="'has-validation-error' && formErrors.firstName">
|
||||||
|
<span x-bind:class="formErrors.firstName ? 'text-red-500' : 'text-gray-700'">Vorname*</span>
|
||||||
|
<f:form.textfield class="border-gray-400 focus:border-gray-800 mt-1 w-full" property="firstName"/>
|
||||||
|
<div class="text-red-500" x-show="formErrors.firstName" x-html="formErrors.firstName"></div>
|
||||||
|
</label>
|
||||||
|
<label class="block mb-4" x-bind:class="'has-validation-error' && formErrors.email">
|
||||||
|
<span x-bind:class="formErrors.email ? 'text-red-500' : 'text-gray-700'">E-Mail*</span>
|
||||||
|
<f:form.textfield class="border-gray-400 focus:border-gray-800 mt-1 w-full" property="email"/>
|
||||||
|
<div class="text-red-500" x-show="formErrors.email" x-html="formErrors.email"></div>
|
||||||
|
</label>
|
||||||
|
<label class="block mb-4" x-bind:class="'has-validation-error' && formErrors.phone">
|
||||||
|
<span x-bind:class="formErrors.phone ? 'text-red-500' : 'text-gray-700'">Telefon/Fax*</span>
|
||||||
|
<f:form.textfield class="border-gray-400 focus:border-gray-800 mt-1 w-full" property="phone"/>
|
||||||
|
<div class="text-red-500" x-show="formErrors.phone" x-html="formErrors.phone"></div>
|
||||||
|
</label>
|
||||||
|
<label class="block mb-4" x-bind:class="'has-validation-error' && formErrors.inquiry">
|
||||||
|
<span x-bind:class="formErrors.inquiry ? 'text-red-500' : 'text-gray-700'">(Zusatz-) Wünsche</span>
|
||||||
|
<f:form.textarea class="border-gray-400 focus:border-gray-800 mt-1 w-full"
|
||||||
|
property="inquiry"
|
||||||
|
placeholder="z. B. Reiseleiter, Halbpension, Bus Anreise, Programm"
|
||||||
|
cols="30" rows="5"/>
|
||||||
|
<div class="text-red-500" x-show="formErrors.inquiry" x-html="formErrors.inquiry"></div>
|
||||||
|
</label>
|
||||||
|
<div class="mb-4">
|
||||||
|
Ich habe die <f:link.typolink parameter="{settings.dataProtectionPageUid}"
|
||||||
|
title="Datenschutzerklärung"
|
||||||
|
target="_blank">Datenschutzbestimmungen</f:link.typolink> gelesen und akzeptiert.
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<button type="submit" class="button button-warning">Jetzt anfragen</button>
|
||||||
|
</div>
|
||||||
|
</f:form>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<div 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="Form/Default"/>
|
||||||
|
|
||||||
|
<f:section name="Main">
|
||||||
|
<f:render partial="ContactForm/Simple" arguments="{_all}"/>
|
||||||
|
</f:section>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<div 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="Form/Default"/>
|
||||||
|
|
||||||
|
<f:section name="Main">
|
||||||
|
<div class="form form--border"
|
||||||
|
data-controller="contactform">
|
||||||
|
<div data-contactform-target="container">
|
||||||
|
<f:render partial="ContactForm/Simple" arguments="{_all}"/>
|
||||||
|
</div>
|
||||||
|
<div class="hidden form__overlay"
|
||||||
|
data-contactform-target="loadingIndicator">
|
||||||
|
<img src="{f:uri.resource(path: 'images/ajax-loader-white.gif', extensionName: 'ep_theme')}" alt="Processing...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:section>
|
||||||
|
|
||||||
|
</div>
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
<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">
|
||||||
|
<table class="table table-striped pricetable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Zimmerart</th>
|
||||||
|
<th>Preis</th>
|
||||||
|
<th>inkl.</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<f:for each="{rooms}" as="row" iteration="iteration">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{row.label}
|
||||||
|
</td>
|
||||||
|
<td class="whitespace-nowrap">
|
||||||
|
ab {row.priceForSelection} €
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<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="daytrip#openModal"
|
||||||
|
data-type="services"
|
||||||
|
data-uid="{iteration.cycle}"
|
||||||
|
data-microtip-position="top"
|
||||||
|
aria-label="Alle Inklusivleistungen anzeigen"
|
||||||
|
role="tooltip">
|
||||||
|
Leistungen
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="button button--small bg-green-500 border-green-500"
|
||||||
|
href="{row.bookingUrl}" target="_blank">
|
||||||
|
Buchen
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</f:for>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="hidden fixed inset-0 w-full h-full z-50"
|
||||||
|
data-controller="modal"
|
||||||
|
data-action="daytrip: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
|
||||||
|
</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="{rooms}" as="row" iteration="iteration">
|
||||||
|
<table class="hidden table table-striped"
|
||||||
|
data-modal-target="content"
|
||||||
|
data-uid="{iteration.cycle}">
|
||||||
|
<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>
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
</f:if>
|
</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-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-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)}">
|
data-product-details-travel-alert-uri-value="{f:uri.typolink(parameter: settings.travelAlertPageUid)}"
|
||||||
|
data-product-details-daytrip-value="{product.daytrip -> f:format.json()}">
|
||||||
<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">
|
data-controller="tabs">
|
||||||
@@ -245,92 +246,40 @@
|
|||||||
</f:section>
|
</f:section>
|
||||||
|
|
||||||
<f:section name="DaytripDateSelect">
|
<f:section name="DaytripDateSelect">
|
||||||
<f:comment><!--
|
<div data-controller="daytrip"
|
||||||
<div x-data='daytripDateSelect({
|
data-daytrip-hotel-uid-value="{hotel.uid}"
|
||||||
<f:format.raw>
|
data-daytrip-product-uid-value="{product.uid}"
|
||||||
hotelName: "{hotel.name}",
|
data-daytrip-rooms-uri-value="{ep:uri.ajax(action: 'daytripRooms', controller: 'AjaxTable', format: 'html', pageUid: settings.defaultAjaxUid)}"
|
||||||
hotelUid: {hotel.uid},
|
data-action="datepicker:range-select->daytrip#load">
|
||||||
productUid: {product.uid},
|
<div class="headerbar"
|
||||||
contingentEndpointUri: "{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}",
|
data-daytrip-target="headerLabel"></div>
|
||||||
roomsEndpointUri: "{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}"
|
|
||||||
</f:format.raw>
|
|
||||||
})'>
|
|
||||||
<div class="headerbar" x-text="headerLabel"></div>
|
|
||||||
<div class="grid grid-cols-2 gap-8">
|
<div class="grid grid-cols-2 gap-8">
|
||||||
<div class="col-span-2 md:col-span-1">
|
<div class="col-span-2 md:col-span-1">
|
||||||
<div class="date-select">
|
<div class="date-select">
|
||||||
<div class="date-select__picker">
|
<div class="date-select__picker">
|
||||||
<div class="daterange__panel">
|
<div class="daterange__panel"
|
||||||
<input x-ref="field" type="hidden">
|
data-controller="datepicker"
|
||||||
|
data-datepicker-min-date-value="{f:format.date(date: 'now', format: 'Y-m-d')}"
|
||||||
|
data-datepicker-enabled-dates-value='{enabledDates -> f:format.json()}'>
|
||||||
|
<input type="hidden"
|
||||||
|
data-datepicker-target="field">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-2 md:col-span-1">
|
<div class="col-span-2 md:col-span-1">
|
||||||
<table class="table table-striped pricetable" x-show="availableRooms.length">
|
<div data-daytrip-target="container"></div>
|
||||||
<thead>
|
<div class="hidden flex items-center space-x-2"
|
||||||
<tr>
|
data-daytrip-target="loadingIndicator">
|
||||||
<th>Zimmerart</th>
|
|
||||||
<th>Preis</th>
|
|
||||||
<th>inkl.</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<template x-for="(row, index) in availableRooms">
|
|
||||||
<tr>
|
|
||||||
<td x-text="row.label"></td>
|
|
||||||
<td style="white-space: nowrap" x-text="'ab ' + calculatePrice(row.code) + ' €'"></td>
|
|
||||||
<td>
|
|
||||||
<i x-show="row.skipassIncluded" aria-label="Skipass inklusive" role="tooltip" data-microtip-position="top" class="fa fa-tag"></i>
|
|
||||||
<a href="#" data-toggle="modal"
|
|
||||||
data-target="'#services' + index"
|
|
||||||
data-microtip-position="top" aria-label="Inklusivleistungen zeigen" role="tooltip">
|
|
||||||
<svg class="w-4 h-4">
|
|
||||||
<use href="#icon-info-circle"></use>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<div class="modal fade" id="'services' + index" role="dialog">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal">
|
|
||||||
<span>×</span>
|
|
||||||
</button>
|
|
||||||
<h5 class="modal-title">Inklusivleistungen</h5>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="service of row.servicesIncluded">
|
|
||||||
<td><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {{ service }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a x-bind:href="row.bookingUrl" target="_blank"
|
|
||||||
class="button button--small button--action">Buchen</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div x-show="loading" class="loader flex items-center space-x-2">
|
|
||||||
<span>Loading...</span>
|
<span>Loading...</span>
|
||||||
<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"/>
|
||||||
</div>
|
</div>
|
||||||
<div x-show="message" x-text="message"></div>
|
<div data-daytrip-target="message">
|
||||||
|
Bitte einen Zeitraum auswählen.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
--></f:comment>
|
|
||||||
</f:section>
|
</f:section>
|
||||||
|
|
||||||
<f:section name="Calendar">
|
<f:section name="Calendar">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<f:section name="Main">
|
<f:section name="Main">
|
||||||
<div data-controller="searchbar"
|
<div data-controller="searchbar"
|
||||||
{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="sticky"')}
|
{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650"')}
|
||||||
data-action="datepicker:range-select->searchbar#setDateRange"
|
data-action="datepicker:range-select->searchbar#setDateRange"
|
||||||
data-searchbar-uri-value="{ep:uri.ajax(controller: 'Searchbar', action: 'index', pageUid: settings.defaultAjaxUid, format: 'html')}">
|
data-searchbar-uri-value="{ep:uri.ajax(controller: 'Searchbar', action: 'index', pageUid: settings.defaultAjaxUid, format: 'html')}">
|
||||||
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"
|
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"
|
||||||
|
|||||||
Reference in New Issue
Block a user