WIP Migrate to stimulus.js and tailwind
This commit is contained in:
@@ -68,16 +68,11 @@ class AjaxSearchbarController extends ActionController
|
||||
* @param array $searchParams
|
||||
* @return string
|
||||
*/
|
||||
public function updateAction(array $searchParams = null)
|
||||
public function indexAction(array $searchParams)
|
||||
{
|
||||
$fixed = (bool)$this->settings['fixed'];
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
|
||||
if (null === $searchParams) {
|
||||
$searchParams = $this->searchResultService->getNormalizedSearchParams();
|
||||
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
|
||||
} else {
|
||||
$filterSettings = $this->filterService->getFilterSettingsFromSearchParams($searchParams);
|
||||
}
|
||||
$filterSettings = $this->filterService->getFilterSettingsFromSearchParams($searchParams);
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
||||
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
|
||||
$filterOptions = $this->filterService->getFilterOptions(
|
||||
@@ -87,9 +82,11 @@ class AjaxSearchbarController extends ActionController
|
||||
$forcedConceptUids
|
||||
);
|
||||
|
||||
return json_encode([
|
||||
$this->view->assignMultiple([
|
||||
'filterSettings' => $filterSettings,
|
||||
'filterOptions' => $filterOptions,
|
||||
'searchParams' => $searchParams,
|
||||
], JSON_THROW_ON_ERROR);
|
||||
'fixed' => $fixed,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ class GroupsPriceController extends ActionController
|
||||
});
|
||||
|
||||
$this->view->assign('hotel', $hotel);
|
||||
$this->view->assign('configs', json_encode(array_values($configs)));
|
||||
$this->view->assign('boards', json_encode($hotel->getGroupsPriceBoards()->toArray()));
|
||||
$this->view->assign('options', json_encode($hotel->getGroupsPriceOptions()->toArray()));
|
||||
$this->view->assign('configs', json_encode(array_values($configs), JSON_HEX_APOS));
|
||||
$this->view->assign('boards', json_encode($hotel->getGroupsPriceBoards()->toArray(), JSON_HEX_APOS));
|
||||
$this->view->assign('options', json_encode($hotel->getGroupsPriceOptions()->toArray(), JSON_HEX_APOS));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getPriceAdditionalPerson(): float
|
||||
public function getPriceAdditionalPerson(): ?float
|
||||
{
|
||||
return $this->priceAdditionalPerson;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getPriceAdditionalPersonGroup1(): float
|
||||
public function getPriceAdditionalPersonGroup1(): ?float
|
||||
{
|
||||
return $this->priceAdditionalPersonGroup1;
|
||||
}
|
||||
@@ -175,7 +175,7 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getPriceAdditionalPersonGroup2(): float
|
||||
public function getPriceAdditionalPersonGroup2(): ?float
|
||||
{
|
||||
return $this->priceAdditionalPersonGroup2;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ class GroupsPriceConfig extends AbstractEntity implements \JsonSerializable
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getPriceAdditionalPersonGroup3(): float
|
||||
public function getPriceAdditionalPersonGroup3(): ?float
|
||||
{
|
||||
return $this->priceAdditionalPersonGroup3;
|
||||
}
|
||||
|
||||
@@ -281,6 +281,7 @@ class FilterService implements SingletonInterface
|
||||
'uid' => $index,
|
||||
'min' => $range[0],
|
||||
'max' => $range[1],
|
||||
'formatted' => $this->formatPriceRange($range[0], $range[1]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -303,8 +304,8 @@ class FilterService implements SingletonInterface
|
||||
|
||||
$filterOptions = new FilterOptions();
|
||||
$filterOptions->setPriceRanges($priceRanges);
|
||||
$filterOptions->setDateFrom(new \DateTime($dateMin));
|
||||
$filterOptions->setDateTo(new \DateTime($dateMax));
|
||||
$filterOptions->setDateFrom($dateMin);
|
||||
$filterOptions->setDateTo($dateMax);
|
||||
$filterOptions->setConcepts($concepts);
|
||||
$filterOptions->setDestinations($destinations);
|
||||
$filterOptions->setHotelTypes($hotelTypes);
|
||||
@@ -316,6 +317,18 @@ class FilterService implements SingletonInterface
|
||||
return $filterOptions;
|
||||
}
|
||||
|
||||
public function formatPriceRange(int $min, int $max): string
|
||||
{
|
||||
if ($min === 1) {
|
||||
return sprintf('bis %d €', $max);
|
||||
}
|
||||
if ($max === 999) {
|
||||
return sprintf('ab %d €', $min);
|
||||
}
|
||||
|
||||
return sprintf('%d - %d €', $min, $max);
|
||||
}
|
||||
|
||||
public function getFiltersettingsFromSettings(array $settings): array
|
||||
{
|
||||
$filterSettings = $this->getNormalizedFilterSettings();
|
||||
|
||||
@@ -256,17 +256,14 @@ class HotelDataService implements SingletonInterface
|
||||
$hotel['dates'][] = $row['dateStart'];
|
||||
}
|
||||
|
||||
$dateStart = new \DateTime($row['dateStart']);
|
||||
$dateEnd = new \DateTime($row['dateEnd']);
|
||||
|
||||
if ($hotel['minDateStart'] === null
|
||||
|| $hotel['minDateStart'] > $dateStart) {
|
||||
$hotel['minDateStart'] = $dateStart;
|
||||
|| $hotel['minDateStart'] > $row['dateStart']) {
|
||||
$hotel['minDateStart'] = $row['dateStart'];
|
||||
}
|
||||
|
||||
if ($hotel['maxDateEnd'] === null
|
||||
|| $hotel['maxDateEnd'] < $dateEnd) {
|
||||
$hotel['maxDateEnd'] = $dateEnd;
|
||||
|| $hotel['maxDateEnd'] < $row['dateEnd']) {
|
||||
$hotel['maxDateEnd'] = $row['dateEnd'];
|
||||
}
|
||||
|
||||
if ($hotel['minPrice'] === null
|
||||
|
||||
@@ -157,7 +157,7 @@ tx_epproducts_ajax_json {
|
||||
1 = dates
|
||||
}
|
||||
AjaxSearchbar {
|
||||
1 = update
|
||||
1 = index
|
||||
}
|
||||
AjaxTable {
|
||||
1 = datestable
|
||||
|
||||
@@ -371,7 +371,7 @@ $iconRegistry->registerIcon(
|
||||
[
|
||||
'AjaxSearch' => 'searchresult',
|
||||
'AjaxDate' => 'dates',
|
||||
'AjaxSearchbar' => 'update',
|
||||
'AjaxSearchbar' => 'index',
|
||||
'AjaxContingent' => 'list,rooms',
|
||||
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||
@@ -381,7 +381,7 @@ $iconRegistry->registerIcon(
|
||||
[
|
||||
'AjaxSearch' => 'searchresult',
|
||||
'AjaxDate' => 'dates',
|
||||
'AjaxSearchbar' => 'update',
|
||||
'AjaxSearchbar' => 'index',
|
||||
'AjaxContingent' => 'list,rooms',
|
||||
// 'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||
|
||||
@@ -94,7 +94,7 @@ lib.stdheader.3.headerClass >
|
||||
@import 'EXT:ep_theme/Configuration/TypoScript/Page/*'
|
||||
|
||||
config {
|
||||
compressJs = 0
|
||||
compressJs = 1
|
||||
compressCss = 1
|
||||
typolinkCheckRootline = 1
|
||||
typolinkEnableLinksAcrossDomains = 1
|
||||
|
||||
@@ -3,7 +3,6 @@ require('cookieconsent')
|
||||
require('lazysizes')
|
||||
require('lazysizes/plugins/unveilhooks/ls.unveilhooks')
|
||||
require('picturefill')
|
||||
import Glightbox from 'glightbox'
|
||||
|
||||
// Polyfills
|
||||
import smoothscroll from 'smoothscroll-polyfill'
|
||||
@@ -11,26 +10,44 @@ smoothscroll.polyfill()
|
||||
|
||||
// Alpine stuff
|
||||
import Alpine from 'alpinejs'
|
||||
import parseAppConfig from './components/appconfig'
|
||||
import appendPaCode from './components/pacode'
|
||||
import daytripDateSelect from './components/daytrip-date-select'
|
||||
import searchBar from './components/search-bar'
|
||||
import calendar from './components/calendar'
|
||||
import contactForm from './components/contact-form'
|
||||
|
||||
const appConfig = parseAppConfig()
|
||||
appendPaCode(appConfig)
|
||||
Alpine.store('appconfig', appConfig)
|
||||
Alpine.data('daytripDateSelect', daytripDateSelect)
|
||||
Alpine.data('searchBar', searchBar)
|
||||
Alpine.data('calendar', calendar)
|
||||
Alpine.data('contactForm', contactForm)
|
||||
Alpine.start()
|
||||
|
||||
Glightbox({
|
||||
selector: 'data-glightbox',
|
||||
slideEffect: 'fade',
|
||||
})
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const configElement = document.getElementById('appconfig')
|
||||
if (null === configElement) {
|
||||
return {}
|
||||
}
|
||||
const appConfig = JSON.parse(configElement.innerHTML)
|
||||
const paCode = appConfig.paCode
|
||||
const externalDomains = appConfig.externalDomains
|
||||
if (paCode) {
|
||||
document.querySelectorAll('a').forEach(element => {
|
||||
if (element.hostname === location.hostname) {
|
||||
return
|
||||
}
|
||||
if (externalDomains.indexOf(element.hostname) === -1) {
|
||||
return
|
||||
}
|
||||
let url = element.href
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
if (url.indexOf('agenturcode=') !== -1) {
|
||||
return
|
||||
}
|
||||
const separator = url.indexOf('?') === -1 ? '?' : '&'
|
||||
url += separator + 'agenturcode=' + paCode
|
||||
element.href = url
|
||||
})
|
||||
}
|
||||
}, false)
|
||||
|
||||
import { Application } from 'stimulus'
|
||||
import { definitionsFromContext } from 'stimulus/webpack-helpers'
|
||||
|
||||
+27
-25
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="form-wrapper form--border">
|
||||
<div class="container">
|
||||
<div class="form-group" v-show="!submitted">
|
||||
<label>Zeitraum</label>
|
||||
<input type="text" class="form-control datepicker--visible" placeholder="von... bis" ref="picker"/>
|
||||
@@ -141,7 +141,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery';
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/de'
|
||||
import 'flatpickr';
|
||||
@@ -150,6 +149,7 @@ import Vue from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: 'GroupsPriceCalculator',
|
||||
props: {
|
||||
loaderUri: {
|
||||
type: String,
|
||||
@@ -159,21 +159,24 @@ export default {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
configs: {
|
||||
type: Array,
|
||||
configsJson: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
boards: {
|
||||
type: Array,
|
||||
boardsJson: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
optionsJson: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
configs: JSON.parse(this.configsJson),
|
||||
boards: JSON.parse(this.boardsJson),
|
||||
options: JSON.parse(this.optionsJson),
|
||||
processing: false,
|
||||
submitted: false,
|
||||
picker: null,
|
||||
@@ -248,27 +251,26 @@ export default {
|
||||
for (let option of this.selectedOptions) {
|
||||
options.push(option.uid);
|
||||
}
|
||||
let formData = {
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][name]': this.name,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][email]': this.email,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][phone]': this.phone,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'),
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.includesBonusCard ? '' : this.selectedPax,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup1]': this.includesBonusCard ? this.selectedPaxGroup1 : '',
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup2]': this.includesBonusCard ? this.selectedPaxGroup2 : '',
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][paxGroup3]': this.includesBonusCard ? this.selectedPaxGroup3 : '',
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][board]': this.selectedBoard ? this.selectedBoard.uid : null,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][options]': options,
|
||||
'tx_epproducts_ajax[groupsPriceInquiry][summary]': this.priceSummary
|
||||
};
|
||||
let formData = new FormData()
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][name]', this.name)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][email]', this.email)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][phone]', this.phone)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][remarks]', this.remarks)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][dateFrom]', this.selectedFrom.format('DD.MM.YYYY'))
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][dateTo]', this.selectedTo.format('DD.MM.YYYY'))
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][pax]', this.includesBonusCard ? '' : this.selectedPax)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][paxGroup1]', this.includesBonusCard ? this.selectedPaxGroup1 : '')
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][paxGroup2]', this.includesBonusCard ? this.selectedPaxGroup2 : '')
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][paxGroup3]', this.includesBonusCard ? this.selectedPaxGroup3 : '')
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][board]', this.selectedBoard ? this.selectedBoard.uid : null)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][options]', options)
|
||||
formData.set('tx_epproducts_ajax[groupsPriceInquiry][summary]', this.priceSummary)
|
||||
this.processing = true;
|
||||
this.submitted = false;
|
||||
axios({
|
||||
url: this.endpoint,
|
||||
method: 'POST',
|
||||
data: $.param(formData)
|
||||
data: formData
|
||||
}).then(response => {
|
||||
if ('validation' === response.data.status) {
|
||||
this.formErrors = response.data.errors;
|
||||
@@ -403,7 +405,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
Vue.nextTick(() => {
|
||||
this.picker = $(this.$refs.picker).flatpickr({
|
||||
this.picker = flatpickr(this.$refs.picker, {
|
||||
mode: 'range',
|
||||
dateFormat: 'd.m.Y',
|
||||
locale: German,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export default () => {
|
||||
const configElement = document.getElementById('appconfig')
|
||||
if (null === configElement) {
|
||||
return {}
|
||||
}
|
||||
return JSON.parse(configElement.innerHTML)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export default props => ({
|
||||
loading: false,
|
||||
selectedIndex: null,
|
||||
selectableMonths: [],
|
||||
calendarWrapper: null,
|
||||
init() {
|
||||
this.calendarWrapper = this.$refs.calendarWrapper
|
||||
let months = []
|
||||
let today = new Date()
|
||||
let year = today.getFullYear()
|
||||
let month = today.getMonth()
|
||||
for (let i = 0; i < 20; i++) {
|
||||
months.push(new Date(year, month, 1))
|
||||
if (month < 12) {
|
||||
month++
|
||||
} else {
|
||||
month = 1
|
||||
year++
|
||||
}
|
||||
}
|
||||
this.selectableMonths = months
|
||||
this.selectedIndex = 0
|
||||
this.load()
|
||||
},
|
||||
load() {
|
||||
this.loading = true
|
||||
let selectedMonth = this.selectableMonths[this.selectedIndex]
|
||||
let data = new URLSearchParams({
|
||||
'tx_epproducts_ajax[hotel]': props.hotel,
|
||||
'tx_epproducts_ajax[month]': selectedMonth.getMonth() + 1,
|
||||
'tx_epproducts_ajax[year]': selectedMonth.getFullYear(),
|
||||
})
|
||||
axios.post(props.url, data)
|
||||
.then(response => (
|
||||
this.calendarWrapper.innerHTML = response.data
|
||||
))
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
export default appConfig => {
|
||||
const paCode = appConfig.paCode
|
||||
const externalDomains = appConfig.externalDomains
|
||||
if (!paCode) {
|
||||
return
|
||||
}
|
||||
document.querySelectorAll('a').forEach(element => {
|
||||
if (element.hostname === location.hostname) {
|
||||
return
|
||||
}
|
||||
if (externalDomains.indexOf(element.hostname) === -1) {
|
||||
return
|
||||
}
|
||||
let url = element.href
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
if (url.indexOf('agenturcode=') !== -1) {
|
||||
return
|
||||
}
|
||||
const separator = url.indexOf('?') === -1 ? '?' : '&'
|
||||
url += separator + 'agenturcode=' + paCode
|
||||
element.href = url
|
||||
})
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
const debounce = (callback, interval) => {
|
||||
let debounceId
|
||||
return (...args) => {
|
||||
clearTimeout(debounceId)
|
||||
debounceId = setTimeout(() => callback.apply(this, args), interval)
|
||||
}
|
||||
}
|
||||
|
||||
const throttle = (callback, interval) => {
|
||||
let enableCall = true
|
||||
return (...args) => {
|
||||
if (!enableCall) return
|
||||
enableCall = false
|
||||
callback.apply(this, args)
|
||||
setTimeout(() => enableCall = true, interval)
|
||||
}
|
||||
}
|
||||
|
||||
export { debounce, throttle }
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useFetch } from '../mixins/use_fetch'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [
|
||||
'container',
|
||||
'field',
|
||||
'loadingIndicator',
|
||||
]
|
||||
|
||||
static values = {
|
||||
loading: Boolean,
|
||||
uri: String,
|
||||
hotelUid: Number,
|
||||
}
|
||||
|
||||
initialize() {
|
||||
useFetch(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
let months = []
|
||||
let today = new Date()
|
||||
let year = today.getFullYear()
|
||||
let month = today.getMonth()
|
||||
for (let i = 0; i < 20; i++) {
|
||||
let selectableDate = new Date(year, month, 1)
|
||||
months.push(selectableDate)
|
||||
if (month < 12) {
|
||||
month++
|
||||
} else {
|
||||
month = 1
|
||||
year++
|
||||
}
|
||||
this.fieldTarget.options[i] = new Option(selectableDate.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'}), i.toString())
|
||||
}
|
||||
this.selectableMonths = months
|
||||
this.load(0)
|
||||
}
|
||||
|
||||
select(e) {
|
||||
let selectedIndex = e.currentTarget.value
|
||||
this.load(selectedIndex)
|
||||
}
|
||||
|
||||
load(selectedIndex) {
|
||||
this.loadingValue = true
|
||||
let selectedMonth = this.selectableMonths[selectedIndex]
|
||||
let data = new URLSearchParams()
|
||||
data.set('tx_epproducts_ajax[hotel]', this.hotelUidValue)
|
||||
data.set('tx_epproducts_ajax[month]', selectedMonth.getMonth() + 1)
|
||||
data.set('tx_epproducts_ajax[year]', selectedMonth.getFullYear())
|
||||
fetch(this.uriValue, { method: 'POST', body: data })
|
||||
.then(this.checkStatus)
|
||||
.then(this.parseHTML)
|
||||
.then(html => (
|
||||
this.containerTarget.innerHTML = html
|
||||
))
|
||||
.finally(() => {
|
||||
this.loadingValue = false
|
||||
})
|
||||
}
|
||||
|
||||
loadingValueChanged() {
|
||||
this.loadingIndicatorTarget.classList.toggle('hidden', this.loadingValue === false)
|
||||
}
|
||||
}
|
||||
+12
-1
@@ -26,10 +26,21 @@ export default class extends Controller {
|
||||
this.toggleByWindowWidth(width)
|
||||
}
|
||||
|
||||
toggle() {
|
||||
toggle(e) {
|
||||
e.preventDefault()
|
||||
this.collapsedValue = !this.collapsedValue
|
||||
}
|
||||
|
||||
open(e) {
|
||||
e.preventDefault()
|
||||
this.collapsedValue = false
|
||||
}
|
||||
|
||||
close(e) {
|
||||
e.preventDefault()
|
||||
this.collapsedValue = true
|
||||
}
|
||||
|
||||
toggleByWindowWidth(width) {
|
||||
if (0 !== this.untilValue) {
|
||||
this.collapsedValue = width < this.untilValue
|
||||
|
||||
+14
-5
@@ -23,9 +23,12 @@ export default class extends Controller {
|
||||
minDate: this.minDateValue,
|
||||
onChange: selectedDates => {
|
||||
if (selectedDates.length === 2) {
|
||||
let dateFrom = flatpickr.formatDate(selectedDates[0], 'Y-m-d')
|
||||
let dateTo = flatpickr.formatDate(selectedDates[1], 'Y-m-d')
|
||||
this.dispatch('range-select', {
|
||||
from: flatpickr.formatDate(selectedDates[0], 'Y-m-d'),
|
||||
to: flatpickr.formatDate(selectedDates[1], 'Y-m-d'),
|
||||
from: dateFrom,
|
||||
to: dateTo,
|
||||
label: `${dateFrom} - ${dateTo}`
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -43,14 +46,20 @@ export default class extends Controller {
|
||||
selectPreset(e) {
|
||||
e.preventDefault()
|
||||
const preset = JSON.parse(e.currentTarget.dataset.preset)
|
||||
this.picker.setDate([ new Date(preset.dateFrom), new Date(preset.dateTo) ])
|
||||
this.dispatch('range-select', { from: preset.dateFrom, to: preset.dateTo })
|
||||
let dateFrom = new Date(preset.dateFrom)
|
||||
let dateTo = new Date(preset.dateTo)
|
||||
this.picker.setDate([ dateFrom, dateTo ])
|
||||
this.dispatch('range-select', {
|
||||
from: preset.dateFrom,
|
||||
to: preset.dateTo,
|
||||
label: `${flatpickr.formatDate(dateFrom, 'd.m.Y')} - ${flatpickr.formatDate(dateTo, 'd.m.Y')}`
|
||||
})
|
||||
}
|
||||
|
||||
reset(e) {
|
||||
e.preventDefault()
|
||||
this.picker.clear()
|
||||
this.picker.jumpToDate()
|
||||
this.dispatch('range-select', { from: null, to: null })
|
||||
this.dispatch('range-select', { from: null, to: null, label: null })
|
||||
}
|
||||
}
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import Glightbox from 'glightbox'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'thumb' ]
|
||||
|
||||
static values = { url: String }
|
||||
|
||||
connect() {
|
||||
let elements = []
|
||||
if (this.hasUrlValue) {
|
||||
let caption = this.getCaption(this.element)
|
||||
let element = { href: this.urlValue, title: caption }
|
||||
elements.push(element)
|
||||
} else if (this.element.hasAttribute('href')) {
|
||||
let caption = this.getCaption(this.element)
|
||||
let element = { href: this.element.href, title: caption }
|
||||
elements.push(element)
|
||||
} else {
|
||||
this.thumbTargets.forEach(thumb => {
|
||||
let caption = this.getCaption(thumb)
|
||||
elements.push({ href: thumb.href, title: caption })
|
||||
})
|
||||
}
|
||||
this.lightbox = Glightbox({
|
||||
elements
|
||||
})
|
||||
}
|
||||
|
||||
open(e) {
|
||||
e.preventDefault()
|
||||
let index = 1
|
||||
if (e.currentTarget.dataset.lightboxIndex) {
|
||||
index = e.currentTarget.dataset.lightboxIndex
|
||||
}
|
||||
this.lightbox.openAt(index - 1)
|
||||
}
|
||||
|
||||
getCaption(element) {
|
||||
return element.hasAttribute('data-lightbox-caption') ? element.dataset.lightboxCaption : null
|
||||
}
|
||||
}
|
||||
-1
@@ -88,6 +88,5 @@ export default class extends Controller {
|
||||
modeValueChanged() {
|
||||
this.datesTableContainerTarget.classList.toggle('hidden', this.modeValue === 'prices')
|
||||
this.priceTableContainerTarget.classList.toggle('hidden', this.modeValue === 'dates')
|
||||
this.element.scrollIntoView({ block: 'start', behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useFetch } from '../mixins/use_fetch'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [
|
||||
'container',
|
||||
'form',
|
||||
'destinationUid',
|
||||
'destinationType',
|
||||
'dateFrom',
|
||||
'dateTo',
|
||||
'nights',
|
||||
'pax',
|
||||
'priceRange',
|
||||
'dateRangeLabel'
|
||||
]
|
||||
|
||||
static values = {
|
||||
uri: String,
|
||||
loading: Boolean,
|
||||
}
|
||||
|
||||
initialize() {
|
||||
useFetch(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
|
||||
}
|
||||
|
||||
load() {
|
||||
this.loadingValue = true
|
||||
let data = new URLSearchParams()
|
||||
data.set('tx_epproducts_ajax[searchParams]', JSON.stringify({
|
||||
dateFrom: this.dateFromTarget.value,
|
||||
dateTo: this.dateToTarget.value,
|
||||
}))
|
||||
fetch(this.uriValue, { method: 'POST', body: data })
|
||||
.then(this.checkStatus)
|
||||
.then(this.parseHTML)
|
||||
.then(html => {
|
||||
this.containerTarget.innerHTML = html
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadingValue = false
|
||||
})
|
||||
}
|
||||
|
||||
setDateRange(e) {
|
||||
this.dateFromTarget.value = e.detail.from
|
||||
this.dateToTarget.value = e.detail.to
|
||||
this.dateRangeLabelTarget.value = e.detail.label
|
||||
this.load()
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -32,8 +32,12 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
onListValueChanged() {
|
||||
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')
|
||||
if (this.hasLabelTarget) {
|
||||
this.labelTarget.innerText = true === this.onListValue ? 'von der Merkliste entfernen' : 'auf die Merkliste setzen'
|
||||
}
|
||||
if (this.hasIconTarget) {
|
||||
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')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// Initialize vue app
|
||||
import Vue from 'vue'
|
||||
import vueCustomElement from 'vue-custom-element'
|
||||
import 'document-register-element/build/document-register-element'
|
||||
import GroupsPriceCalculator from './components/GroupsPriceCalculator'
|
||||
|
||||
Vue.use(vueCustomElement)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
Vue.customElement('groups-price-calculator', GroupsPriceCalculator)
|
||||
@@ -1,4 +1,4 @@
|
||||
// Initialize vue apps
|
||||
// Initialize vue app
|
||||
import Vue from 'vue'
|
||||
import vueCustomElement from 'vue-custom-element'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
@@ -117,11 +117,19 @@
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
<a href="#" class="teaserbox-watchlist" x-data="watchlistToggle('{item.key}')" x-bind="trigger">
|
||||
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
|
||||
<i class="fa fa-plus-circle" x-bind:class="icon"></i>
|
||||
</span>
|
||||
</a>
|
||||
<button class="teaserbox-watchlist"
|
||||
data-controller="watchlist-toggle"
|
||||
data-watchlist-toggle-key-value="{item.key}"
|
||||
data-action="watchlist-toggle#toggle">
|
||||
<span data-microtip-position="top"
|
||||
role="tooltip"
|
||||
aria-label="auf die Merkliste setzen"
|
||||
data-watchlist-toggle-target="ariaLabel">
|
||||
<svg class="w-5 h-5 text-ep-secondary">
|
||||
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@
|
||||
<symbol id="icon-bed" fill="currentColor" stroke="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Pro 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M176 256c44.11 0 80-35.89 80-80s-35.89-80-80-80-80 35.89-80 80 35.89 80 80 80zm352-128H304c-8.84 0-16 7.16-16 16v144H64V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v352c0 8.84 7.16 16 16 16h32c8.84 0 16-7.16 16-16v-48h512v48c0 8.84 7.16 16 16 16h32c8.84 0 16-7.16 16-16V240c0-61.86-50.14-112-112-112z"/></symbol>
|
||||
<symbol id="icon-bus" fill="currentColor" stroke="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M488 128h-8V80c0-44.8-99.2-80-224-80S32 35.2 32 80v48h-8c-13.25 0-24 10.74-24 24v80c0 13.25 10.75 24 24 24h8v160c0 17.67 14.33 32 32 32v32c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32v-32h192v32c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32v-32h6.4c16 0 25.6-12.8 25.6-25.6V256h8c13.25 0 24-10.75 24-24v-80c0-13.26-10.75-24-24-24zM112 400c-17.67 0-32-14.33-32-32s14.33-32 32-32 32 14.33 32 32-14.33 32-32 32zm16-112c-17.67 0-32-14.33-32-32V128c0-17.67 14.33-32 32-32h256c17.67 0 32 14.33 32 32v128c0 17.67-14.33 32-32 32H128zm272 112c-17.67 0-32-14.33-32-32s14.33-32 32-32 32 14.33 32 32-14.33 32-32 32z"/></symbol>
|
||||
<symbol id="icon-location-marker" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path></symbol>
|
||||
<symbol id="icon-spinner" fill="currentColor" stroke="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||
<g>
|
||||
<path d="M109.25 55.5h-36l12-12a29.54 29.54 0 0 0-49.53 12H18.75A46.04 46.04 0 0 1 96.9 31.84l12.35-12.34v36zm-90.5 17h36l-12 12a29.54 29.54 0 0 0 49.53-12h16.97A46.04 46.04 0 0 1 31.1 96.16L18.74 108.5v-36z"></path>
|
||||
<animateTransform attributeName="transform" type="rotate" from="0 64 64" to="360 64 64" dur="960ms" repeatCount="indefinite"></animateTransform>
|
||||
</g>
|
||||
</symbol>
|
||||
<!-- Flags -->
|
||||
<symbol id="icon-flag-at" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd"><path fill="#fff" d="M640 480H0V0h640z"/><path fill="#ed2939" d="M640 480H0V320h640zm0-319.9H0V.1h640z"/></g></symbol>
|
||||
<symbol id="icon-flag-ch" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"><g fill-rule="evenodd" stroke-width="1pt"><path fill="#d52b1e" d="M0 0h640v480H0z"/><g fill="#fff"><path d="M170 195h300v90H170z"/><path d="M275 90h90v300h-90z"/></g></g></symbol>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
@@ -2,7 +2,8 @@
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="relative mb-8">
|
||||
<div class="relative mb-8"
|
||||
data-controller="lightbox">
|
||||
<div class="absolute top-0 right-0 mt-4 mr-4 flex space-x-2 text-white">
|
||||
<svg class="w-8 h-8 md:w-10 h-10">
|
||||
<use href="#icon-search"></use>
|
||||
@@ -12,7 +13,8 @@
|
||||
</span>
|
||||
</div>
|
||||
<f:alias map="{image: images.0}">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}" data-glightbox="gallery-{id}">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
|
||||
data-action="lightbox#open">
|
||||
<img data-src="{f:uri.image(image: image, width: '770', height: '430x+100')}"
|
||||
class="block w-full h-auto lazyload"
|
||||
alt="{image.alternative}"
|
||||
@@ -25,8 +27,10 @@
|
||||
<f:for each="{images}" as="image" iteration="iteration">
|
||||
<f:if condition="{iteration.cycle} <= 5">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
|
||||
data-glightbox data-gallery="gallery-{id}"
|
||||
data-caption="{image.originalResource.description}"
|
||||
data-action="lightbox#open"
|
||||
data-lightbox-target="thumb"
|
||||
data-lightbox-index="{iteration.cycle}"
|
||||
data-lightbox-caption="{image.originalResource.description}"
|
||||
class="block">
|
||||
<f:image image="{image}" width="150" height="85c+100" class="block w-full h-auto"
|
||||
alt="{image.originalResource.alternative}" title="{image.originalResource.title}"/>
|
||||
@@ -37,35 +41,15 @@
|
||||
</f:if>
|
||||
</f:if>
|
||||
<div class="hidden">
|
||||
<f:if condition="{thumbnails}">
|
||||
<f:then>
|
||||
<f:if condition="{images -> f:count()} == 1">
|
||||
<f:then>
|
||||
<f:alias map="{image: images.0}">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
|
||||
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}"
|
||||
title="{image.title}">1</a>
|
||||
</f:alias>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:for each="{images}" as="image" iteration="iteration">
|
||||
<f:if condition="{iteration.cycle} > 5">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
|
||||
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}"
|
||||
title="{image.originalResource.title}">{iteration.index}</a>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:for each="{images}" as="image">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
|
||||
data-glightbox data-gallery="gallery-{id}" data-caption="{image.originalResource.description}"
|
||||
title="{image.originalResource.title}">{iteration.index}</a>
|
||||
</f:for>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<f:for each="{images}" as="image" iteration="iteration">
|
||||
<f:if condition="{iteration.cycle} > 5">
|
||||
<a href="{f:uri.image(image: image, cropVariant: 'zoom', maxWidth: '2560')}"
|
||||
data-lightbox-target="thumb"
|
||||
data-lightbox-index="{iteration.cycle}"
|
||||
data-lightbox-caption="{image.originalResource.description}"
|
||||
title="{image.originalResource.title}">{iteration.index}</a>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
{product.conceptDescription -> f:format.html()}
|
||||
</div>
|
||||
<f:if condition="{product.calendarHotel} && {hotel.groupsPriceConfigs}">
|
||||
<a class="button button--full" data-glightbox
|
||||
href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
|
||||
<button class="button w-full"
|
||||
data-controller="lightbox"
|
||||
data-lightbox-url-value="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
|
||||
Preisberechnung
|
||||
</a>
|
||||
</button>
|
||||
</f:if>
|
||||
<div class="headerbar">
|
||||
Verpflegung
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
Webcam
|
||||
</p>
|
||||
<a href="{region.webcam.url}"
|
||||
data-glightbox data-caption="Webcam {region.name}"
|
||||
data-controller="lightbox"
|
||||
data-action="lightbox#open"
|
||||
data-lightbox-caption="Webcam {region.name}"
|
||||
title="Webcam {region.name}">
|
||||
<img src="{ep:uri.externalImage(url: region.webcam.url)}"
|
||||
width="330"
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<f:if condition="{region.regionMaps}">
|
||||
<div class="bg-gray-200 p-4 mb-4">
|
||||
<div class="bg-gray-200 p-4 mb-4"
|
||||
data-controller="lightbox">
|
||||
<p class="font-bold">
|
||||
Pistenplan
|
||||
</p>
|
||||
<f:for each="{region.regionMaps}" as="regionMap">
|
||||
<f:for each="{region.regionMaps}" as="regionMap" iteration="iteration">
|
||||
<a href="{f:uri.image(src: '{regionMap.uid}', treatIdAsReference: '1', maxWidth: '2560')}"
|
||||
data-glightbox data-gallery="regionmap-{region.uid}" data-caption="Pistenplan {region.name}"
|
||||
data-action="lightbox#open"
|
||||
data-lightbox-target="thumb"
|
||||
data-lightbox-index="{iteration.cycle}"
|
||||
data-lightbox-caption="Pistenplan {region.name}"
|
||||
title="Pistenplan {region.name}">
|
||||
<img class="img-responsive lazyload"
|
||||
data-src="{f:uri.image(image: regionMap, width: '330')}"
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"
|
||||
data-searchbar-target="container"
|
||||
{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="sticky"')}>
|
||||
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
|
||||
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
|
||||
pageUid="{settings.defaultSearchPageUid}" data="{'searchbar-target': 'form'}">
|
||||
<f:form.hidden property="destinationUid" data="{'searchbar-target': 'destinationUid'}"/>
|
||||
<f:form.hidden property="destinationType" data="{'searchbar-target': 'destinationType'}"/>
|
||||
<f:form.hidden property="dateFrom" data="{'searchbar-target': 'dateFrom'}"/>
|
||||
<f:form.hidden property="dateTo" data="{'searchbar-target': 'dateTo'}"/>
|
||||
<f:form.hidden property="nights" data="{'searchbar-target': 'nights'}"/>
|
||||
<f:form.hidden property="pax" data="{'searchbar-target': 'pax'}"/>
|
||||
<f:form.hidden property="priceRange" data="{'searchbar-target': 'priceRange'}"/>
|
||||
<div class="sticky:max-w-screen-xl sticky:mx-auto flex flex-col sticky:flex-row sticky:items-center">
|
||||
<div class="flex items-start justify-between sticky:flex-1 pt-8 px-8 sticky:py-4 sticky:px-0">
|
||||
<div class="flex-1"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
|
||||
placeholder="Zeitraum"
|
||||
data-searchbar-target="dateRangeLabel"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
<div class="hidden absolute z-20 top-100 mt-2 left-0 bg-white p-4 shadow-md"
|
||||
data-collapse-target="container"
|
||||
data-controller="datepicker"
|
||||
<f:if condition="{filterSettings.dateFrom} && {filterSettings.dateTo}">
|
||||
data-datepicker-date-from-value="{filterSettings.dateFrom}"
|
||||
data-datepicker-date-to-value="{filterSettings.dateTo}"
|
||||
</f:if>
|
||||
data-datepicker-min-date-value="{filterOptions.dateFrom}"
|
||||
data-datepicker-max-date-value="{filterOptions.dateTo}">
|
||||
<f:if condition="{settings.datePickerPresets}">
|
||||
<div class="flex items-center justify-between pb-2">
|
||||
<f:for each="{settings.datePickerPresets}" as="preset">
|
||||
<button class="daterange__preset"
|
||||
data-preset='{preset -> f:format.json()}'
|
||||
data-action="datepicker#selectPreset">
|
||||
<span>
|
||||
{preset.label}
|
||||
</span>
|
||||
</button>
|
||||
</f:for>
|
||||
</div>
|
||||
</f:if>
|
||||
<input type="hidden"
|
||||
data-datepicker-target="field">
|
||||
<div class="flex items-center justify-between pt-2">
|
||||
<button class="daterange__button focus:outline-none"
|
||||
data-action="collapse#close">
|
||||
schließen
|
||||
</button>
|
||||
<button class="daterange__button focus:outline-none"
|
||||
data-action="datepicker#reset">
|
||||
zurücksetzen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-24"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
|
||||
<use href="#icon-user-group"></use>
|
||||
</svg>
|
||||
<div class="hidden absolute top-100 mt-2 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll"
|
||||
data-collapse-target="container">
|
||||
<ul class="m-0 p-0">
|
||||
<f:for each="{filterOptions.pax}" as="option">
|
||||
<li>
|
||||
<a href="#" data-option-value="{option}">
|
||||
{option}
|
||||
</a>
|
||||
</li>
|
||||
</f:for>
|
||||
<li>
|
||||
<a href="{f:uri.typolink(parameter: settings.groupsPageUid)}">
|
||||
> 20
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
|
||||
placeholder="Reiseziel"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1 transform rotate-45">
|
||||
<use href="#icon-airplane"></use>
|
||||
</svg>
|
||||
<div class="hidden absolute top-100 left-0 bg-white p-4 shadow-md min-w-full h-48 overflow-y-scroll"
|
||||
data-collapse-target="container">
|
||||
<f:for each="{filterOptions.destinations}" as="destination">
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="w-1/2">
|
||||
<a href="#">
|
||||
<svg class="block w-8 h-6">
|
||||
<use href="#icon-flag-{destination.country.code -> f:format.case(mode: 'lower')}"/>
|
||||
</svg>
|
||||
<strong>
|
||||
{destination.country.label}
|
||||
</strong>
|
||||
</a>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<ul class="plain-list mb-0">
|
||||
<f:for each="{destination.regions}" as="region">
|
||||
<li>
|
||||
<a href="#">
|
||||
{region.label}
|
||||
</a>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</f:for>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="w-1/2">
|
||||
<strong>Konzept</strong>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<ul class="plain-list mb-0">
|
||||
<f:for each="{filterOptions.concepts}" as="concept">
|
||||
<li>
|
||||
<a href="#">
|
||||
{concept.name}
|
||||
</a>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<f:if condition="{filterSettings.destinationUid} || {filterSettings.conceptUid}">
|
||||
<button>
|
||||
zurücksetzen
|
||||
</button>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-32"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-r border-b border-white">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
|
||||
placeholder="Preis"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
|
||||
<use href="#icon-euro"></use>
|
||||
</svg>
|
||||
<div class="hidden absolute top-100 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll"
|
||||
data-collapse-target="container">
|
||||
<ul class="m-0 p-0">
|
||||
<li>
|
||||
<a href="#">
|
||||
beliebig
|
||||
</a>
|
||||
</li>
|
||||
<f:for each="{filterOptions.priceRanges}" as="range" iteration="iteration">
|
||||
<li>
|
||||
<a href="#">
|
||||
{range.formatted}
|
||||
</a>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="hidden sticky:block sticky:ml-4">
|
||||
<svg class="w-8 h-8 text-white">
|
||||
<use href="#icon-chevron-right"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="flex justify-end mt-4 sticky:hidden">
|
||||
<button type="submit" class="w-1/3 text-white uppercase py-2">
|
||||
Detailsuche
|
||||
</button>
|
||||
<button type="submit" class="w-1/3 text-white bg-ep-primary-light uppercase py-2">
|
||||
Alle anzeigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</f:form>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
@@ -28,8 +28,8 @@
|
||||
data="{'search-target': 'dateTo'}"/>
|
||||
<div data-controller="datepicker"
|
||||
<f:if condition="{filterSettings.dateFrom} && {filterSettings.dateTo}">
|
||||
data-datepicker-date-from-value="{filterSettings.dateFrom -> f:format.date(format: 'Y-m-d')}"
|
||||
data-datepicker-date-to-value="{filterSettings.dateTo -> f:format.date(format: 'Y-m-d')}"
|
||||
data-datepicker-date-from-value="{filterSettings.dateFrom}"
|
||||
data-datepicker-date-to-value="{filterSettings.dateTo}"
|
||||
</f:if>
|
||||
data-datepicker-min-date-value="{f:format.date(date: 'now', format: 'Y-m-d')}">
|
||||
<f:if condition="{settings.datePickerPresets}">
|
||||
@@ -150,8 +150,8 @@
|
||||
<select name="pax"
|
||||
data-action="search#filter">
|
||||
<f:for each="{filterOptions.pax}" as="option">
|
||||
<option value="<f:format.raw>{option}</f:format.raw>">
|
||||
<f:format.raw>{option}</f:format.raw>
|
||||
<option value="{option}">
|
||||
{option}
|
||||
</option>
|
||||
</f:for>
|
||||
</select>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<f:render partial="Search/Bar" arguments="{_all}"/>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
@@ -121,8 +121,8 @@
|
||||
</f:if>
|
||||
<hr>
|
||||
<span class="searchresult-item__label">
|
||||
{f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')}
|
||||
</span>
|
||||
{f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')}
|
||||
</span>
|
||||
</div>
|
||||
<div class="teaserbox__aside__bottom">
|
||||
<span class="searchresult-item__price">ab {item.minPrice} €</span>
|
||||
@@ -136,20 +136,20 @@
|
||||
Details{f:if(condition: item.hideBookingButton, else: ' & Buchen')}
|
||||
</f:link.action>
|
||||
</div>
|
||||
<button class="teaserbox-watchlist"
|
||||
data-controller="watchlist-toggle"
|
||||
data-watchlist-toggle-key-value="{item.key}"
|
||||
data-action="watchlist-toggle#toggle">
|
||||
<span data-microtip-position="top"
|
||||
role="tooltip"
|
||||
aria-label="auf die Merkliste setzen"
|
||||
data-watchlist-toggle-target="ariaLabel">
|
||||
<svg class="w-5 h-5 text-ep-secondary">
|
||||
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="teaserbox-watchlist"
|
||||
data-controller="watchlist-toggle"
|
||||
data-watchlist-toggle-key-value="{item.key}"
|
||||
data-action="watchlist-toggle#toggle">
|
||||
<span data-microtip-position="top"
|
||||
role="tooltip"
|
||||
aria-label="auf die Merkliste setzen"
|
||||
data-watchlist-toggle-target="label">
|
||||
<svg class="w-5 h-5 text-ep-secondary">
|
||||
<use href="#icon-plus-circle" data-watchlist-toggle-target="icon"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</f:for>
|
||||
</div>
|
||||
|
||||
+14
-10
@@ -7,17 +7,21 @@
|
||||
|
||||
<f:section name="Main">
|
||||
<div class="container">
|
||||
<div class="calendar calandar--single"
|
||||
x-data='calendar("<f:format.raw>{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, arguments: '{hotel: data.tx_eptheme_context_hotel}', format: 'html')}</f:format.raw>")'>
|
||||
<h2>Belegungskalender {hotel.0.data.name}</h2>
|
||||
<select class="form-control calendar__select" x-model="selectedIndex" x-on:change="load">
|
||||
<template x-for="(month, index) in selectableMonths">
|
||||
<option x-bind:value="index" x-text="month.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'})"></option>
|
||||
</template>
|
||||
<h2>Belegungskalender {hotel.0.data.name}</h2>
|
||||
<div data-controller="calendar"
|
||||
data-calendar-uri-value="{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, format: 'html')}"
|
||||
data-calendar-hotel-uid-value="{product.calendarHotel.uid}">
|
||||
<select class="w-full border-gray-400 focus:border-gray-400"
|
||||
data-calendar-target="field"
|
||||
data-action="calendar#select">
|
||||
</select>
|
||||
<div class="calendar__inner" x-cloak x-ref="calendarWrapper"></div>
|
||||
<div class="absolute top-0 left-0 inset-0 w-full h-full bg-overlay-white" x-show="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 class="mt-4"
|
||||
data-calendar-target="container"></div>
|
||||
<div class="absolute top-0 left-0 inset-0 w-full h-full bg-overlay-white"
|
||||
data-calendar-target="loadingIndicator">
|
||||
<svg class="w-6 h-6 text-gray-800 absolute top-1/2 left-1/2 transform -translate-xy-1/2">
|
||||
<use href="#icon-spinner"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+27
-25
@@ -5,36 +5,38 @@
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<div class="bg-winter">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
<div class="container grid grid-cols-3 gap-8 py-8">
|
||||
<div class="col-span-3 md:col-span-1 left-content">
|
||||
<span class="headline--primary text-white">
|
||||
SchneEPchen
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-span-3 md:col-span-2 right-content">
|
||||
<div class="w-full md:w-1/4 text-white">
|
||||
<div class="uppercase text-2xl font-bold mb-2">
|
||||
Angebot
|
||||
</div>
|
||||
<span class="headline headline--4 text-white mb-2 pb-2 border-b-2 border-white">
|
||||
{data.header}
|
||||
<div class="bg-winter my-2">
|
||||
<div class="bg-ep-primary-80">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
<div class="container grid grid-cols-3 gap-8 py-8">
|
||||
<div class="col-span-3 md:col-span-1 left-content">
|
||||
<span class="headline--primary text-white">
|
||||
SchneEPchen
|
||||
</span>
|
||||
<div class="mb-4" data-rte-content>
|
||||
{data.bodytext -> f:format.nl2br()}
|
||||
</div>
|
||||
<div class="col-span-3 md:col-span-2 right-content">
|
||||
<div class="w-full md:w-1/4 text-white">
|
||||
<div class="uppercase text-2xl font-bold mb-2">
|
||||
Angebot
|
||||
</div>
|
||||
<span class="headline headline--4 text-white mb-2 pb-2 border-b-2 border-white">
|
||||
{data.header}
|
||||
</span>
|
||||
<div class="mb-4" data-rte-content>
|
||||
{data.bodytext -> f:format.nl2br()}
|
||||
</div>
|
||||
<f:if condition="{data.header_link}">
|
||||
<a class="btn btn-warning"
|
||||
href="{f:uri.typolink(parameter: data.header_link)}"
|
||||
title="{product.name}">
|
||||
Details
|
||||
</a>
|
||||
</f:if>
|
||||
</div>
|
||||
<f:if condition="{data.header_link}">
|
||||
<a class="btn btn-warning"
|
||||
href="{f:uri.typolink(parameter: data.header_link)}"
|
||||
title="{product.name}">
|
||||
Details
|
||||
</a>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</div>
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
|
||||
xmlns:encore="http://typo3.org/ns/Ssch/Typo3Encore/ViewHelpers"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle">
|
||||
<f:image class="media-object" width="128" src="EXT:ep_theme/Resources/Public/images/logo_wide.svg" alt=""/>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<h1 class="media-heading">Preisberechnung <small>{hotel.name}</small></h1>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle">
|
||||
<f:image class="media-object" width="128" src="EXT:ep_theme/Resources/Public/images/logo_wide.svg" alt=""/>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
<h1 class="media-heading">Preisberechnung <small>{hotel.name}</small></h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<f:format.raw>
|
||||
<groups-price-calculator
|
||||
configs-json='{configs}'
|
||||
boards-json='{boards}'
|
||||
options-json='{options}'
|
||||
endpoint="{ep:uri.ajax(action: 'processForm', controller: 'AjaxGroupsPrice', pageUid: settings.defaultAjaxUid, arguments: '{hotel: hotel}')}"
|
||||
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
|
||||
>
|
||||
</groups-price-calculator>
|
||||
</f:format.raw>
|
||||
<encore:renderWebpackScriptTags entryName="groups-calculator" buildName="EpThemeConfig"/>
|
||||
</div>
|
||||
<groups-price-calculator
|
||||
:configs='{configs -> f:format.raw()}'
|
||||
:boards='{boards -> f:format.raw()}'
|
||||
:options='{options -> f:format.raw()}'
|
||||
endpoint="{ep:uri.ajax(action: 'processForm', controller: 'AjaxGroupsPrice', pageUid: settings.defaultAjaxUid, arguments: '{hotel: hotel}')}"
|
||||
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
|
||||
>
|
||||
</groups-price-calculator>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -115,9 +115,11 @@
|
||||
<div id="main" class="w-full lg:w-2/3">
|
||||
<div class="lg:pr-4">
|
||||
<!-- Loader -->
|
||||
<div class="flex items-center space-x-2 mb-4 hidden border border-ep-primary p-2"
|
||||
<div class="flex items-center space-x-2 mb-4 hidden bg-ep-secondary text-white p-2"
|
||||
data-product-details-target="loadingIndicator">
|
||||
<f:image class="block w-4 h-4" src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/>
|
||||
<svg class="w-4 h-4 text-white">
|
||||
<use href="#icon-spinner"></use>
|
||||
</svg>
|
||||
<span data-product-details-target="loadingMessage">
|
||||
Loading...
|
||||
</span>
|
||||
@@ -334,12 +336,12 @@
|
||||
<f:section name="Calendar">
|
||||
<f:if condition="{product.calendarHotel}">
|
||||
<f:if condition="{hotel.groupsPriceConfigs}">
|
||||
<a class="button button--full"
|
||||
data-glightbox
|
||||
data-gallery="groups-calculator"
|
||||
href="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}">
|
||||
<button class="button w-full"
|
||||
data-controller="lightbox"
|
||||
data-lightbox-url-value="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid)}"
|
||||
data-action="lightbox#open">
|
||||
Preisberechnung
|
||||
</a>
|
||||
</button>
|
||||
</f:if>
|
||||
<div class="bg-gray-200 p-4 relative mb-8">
|
||||
<span class="block font-bold mb-2">
|
||||
@@ -349,10 +351,8 @@
|
||||
data-calendar-uri-value="{ep:uri.ajax(action: 'range', controller: 'AjaxCalendar', extensionName: 'epproducts', pageUid: settings.defaultAjaxUid, format: 'html')}"
|
||||
data-calendar-hotel-uid-value="{product.calendarHotel.uid}">
|
||||
<select class="w-full border-gray-400 focus:border-gray-400"
|
||||
data-action="calendar#selectMonth">
|
||||
<template x-for="(month, index) in selectableMonths">
|
||||
<option value="index" data-text="month.toLocaleDateString('de-DE', {year: 'numeric', month: 'long'})"></option>
|
||||
</template>
|
||||
data-calendar-target="field"
|
||||
data-action="calendar#select">
|
||||
</select>
|
||||
<div class="mt-4"
|
||||
data-calendar-target="container"></div>
|
||||
|
||||
@@ -5,186 +5,10 @@
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<div x-data='searchBar({
|
||||
<f:format.raw>
|
||||
uri: "{ep:uri.ajax(controller: "AjaxSearchbar", action: "update", pageUid: settings.defaultAjaxUid)}",
|
||||
initialSearchParams: {searchParams -> f:format.json()},
|
||||
initialFilterOptions: {filterOptions -> f:format.json()},
|
||||
datePresets: {settings.datePickerPresets -> f:format.json()},
|
||||
sticky: <f:if condition="{fixed}"><f:then>true</f:then><f:else>false</f:else></f:if>
|
||||
</f:format.raw>
|
||||
})'>
|
||||
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="sticky"')}>
|
||||
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
|
||||
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
|
||||
pageUid="{settings.defaultSearchPageUid}" additionalAttributes="{ref: 'form'}">
|
||||
<f:form.hidden property="destinationUid" additionalAttributes="{x-model: 'searchParams.destinationUid'}"/>
|
||||
<f:form.hidden property="destinationType" additionalAttributes="{x-model: 'searchParams.destinationType'}"/>
|
||||
<f:form.hidden property="dateFrom" value="" additionalAttributes="{x-model: 'searchParams.dateFrom'}"/>
|
||||
<f:form.hidden property="dateTo" value="" additionalAttributes="{x-model: 'searchParams.dateTo'}"/>
|
||||
<f:form.hidden property="nights" additionalAttributes="{x-model: 'searchParams.nights'}"/>
|
||||
<f:form.hidden property="pax" additionalAttributes="{x-model: 'searchParams.pax'}"/>
|
||||
<f:form.hidden property="priceRange" additionalAttributes="{x-model: 'searchParams.priceRange'}"/>
|
||||
<f:form.hidden property="pageUid" additionalAttributes="{x-model: 'searchParams.pageUid'}"/>
|
||||
<div class="sticky:max-w-screen-xl sticky:mx-auto flex flex-col sticky:flex-row sticky:items-center">
|
||||
<div class="flex items-start justify-between sticky:flex-1 pt-8 px-8 sticky:py-4 sticky:px-0">
|
||||
<div class="flex-1">
|
||||
<div class="relative border-b border-r border-white mr-8" x-init="initDatePicker()">
|
||||
<input type="text"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
|
||||
placeholder="Zeitraum"
|
||||
x-bind:value="dateRangeLabel"
|
||||
x-on:click.prevent="show = show === 'dateRange' ? null : 'dateRange'"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
<div class="absolute top-100 mt-2 left-0 bg-white p-4 shadow-md" x-show="show === 'dateRange'" x-cloak>
|
||||
<template x-if="datePresets">
|
||||
<div class="flex items-center justify-between pb-2">
|
||||
<template x-for="preset in datePresets">
|
||||
<a class="daterange__preset" href="#"
|
||||
x-on:click.prevent="selectDateRangePreset(preset)">
|
||||
<i class="fa fa-calendar"></i> <span x-text="preset.label"></span>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<input x-ref="picker" type="hidden">
|
||||
<div class="flex items-center justify-between pt-2">
|
||||
<button class="daterange__button focus:outline-none" x-on:click.prevent="show = null">
|
||||
<i class="fa fa-times"></i> schließen
|
||||
</button>
|
||||
<button class="daterange__button focus:outline-none" x-on:click.prevent="resetDateRange()">
|
||||
<i class="fa fa-trash-o"></i> zurücksetzen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-24">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer"
|
||||
x-bind:class="loading && 'cursor-not-allowed'"
|
||||
x-bind:value="searchParams.pax"
|
||||
x-on:click.prevent="show = show === 'pax' ? null : 'pax'"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
|
||||
<use href="#icon-user-group"></use>
|
||||
</svg>
|
||||
<div class="absolute top-100 mt-2 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll" x-show="show === 'pax'" x-cloak>
|
||||
<ul class="m-0 p-0">
|
||||
<template x-for="paxOption in filterOptions.pax">
|
||||
<li>
|
||||
<a href="#" x-on:click.prevent="updateSearchParam('pax', paxOption)" x-text="paxOption"></a>
|
||||
</li>
|
||||
</template>
|
||||
<li><a href="{f:uri.typolink(parameter: settings.groupsPageUid)}">> 20</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
|
||||
placeholder="Reiseziel"
|
||||
x-bind:class="loading && 'cursor-not-allowed'"
|
||||
x-bind:value="destinationName"
|
||||
x-on:click.prevent="show = show === 'destination' ? null : 'destination'"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1 transform rotate-45">
|
||||
<use href="#icon-airplane"></use>
|
||||
</svg>
|
||||
<div class="absolute top-100 left-0 bg-white p-4 shadow-md min-w-full h-48 overflow-y-scroll" x-show="show === 'destination'" x-cloak>
|
||||
<template x-if="filterOptions.destinations && !conceptSelected">
|
||||
<template x-for="destination in filterOptions.destinations">
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="w-1/2">
|
||||
<a href="#" x-on:click.prevent="selectDestination(destination.country.uid, destination.country.label, 'country')">
|
||||
<img class="destination-pulldown__flag" x-bind:src="destination.country.flag" x-bind:alt="destination.country.label">
|
||||
<strong x-text="destination.country.label"></strong>
|
||||
</a>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<ul class="plain-list mb-0">
|
||||
<template x-for="region in destination.regions">
|
||||
<li>
|
||||
<a href="#" x-on:click.prevent="selectDestination(region.uid, region.label, 'region')" x-text="region.label"></a>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template x-if="filterOptions.concepts && !destinationSelected">
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="w-1/2">
|
||||
<strong>Konzept</strong>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<ul class="plain-list mb-0">
|
||||
<template x-for="concept in filterOptions.concepts">
|
||||
<li>
|
||||
<a href="#" x-on:click.prevent="selectDestination(concept.uid, concept.name, 'concept')" x-text="concept.name"></a>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="destinationSelected || conceptSelected">
|
||||
<button x-on:click.prevent="resetDestination()">
|
||||
<i class="fa fa-trash-o"></i>
|
||||
zurücksetzen
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<div class="relative border-r border-b border-white">
|
||||
<input type="text"
|
||||
class="w-full bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase px-2 py-1 cursor-pointer placeholder-white"
|
||||
placeholder="Preis"
|
||||
x-bind:class="loading && 'cursor-not-allowed'"
|
||||
x-bind:value="priceRangeLabel"
|
||||
x-on:click.prevent="show = show === 'priceRange' ? null : 'priceRange'"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1">
|
||||
<use href="#icon-euro"></use>
|
||||
</svg>
|
||||
<div class="absolute top-100 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll" x-show="show === 'priceRange'" x-transition x-cloak>
|
||||
<ul class="m-0 p-0">
|
||||
<li>
|
||||
<a href="#" x-on:click.prevent="updateSearchParam('priceRange', null)">beliebig</a>
|
||||
</li>
|
||||
<template x-for="(range, index) in filterOptions.priceRanges">
|
||||
<li>
|
||||
<a href="#"
|
||||
x-on:click.prevent="updateSearchParam('priceRange', index)"
|
||||
x-text="priceRangeFormatted(range)"></a>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="hidden sticky:block sticky:ml-4">
|
||||
<svg class="w-8 h-8 text-white">
|
||||
<use href="#icon-chevron-right"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="flex justify-end mt-4 sticky:hidden">
|
||||
<button type="submit" class="w-1/3 text-white uppercase py-2" x-bind:disabled="loading">
|
||||
Detailsuche
|
||||
</button>
|
||||
<button type="submit" class="w-1/3 text-white bg-ep-primary-light uppercase py-2" x-bind:disabled="loading">
|
||||
Alle anzeigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</f:form>
|
||||
</div>
|
||||
<div data-controller="searchbar"
|
||||
data-action="datepicker:range-select->searchbar#setDateRange"
|
||||
data-searchbar-uri-value="{ep:uri.ajax(controller: 'AjaxSearchbar', action: 'index', pageUid: settings.defaultAjaxUid, format: 'html')}">
|
||||
<f:render partial="Search/Bar" arguments="{_all}"/>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<div data-watchlist-target="container"></div>
|
||||
<div class="absolute top-0 left-0 inset-0 bg-overlay-white hidden"
|
||||
data-watchlist-target="loadingIndicator">
|
||||
<f:image class="absolute top-1/2 left-1/2 transform -translate-xy-1/2"
|
||||
src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif"
|
||||
alt="Loading"/>
|
||||
<svg class="w-6 h-6 text-ep-primary absolute top-1/2 left-1/2 transform -translate-xy-1/2">
|
||||
<use href="#icon-spinner"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</ep:container>
|
||||
|
||||
@@ -10,6 +10,7 @@ Encore
|
||||
.setPublicPath('/typo3conf/ext/ep_theme/Resources/Public/')
|
||||
.addEntry('main', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/main.js')
|
||||
.addEntry('myep', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/myep.js')
|
||||
.addEntry('groups-calculator', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/groups-calculator.js')
|
||||
.addStyleEntry('rte', './public/typo3conf/ext/ep_theme/Resources/Private/Assets/scss/rte.scss')
|
||||
.addExternals({
|
||||
jquery: 'jQuery',
|
||||
|
||||
Reference in New Issue
Block a user