Merge branch 'filtersettingsstore'

This commit is contained in:
Björn Fromme
2018-10-07 15:29:33 +02:00
24 changed files with 519 additions and 426 deletions
+16
View File
@@ -751,6 +751,12 @@
"integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
"dev": true "dev": true
}, },
"babel-plugin-syntax-object-rest-spread": {
"version": "6.13.0",
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
"dev": true
},
"babel-plugin-syntax-trailing-function-commas": { "babel-plugin-syntax-trailing-function-commas": {
"version": "6.22.0", "version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
@@ -1013,6 +1019,16 @@
"babel-runtime": "^6.22.0" "babel-runtime": "^6.22.0"
} }
}, },
"babel-plugin-transform-object-rest-spread": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
"integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=",
"dev": true,
"requires": {
"babel-plugin-syntax-object-rest-spread": "^6.8.0",
"babel-runtime": "^6.26.0"
}
},
"babel-plugin-transform-regenerator": { "babel-plugin-transform-regenerator": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
+1
View File
@@ -32,6 +32,7 @@
"devDependencies": { "devDependencies": {
"@symfony/webpack-encore": "^0.19.0", "@symfony/webpack-encore": "^0.19.0",
"autoprefixer": "^8.6.5", "autoprefixer": "^8.6.5",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"node-sass": "^4.9.3", "node-sass": "^4.9.3",
"postcss-loader": "^2.1.6", "postcss-loader": "^2.1.6",
"sass-loader": "^6.0.7", "sass-loader": "^6.0.7",
@@ -84,8 +84,8 @@ class AjaxSearchController extends ActionController
/** /**
* @param FilterSettings $filterSettings * @param FilterSettings $filterSettings
* @param integer $referringPageUid * @param integer $referringPageUid
*
* @return string * @return string
* @throws \Doctrine\DBAL\DBALException
*/ */
public function searchresultAction(FilterSettings $filterSettings = null, $referringPageUid = null) public function searchresultAction(FilterSettings $filterSettings = null, $referringPageUid = null)
{ {
@@ -115,7 +115,6 @@ class AjaxSearchController extends ActionController
$options = [ $options = [
'defaultDetailPageUid' => $this->settings['defaultDetailPageUid'], 'defaultDetailPageUid' => $this->settings['defaultDetailPageUid'],
'referringPageUid' => $referringPageUid, 'referringPageUid' => $referringPageUid,
'filterSettings' => $filterSettings,
]; ];
$this->searchResultUrlService->process($searchResult, $options); $this->searchResultUrlService->process($searchResult, $options);
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller;
* This copyright notice MUST APPEAR in all copies of the script! * This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/ ***************************************************************/
use EP\EpProducts\Domain\Model\FilterSettings;
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\HotelRepository; use EP\EpProducts\Domain\Repository\HotelRepository;
@@ -36,15 +35,12 @@ 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;
use EP\EpProducts\Service\ProductDataService; use EP\EpProducts\Service\ProductDataService;
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class ProductController extends ActionController class ProductController extends ActionController
{ {
use RequestArgumentTypeConversionTrait;
/** /**
* @var \EP\EpProducts\Domain\Repository\ProductRepository * @var \EP\EpProducts\Domain\Repository\ProductRepository
*/ */
@@ -101,22 +97,15 @@ class ProductController extends ActionController
$this->filterService = $filterService; $this->filterService = $filterService;
} }
public function initializeAction()
{
$this->processFilterSettingsArgument();
}
/** /**
* @param Product $product * @param Product $product
* @param Hotel $hotel * @param Hotel $hotel
* @param FilterSettings $filterSettings * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
* @param int $referringPageUid * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
*/ */
public function detailAction( public function detailAction(
Product $product = null, Product $product = null,
Hotel $hotel = null, Hotel $hotel = null
FilterSettings $filterSettings = null,
$referringPageUid = null
) )
{ {
// Get product to display from flexform settings if not provided via url // Get product to display from flexform settings if not provided via url
@@ -138,8 +127,6 @@ class ProductController extends ActionController
if ($product->getHotels()->count() > 1) { if ($product->getHotels()->count() > 1) {
$arguments = [ $arguments = [
'product' => $product, 'product' => $product,
'filterSettings' => $filterSettings,
'referringPageUid' => $referringPageUid,
]; ];
$this->forward('hotelList', null, null, $arguments); $this->forward('hotelList', null, null, $arguments);
} }
@@ -153,11 +140,8 @@ class ProductController extends ActionController
$noInfoConceptUids = GeneralUtility::trimExplode(',', $this->settings['noInfoConceptUids']); $noInfoConceptUids = GeneralUtility::trimExplode(',', $this->settings['noInfoConceptUids']);
$isProductWithNoInfoConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(), $noInfoConceptUids, false); $isProductWithNoInfoConcept = ($product->getConcept() !== null) && \in_array($product->getConcept()->getUid(), $noInfoConceptUids, false);
$this->view->assignMultiple([ $this->view->assignMultiple([
'filterSettings' => $filterSettings,
'hotel' => $hotel, 'hotel' => $hotel,
'product' => $product, 'product' => $product,
'referringPageUid' => $referringPageUid,
'currentPageUid' => $GLOBALS['TSFE']->id,
'isProductWithExcludedConcept' => $isProductWithExcludedConcept, 'isProductWithExcludedConcept' => $isProductWithExcludedConcept,
'isProductWithNoInfoConcept' => $isProductWithNoInfoConcept, 'isProductWithNoInfoConcept' => $isProductWithNoInfoConcept,
]); ]);
@@ -181,21 +165,16 @@ class ProductController extends ActionController
/** /**
* @param Product $product * @param Product $product
* @param FilterSettings $filterSettings
* @param int $referringPageUid
*/ */
public function hotelListAction( public function hotelListAction(
Product $product, Product $product
FilterSettings $filterSettings = null,
$referringPageUid = null
) )
{ {
$hotels = $this->hotelDataService->getList($product); $hotels = $this->hotelDataService->getList($product);
$this->view->assignMultiple([ $this->view->assignMultiple([
'filterSettings' => $filterSettings,
'hotels' => $hotels, 'hotels' => $hotels,
'product' => $product, 'product' => $product,
'referringPageUid' => $referringPageUid, 'currentPageUid' => $GLOBALS['TSFE']->id,
'linkHotel' => true, 'linkHotel' => true,
]); ]);
} }
@@ -110,25 +110,15 @@ class SearchController extends ActionController
*/ */
public function searchresultAction(SearchParams $searchParams = null, FilterSettings $filterSettings = null) public function searchresultAction(SearchParams $searchParams = null, FilterSettings $filterSettings = null)
{ {
if ($searchParams === null) { if ($searchParams !== null) {
$searchParams = new SearchParams();
} else {
$this->logSearch($searchParams); $this->logSearch($searchParams);
}
if ($filterSettings === null) {
$filterSettings = FilterSettings::fromSearchParams($searchParams); $filterSettings = FilterSettings::fromSearchParams($searchParams);
} }
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
$filterOptions = $this->filterService->getFilterOptions($filterSettings, $excludedConceptUids);
if ($filterOptions === null) {
$filterOptions = $this->filterService->getFilterOptions($this->defaultFilterSettings, $excludedConceptUids);
}
$this->view->assignMultiple([ $this->view->assignMultiple([
'detailPageUid' => $this->settings['detailPageUid'], 'detailPageUid' => $this->settings['detailPageUid'],
'referringPageUid' => $GLOBALS['TSFE']->id, 'referringPageUid' => $GLOBALS['TSFE']->id,
'filterSettings' => $filterSettings, 'filterSettings' => $filterSettings,
'filterOptions' => $filterOptions,
]); ]);
} }
@@ -129,13 +129,13 @@ class DateRepository extends AbstractRepository
$qb = $this->getDbConnection()->createQueryBuilder(); $qb = $this->getDbConnection()->createQueryBuilder();
$query = $qb $query = $qb
->select('hotel uid') ->select('hotel as uid')
->addSelectLiteral('REPLACE(hotel_name, "\r\n", " ") name') ->addSelectLiteral('REPLACE(hotel_name, "\r\n", " ") name')
->from('tx_epproducts_domain_model_date', 'date') ->from('tx_epproducts_domain_model_date', 'date')
->where('product_searchable = 1') ->where('product_searchable = 1')
->andWhere($qb->expr()->orX( ->andWhere($qb->expr()->orX(
$qb->expr()->like('LOWER(hotel_name) COLLATE utf8_bin',':searchName'), $qb->expr()->like('hotel_name',':searchName'),
$qb->expr()->like('LOWER(hotel_keywords) COLLATE utf8_bin', ':searchKeywords') $qb->expr()->like('hotel_keywords', ':searchKeywords')
)) ))
->orderBy('hotel_name') ->orderBy('hotel_name')
->groupBy('hotel_name') ->groupBy('hotel_name')
@@ -170,14 +170,14 @@ class DateRepository extends AbstractRepository
->from('tx_epproducts_domain_model_date', 'date') ->from('tx_epproducts_domain_model_date', 'date')
->where('product_searchable = 1') ->where('product_searchable = 1')
->andWhere($qb->expr()->orX( ->andWhere($qb->expr()->orX(
$qb->expr()->like('LOWER(product_name) COLLATE utf8_bin',':searchName'), $qb->expr()->like('product_name',':searchName'),
$qb->expr()->like('LOWER(product_keywords) COLLATE utf8_bin', ':searchKeywords'), $qb->expr()->like('product_keywords', ':searchKeywords'),
$qb->expr()->like('LOWER(country_name) COLLATE utf8_bin',':searchKeywords'), $qb->expr()->like('country_name',':searchKeywords'),
$qb->expr()->like('LOWER(country_keywords) COLLATE utf8_bin', ':searchKeywords'), $qb->expr()->like('country_keywords', ':searchKeywords'),
$qb->expr()->like('LOWER(region_name) COLLATE utf8_bin', ':searchKeywords'), $qb->expr()->like('region_name', ':searchKeywords'),
$qb->expr()->like('LOWER(region_keywords) COLLATE utf8_bin', ':searchKeywords'), $qb->expr()->like('region_keywords', ':searchKeywords'),
$qb->expr()->like('LOWER(city_name) COLLATE utf8_bin', ':searchKeywords'), $qb->expr()->like('city_name', ':searchKeywords'),
$qb->expr()->like('LOWER(city_keywords) COLLATE utf8_bin', ':searchKeywords') $qb->expr()->like('city_keywords', ':searchKeywords')
)) ))
->orderBy('product_name') ->orderBy('product_name')
->groupBy('product_name') ->groupBy('product_name')
@@ -218,8 +218,8 @@ class DateRepository extends AbstractRepository
->from('tx_epproducts_domain_model_date', 'date') ->from('tx_epproducts_domain_model_date', 'date')
->where('product_searchable = 1') ->where('product_searchable = 1')
->andWhere($qb->expr()->orX( ->andWhere($qb->expr()->orX(
$qb->expr()->like('LOWER(' . $type . '_name) COLLATE utf8_bin',':searchName'), $qb->expr()->like($type . '_name',':searchName'),
$qb->expr()->like('LOWER(' . $type . '_keywords) COLLATE utf8_bin', ':searchKeywords') $qb->expr()->like($type . '_keywords', ':searchKeywords')
)) ))
->orderBy($type . '_name') ->orderBy($type . '_name')
->groupBy($type . '_name') ->groupBy($type . '_name')
@@ -54,13 +54,9 @@ class SearchResultUrlService implements SingletonInterface
public function process(SearchResult $searchResult, array $options) public function process(SearchResult $searchResult, array $options)
{ {
$optionsResolver = new OptionsResolver(); $optionsResolver = new OptionsResolver();
$optionsResolver->setRequired([ 'defaultDetailPageUid', 'referringPageUid', 'filterSettings' ]); $optionsResolver->setRequired([ 'defaultDetailPageUid', 'referringPageUid' ]);
$optionsResolver->setAllowedTypes('filterSettings', FilterSettings::class);
$resolvedOptions = $optionsResolver->resolve($options); $resolvedOptions = $optionsResolver->resolve($options);
/** @var FilterSettings $filterSettings */
$filterSettings = $resolvedOptions['filterSettings'];
$encodedFilterSettings = FilterSettingsEncoder::encode($filterSettings);
$defaultDetailPageUid = $resolvedOptions['defaultDetailPageUid']; $defaultDetailPageUid = $resolvedOptions['defaultDetailPageUid'];
$referringPageUid = $resolvedOptions['referringPageUid']; $referringPageUid = $resolvedOptions['referringPageUid'];
@@ -75,7 +71,6 @@ class SearchResultUrlService implements SingletonInterface
'product' => $item['product']['uid'], 'product' => $item['product']['uid'],
'hotel' => $item['hotel']['uid'], 'hotel' => $item['hotel']['uid'],
'referringPageUid' => $referringPageUid, 'referringPageUid' => $referringPageUid,
'filterSettings' => $encodedFilterSettings,
]; ];
$uri = $this $uri = $this
->uriBuilder ->uriBuilder
@@ -136,13 +136,13 @@ trait RequestArgumentTypeConversionTrait
*/ */
static protected function convertType($value) static protected function convertType($value)
{ {
if (is_string($value) && (strtolower($value) === 'true' || strtolower($value) === 'false')) { if (\is_string($value) && (strtolower($value) === 'true' || strtolower($value) === 'false')) {
return strtolower($value) === 'true'; return strtolower($value) === 'true';
} }
if (is_numeric($value)) { if (\is_numeric($value)) {
return (int) $value; return (int) $value;
} }
if (is_array($value)) { if (\is_array($value)) {
return array_map(function($item) { return array_map(function($item) {
return static::convertType($item); return static::convertType($item);
}, $value); }, $value);
@@ -30,6 +30,8 @@ import EventPricetable from './components/EventPriceTable.vue'
import ContactForm from './components/ContactForm.vue' import ContactForm from './components/ContactForm.vue'
import WatchlistToggle from './components/WatchlistToggle.vue' import WatchlistToggle from './components/WatchlistToggle.vue'
import Watchlist from './components/Watchlist.vue' import Watchlist from './components/Watchlist.vue'
import HotelList from './components/HotelList.vue'
import ReferrerLink from './components/ReferrerLink.vue'
// Define global constants // Define global constants
const $window = $(window); const $window = $(window);
@@ -59,7 +61,9 @@ new Vue({
EventPricetable, EventPricetable,
ContactForm, ContactForm,
WatchlistToggle, WatchlistToggle,
Watchlist Watchlist,
HotelList,
ReferrerLink
}, },
data () { data () {
return { return {
@@ -0,0 +1,13 @@
export default {
priceRanges: {},
destinations: {},
concepts: {},
pax: [],
nights: [],
hotelTypes: [],
boardTypes: [],
roomTypes: [],
dateFrom: null,
dateTo: null,
busAvailable: false
}
@@ -10,6 +10,7 @@ export default {
conceptUids: [], conceptUids: [],
dateFrom: null, dateFrom: null,
dateTo: null, dateTo: null,
priceRange: 0,
nights: 0, nights: 0,
pax: 1, pax: 1,
hotelTypes: [], hotelTypes: [],
@@ -80,15 +80,21 @@
} }
} }
}, },
created () { watch: {
minDate (date) {
this.picker.set('minDate', new Date(date))
},
maxDate (date) {
this.picker.set('maxDate', new Date(date))
}
},
mounted () {
Vue.nextTick(() => { Vue.nextTick(() => {
this.picker = $(this.$refs.field).flatpickr({ this.picker = $(this.$refs.field).flatpickr({
mode: 'range', mode: 'range',
dateFormat: 'd.m.Y', dateFormat: 'd.m.Y',
locale: German, locale: German,
inline: true, inline: true,
minDate: new Date(this.minDate),
maxDate: new Date(this.maxDate),
onChange: (selectedDates) => { onChange: (selectedDates) => {
if (selectedDates.length === 2) { if (selectedDates.length === 2) {
this.selectedFrom = selectedDates[0]; this.selectedFrom = selectedDates[0];
@@ -0,0 +1,35 @@
<script>
import WatchlistToggle from './WatchlistToggle.vue'
import ReferrerLink from './ReferrerLink.vue'
import defaultFilterSettings from '../_filtersettings'
export default {
components: {
WatchlistToggle,
ReferrerLink
},
props: {
referringPageUrl: {
type: String,
required: true
},
watchlist: {
type: Array,
default () {
return [];
}
}
},
data () {
return {
filterSettings: defaultFilterSettings
}
},
created () {
if (this.$session.has('filterSettings')) {
this.filterSettings = this.$session.get('filterSettings')
}
}
}
</script>
@@ -31,6 +31,7 @@
required: true required: true
}, },
range: { range: {
type: Number,
default: 0 default: 0
}, },
showLabel: { showLabel: {
@@ -40,7 +41,7 @@
}, },
computed: { computed: {
rangeLabel () { rangeLabel () {
if (this.rangeSelected === 0) { if (this.rangeSelected === 0 || $.isEmptyObject(this.rangeOptions)) {
return 'beliebig'; return 'beliebig';
} }
let range = this.rangeOptions[this.rangeSelected]; let range = this.rangeOptions[this.rangeSelected];
@@ -64,8 +65,8 @@
this.$emit('selected', 0); this.$emit('selected', 0);
}, },
optionSelect (value) { optionSelect (value) {
this.rangeSelected = value; this.rangeSelected = parseInt(value);
this.$emit('selected', value); this.$emit('selected', parseInt(value));
} }
}, },
watch: { watch: {
@@ -16,6 +16,8 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import 'dayjs/locale/de' import 'dayjs/locale/de'
import defaultFilterSettings from '../_filtersettings'
export default { export default {
components: { components: {
DatesTable, DatesTable,
@@ -46,16 +48,12 @@
detailShow: false, detailShow: false,
loading: true, loading: true,
activeSection: null, activeSection: null,
singleDate: false singleDate: false,
filterSettings: defaultFilterSettings,
referringPage: null
} }
}, },
props: { props: {
filterSettings: {
type: Object,
default () {
return {}
}
},
argumentPrefix: { argumentPrefix: {
type: String, type: String,
required: true required: true
@@ -190,6 +188,13 @@
} }
}, },
created () { created () {
if (this.$session.has('filterSettings')) {
this.filterSettings = this.$session.get('filterSettings')
}
if (this.$session.has('referringPage')) {
this.referringPage = this.$session.get('referringPage');
this.$session.remove('referringPage');
}
EventBus.$on('loadDatesTable', () => { EventBus.$on('loadDatesTable', () => {
this.loadDatesView(); this.loadDatesView();
}); });
@@ -0,0 +1,28 @@
<template>
<a @click="storeReferringPage()">
<slot></slot>
</a>
</template>
<script>
export default {
props: {
referringPage: {
type: Object,
default () {
return {
url: null,
label: null
}
}
}
},
methods: {
storeReferringPage () {
if (this.referringPage.url && this.referringPage.label) {
this.$session.set('referringPage', { url: this.referringPage.url, label: this.referringPage.label })
}
}
}
}
</script>
@@ -18,6 +18,7 @@
import 'dayjs/locale/de' import 'dayjs/locale/de'
import defaultFilterSettings from '../_filtersettings' import defaultFilterSettings from '../_filtersettings'
import defaultFilterOptions from '../_filteroptions'
export default { export default {
components: { components: {
@@ -39,9 +40,8 @@
loading: false, loading: false,
resultData: {}, resultData: {},
resultMeta: null, resultMeta: null,
defaultFilterSettings: defaultFilterSettings, filterSettings: defaultFilterSettings,
filterSettings: this.initialFilterSettings, filterOptions: defaultFilterOptions
filterOptions: this.initialFilterOptions
} }
}, },
props: { props: {
@@ -51,18 +51,11 @@
}, },
initialFilterSettings: { initialFilterSettings: {
type: Object, type: Object,
default () { default: null
return defaultFilterSettings;
}
}, },
initialFilterOptions: { referringPageUrl: {
type: Object, type: String,
default () { default: ''
return defaultFilterSettings;
}
},
referringPageUid: {
type: Number
}, },
argumentPrefix: { argumentPrefix: {
type: String, type: String,
@@ -141,62 +134,81 @@
}); });
}, },
resetFilterSettings () { resetFilterSettings () {
this.filterSettings = this.defaultFilterSettings; this.updateFilterSettings(defaultFilterSettings);
EventBus.$emit('filterSettingsReset'); EventBus.$emit('filterSettingsReset');
this.loadResults(); this.loadResults();
}, },
updateDestination (destination) { updateDestination (destination) {
this.filterSettings.destinationUid = destination.uid; this.updateFilterSettings({
this.filterSettings.destinationType = destination.type; destinationUid: destination.uid,
this.filterSettings.destinationName = destination.name; destinationType: destination.type,
destinationName: destination.name
});
this.loadResults(); this.loadResults();
}, },
updateDestinations (destinations) { updateDestinations (destinations) {
this.filterSettings.countryUids = destinations.countryUids; this.updateFilterSettings({
this.filterSettings.regionUids = destinations.regionUids; countryUids: destinations.countryUids,
regionUids: destinations.regionUids
});
this.loadResults(); this.loadResults();
}, },
updateConcepts (conceptUids) { updateConcepts (conceptUids) {
this.filterSettings.conceptUids = conceptUids; this.updateFilterSettings({ conceptUids: conceptUids });
this.loadResults(); this.loadResults();
}, },
updateDateRange (range) { updateDateRange (range) {
this.filterSettings.dateFrom = (range.from !== null) ? dayjs(range.from).format('YYYY-MM-DD') : null; const dateFrom = (range.from !== null) ? dayjs(range.from).format('YYYY-MM-DD') : null;
this.filterSettings.dateTo = (range.to !== null) ? dayjs(range.to).format('YYYY-MM-DD') : null; const dateTo = (range.to !== null) ? dayjs(range.to).format('YYYY-MM-DD') : null;
this.updateFilterSettings({ dateFrom: dateFrom, dateTo: dateTo });
this.loadResults(); this.loadResults();
}, },
updateDateSelectMobile () { updateDateSelectMobile () {
if (this.filterSettings.dateFrom === '') { let dateFrom = this.filterSettings.dateFrom;
this.filterSettings.dateFrom = null; let dateTo = this.filterSettings.dateTo;
if (dateFrom === '') {
dateFrom = null;
} }
if (this.filterSettings.dateTo === '') { if (dateTo === '') {
this.filterSettings.dateTo = null; dateTo = null;
} }
this.updateFilterSettings({ dateFrom: dateFrom, dateTo: dateTo });
this.loadResults(); this.loadResults();
}, },
updatePriceRange (range) { updatePriceRange (range) {
this.filterSettings.priceRange = range; this.updateFilterSettings({ priceRange: range });
this.loadResults(); this.loadResults();
}, },
updatePax (pax) { updatePax (pax) {
this.filterSettings.pax = pax; this.updateFilterSettings({ pax: pax });
this.loadResults(); this.loadResults();
}, },
updateHotelTypes (hotelTypes) { updateHotelTypes (hotelTypes) {
this.filterSettings.hotelTypes = hotelTypes; this.updateFilterSettings({ hotelTypes: hotelTypes });
this.loadResults(); this.loadResults();
}, },
updateBoardTypes (hotelTypes) { updateBoardTypes (boardTypes) {
this.filterSettings.boardTypes = hotelTypes; this.updateFilterSettings({ boardTypes: boardTypes });
this.loadResults(); this.loadResults();
}, },
updateRoomTypes (roomTypes) { updateRoomTypes (roomTypes) {
this.filterSettings.roomTypes = roomTypes; this.updateFilterSettings({ roomTypes: roomTypes });
this.loadResults(); this.loadResults();
}, },
updateBus (bus) { updateBus (bus) {
this.filterSettings.bus = bus; this.updateFilterSettings({ bus: bus });
this.loadResults(); this.loadResults();
},
updateFilterSettings (settings) {
this.filterSettings = { ...this.filterSettings, ...settings };
this.$session.set('filterSettings', this.filterSettings);
}
},
created () {
if (!$.isEmptyObject(this.initialFilterSettings)) {
this.updateFilterSettings(this.initialFilterSettings)
} else if (this.$session.has('filterSettings')) {
this.updateFilterSettings(this.$session.get('filterSettings'))
} }
}, },
mounted () { mounted () {
@@ -2,7 +2,8 @@
<div class="teaserbox teaserbox--landscape teaserbox--searchresult searchresult-item"> <div class="teaserbox teaserbox--landscape teaserbox--searchresult searchresult-item">
<div class="teaserbox__image"> <div class="teaserbox__image">
<img class="teaserbox__flag" :src="item.country.flag" :alt="item.country.name"/> <img class="teaserbox__flag" :src="item.country.flag" :alt="item.country.name"/>
<a :href="item.detailPageUri" rel="nofollow"> <referrer-link :href="item.detailPageUri" :referring-page="{ url: referringPageUrl, label: 'zurück zum Suchergebnis' }"
rel="nofollow" title="Details &amp; Buchen">
<picture class="searchresult-item__image"> <picture class="searchresult-item__image">
<source media="(max-width: 768px)" :srcset="item.hotel.images.resized.s[0].url + ' 220w'"/> <source media="(max-width: 768px)" :srcset="item.hotel.images.resized.s[0].url + ' 220w'"/>
<source media="(max-width: 1200px)" :srcset="item.hotel.images.resized.l[0].url + ' 280w'"/> <source media="(max-width: 1200px)" :srcset="item.hotel.images.resized.l[0].url + ' 280w'"/>
@@ -11,7 +12,7 @@
:title="item.hotel.images.resized.s[0].title" :title="item.hotel.images.resized.s[0].title"
:alt="item.hotel.images.resized.s[0].alt"> :alt="item.hotel.images.resized.s[0].alt">
</picture> </picture>
</a> </referrer-link>
<div class="concept-badge" :id="section.concept.code" <div class="concept-badge" :id="section.concept.code"
:class=" 'concept-badge--' + section.concept.code">{{ section.concept.name }}</div> :class=" 'concept-badge--' + section.concept.code">{{ section.concept.name }}</div>
</div> </div>
@@ -44,19 +45,21 @@
<div class="teaserbox__aside__top"> <div class="teaserbox__aside__top">
<span class="searchresult-item__label">{{ item.dates.length > 1 ? 'Zeitraum' : 'Termin' }}</span> <span class="searchresult-item__label">{{ item.dates.length > 1 ? 'Zeitraum' : 'Termin' }}</span>
<span class="searchresult-item__daterange">{{ dateRange }}</span> <span class="searchresult-item__daterange">{{ dateRange }}</span>
<a :href="item.detailPageUri" rel="nofollow" v-if="item.dates.length && !item.daytrip"> <referrer-link :href="item.detailPageUri" :referring-page="{ url: referringPageUrl, label: 'zurück zum Suchergebnis' }"
rel="nofollow" title="Details &amp; Buchen" v-if="item.dates.length && !item.daytrip">
<span class="searchresult-item__daterange"> <span class="searchresult-item__daterange">
<i class="fa fa-arrow-circle-right"></i> {{ item.dates.length }} Termin{{ item.dates.length > 1 ? 'e' : '' }} <i class="fa fa-arrow-circle-right"></i> {{ item.dates.length }} Termin{{ item.dates.length > 1 ? 'e' : '' }}
</span> </span>
</a> </referrer-link>
<hr> <hr>
<span class="searchresult-item__label">{{ item.daytrip ? 'Tageweise buchbar' : nightsOptions }}</span> <span class="searchresult-item__label">{{ item.daytrip ? 'Tageweise buchbar' : nightsOptions }}</span>
</div> </div>
<div class="teaserbox__aside__bottom"> <div class="teaserbox__aside__bottom">
<span class="searchresult-item__price">ab {{ item.minPrice }} </span> <span class="searchresult-item__price">ab {{ item.minPrice }} </span>
<a :href="item.detailPageUri" class="button button--full searchresult-item__button" rel="nofollow"> <referrer-link :href="item.detailPageUri" :referring-page="{ url: referringPageUrl, label: 'zurück zum Suchergebnis' }"
class="button button--full searchresult-item__button" rel="nofollow" title="Details &amp; Buchen">
Details &amp; Buchen Details &amp; Buchen
</a> </referrer-link>
</div> </div>
</div> </div>
<watchlist-toggle :uid="item.key" :watchlist="watchlist" class="teaserbox-watchlist"></watchlist-toggle> <watchlist-toggle :uid="item.key" :watchlist="watchlist" class="teaserbox-watchlist"></watchlist-toggle>
@@ -67,10 +70,12 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import 'dayjs/locale/de' import 'dayjs/locale/de'
import WatchlistToggle from './WatchlistToggle.vue' import WatchlistToggle from './WatchlistToggle.vue'
import ReferrerLink from './ReferrerLink.vue'
export default { export default {
components: { components: {
WatchlistToggle WatchlistToggle,
ReferrerLink
}, },
props: { props: {
item: { item: {
@@ -86,6 +91,10 @@
default () { default () {
return []; return [];
} }
},
referringPageUrl: {
type: String,
default: null
} }
}, },
computed: { computed: {
@@ -80,48 +80,33 @@
<span class="searchresult-item__label">{f:if(condition: '{item.dates -> f:count()} > 1', then: 'Zeitraum', else: 'Termin')}</span> <span class="searchresult-item__label">{f:if(condition: '{item.dates -> f:count()} > 1', then: 'Zeitraum', else: 'Termin')}</span>
<span class="searchresult-item__daterange">{ep:dateRange(dateFrom: item.minDateStart, dateTo: item.maxDateEnd, includeLabel: 0)}</span> <span class="searchresult-item__daterange">{ep:dateRange(dateFrom: item.minDateStart, dateTo: item.maxDateEnd, includeLabel: 0)}</span>
<f:if condition="{item.dates -> f:count()} > 1"> <f:if condition="{item.dates -> f:count()} > 1">
<f:link.action <referrer-link href="{f:uri.action(pageUid: detailPage, action: 'detail', controller: 'Product', arguments: '{product: product.uid, hotel: item.hotel.uid}')}"
pageUid="{product.detailPage}" :referring-page="{ url: referringPageUrl, label: 'zurück' }"
action="detail" class="button button--full searchresult-item__button" rel="nofollow">
controller="Product"
arguments="{
product: product.uid,
hotel: item.hotel.uid,
referringPageUid: referringPageUid,
filterSettings: '{filterSettings -> ep:filterSettingsEncoder()}'
}"
additionalAttributes="{rel: 'nofollow'}"
>
<span class="searchresult-item__daterange"> <span class="searchresult-item__daterange">
<i class="fa fa-arrow-circle-right"></i> {item.dates -> f:count()} Termin{f:if(condition: '{item.dates -> f:count()} > 1', then: 'e')} <i class="fa fa-arrow-circle-right"></i> {item.dates -> f:count()} Termin{f:if(condition: '{item.dates -> f:count()} > 1', then: 'e')}
</span> </span>
</f:link.action> </referrer-link>
</f:if> </f:if>
<hr> <hr>
<span class="searchresult-item__label">{ep:nightsOptions(options: item.nights)}</span> <span class="searchresult-item__label">{ep:nightsOptions(options: item.nights)}</span>
</div> </div>
<div class="teaserbox__aside__bottom"> <div class="teaserbox__aside__bottom">
<span class="searchresult-item__price">ab {item.minPrice} €</span> <span class="searchresult-item__price">ab {item.minPrice} €</span>
<f:link.action <referrer-link href="{f:uri.action(pageUid: detailPage, action: 'detail', controller: 'Product', arguments: '{product: product.uid, hotel: item.hotel.uid}')}"
pageUid="{product.detailPage}" :referring-page="{ url: referringPageUrl, label: 'zurück' }"
action="detail" class="button button--full searchresult-item__button" rel="nofollow">
controller="Product" Details &amp; Buchen
arguments="{product: product.uid, hotel: item.hotel.uid, referringPageUid: referringPageUid, filterSettings: '{filterSettings -> ep:filterSettingsEncoder()}'}" </referrer-link>
class="button button--full searchresult-item__button"
additionalAttributes="{rel: 'nofollow'}">Details &amp; Buchen</f:link.action>
</div> </div>
</f:then> </f:then>
<f:else> <f:else>
<div class="teaserbox__aside__top">&nbsp;</div> <div class="teaserbox__aside__top">&nbsp;</div>
<div class="teaserbox__aside__bottom"> <div class="teaserbox__aside__bottom">
<span class="searchresult-item__price">ausgebucht</span> <span class="searchresult-item__price">ausgebucht</span>
<f:link.action <referrer-link href="{f:uri.action(pageUid: detailPage, action: 'detail', controller: 'Product', arguments: '{product: product.uid, hotel: item.hotel.uid}')}"
pageUid="{product.detailPage}" :referring-page="{ url: referringPageUrl, label: 'zurück' }"
action="detail" class="button button--full searchresult-item__button" rel="nofollow">Details</referrer-link>
controller="Product"
arguments="{product: product.uid, hotel: item.hotel.uid, referringPageUid: referringPageUid, filterSettings: '{filterSettings -> ep:filterSettingsEncoder()}'}"
class="button button--full searchresult-item__button"
additionalAttributes="{rel: 'nofollow'}">Details</f:link.action>
</div> </div>
</f:else> </f:else>
</f:if> </f:if>
@@ -60,20 +60,8 @@
</f:link.typolink> </f:link.typolink>
</f:if> </f:if>
</div> </div>
<f:if condition="{referringPageUid}"> <div class="ep-backlink" v-if="referringPage" v-cloak>
<div class="ep-backlink"> <a :href="referringPage.url">{{ referringPage.label }}</a>
<f:if condition="{referringPageUid} == {settings.defaultSearchPageUid}"> </div>
<f:then>
<f:link.action pageUid="{referringPageUid}"
action="searchresult"
controller="Search"
arguments="{filterSettings: '{filterSettings -> ep:filterSettingsEncoder()}'}">zurück zum Suchergebnis</f:link.action>
</f:then>
<f:else>
<f:link.typolink parameter="{referringPageUid}">zurück</f:link.typolink>
</f:else>
</f:if>
</div>
</f:if>
{v:render.template(file: 'EXT:ep_theme/Resources/Private/Partials/Breadcrumb.html')} {v:render.template(file: 'EXT:ep_theme/Resources/Private/Partials/Breadcrumb.html')}
</div> </div>
@@ -7,235 +7,236 @@
<f:layout name="Default"/> <f:layout name="Default"/>
<f:section name="Main"> <f:section name="Main">
<f:render partial="Product/HeaderDetail" arguments="{_all}"/> <noscript><h1>{product.headline}</h1></noscript>
<div class="container" id="detail"> <product-detail
<noscript><h1>{product.headline}</h1></noscript> :uris='{
<product-detail dates: "<f:format.raw>{ep:uri.ajax(action: 'dates', controller: 'AjaxDate', arguments: '{product: product, hotel: hotel}', format: 'json', noCacheHash: 1)}</f:format.raw>",
:filter-settings='<v:format.json.encode>{filterSettings}</v:format.json.encode>' contingents: "<f:format.raw>{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', noCacheHash: 1)}</f:format.raw>",
:uris='{ rooms: "<f:format.raw>{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', noCacheHash: 1)}</f:format.raw>",
dates: "<f:format.raw>{ep:uri.ajax(action: 'dates', controller: 'AjaxDate', arguments: '{product: product, hotel: hotel}', format: 'json', noCacheHash: 1)}</f:format.raw>", pricetable: "<f:format.raw>{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'json', noCacheHash: 1)}</f:format.raw>"
contingents: "<f:format.raw>{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', noCacheHash: 1)}</f:format.raw>", }'
rooms: "<f:format.raw>{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', noCacheHash: 1)}</f:format.raw>", argument-prefix='<ep:argumentPrefix pluginName="Ajax" />'
pricetable: "<f:format.raw>{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'json', noCacheHash: 1)}</f:format.raw>" :hotel-first="{f:if(condition: settings.hotelOnTop, then: 'true', else: 'false')}"
}' :fb-pixel-data="{ productUid: <f:format.raw>{product.uid}</f:format.raw>, nameInternal: '<f:format.raw>{product.nameInternal}</f:format.raw>' }"
argument-prefix='<ep:argumentPrefix pluginName="Ajax" />' :watchlist="watchlist"
:hotel-first="{f:if(condition: settings.hotelOnTop, then: 'true', else: 'false')}" :daytrip="{product.daytrip -> v:variable.convert(type: 'int')}"
:fb-pixel-data="{ productUid: <f:format.raw>{product.uid}</f:format.raw>, nameInternal: '<f:format.raw>{product.nameInternal}</f:format.raw>' }" inline-template>
:watchlist="watchlist" <article>
:daytrip="{product.daytrip -> v:variable.convert(type: 'int')}" <f:render partial="Product/HeaderDetail" arguments="{_all}"/>
inline-template> <div class="container" id="detail">
<div class="product-detail-wrapper"> <div class="product-detail-wrapper">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<f:render partial="Header/Header" arguments="{layout: 8, subheader: product.name, header: product.headline}"/> <f:render partial="Header/Header" arguments="{layout: 8, subheader: product.name, header: product.headline}"/>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-push-8">
<div class="row">
<div class="col-xs-12">
<f:if condition="{settings.hotelOnTop}">
<f:then>
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'hotel'}"
@click.prevent="showHtml('hotel')">Unterkunft</button>
</f:then>
<f:else>
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'dates'}"
@click.prevent="loadDatesView()">Termine/Preise</button>
</f:else>
</f:if>
</div>
</div>
<div class="row">
<div class="col-xs-6 col-md-12">
<f:if condition="{settings.hotelOnTop}">
<f:then>
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'dates'}"
@click.prevent="loadDatesView()">Termine/Preise</button>
</f:then>
<f:else>
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'hotel'}"
@click.prevent="showHtml('hotel')">Unterkunft</button>
</f:else>
</f:if>
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'region'}"
@click.prevent="showHtml('region')">Gebiet</button>
</div>
<div class="col-xs-6 col-md-12">
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'city'}"
@click.prevent="showHtml('city')">Ort</button>
<f:if condition="{product.journey}">
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'journey'}"
@click.prevent="showHtml('journey')">Anreise</button>
</f:if>
</div>
<div class="col-xs-12">
<p>
<watchlist-toggle :watchlist="watchlist" uid="{hotel.uid}:{product.uid}"
layout="link"></watchlist-toggle>
</p>
</div>
</div>
<div class="hidden-xs">
<f:render section="Calendar" arguments="{_all}"/>
<f:render partial="Facts" arguments="{facts: product.facts}"/>
<f:if condition="{product.video}">
<div class="video">
<iframe class="video__frame" width="560" height="315" frameborder="0" allowfullscreen
src="{ep:uri.video(value: product.video)}"></iframe>
</div>
</f:if>
<div class="ep-sidebar ep-snow">
<p><strong>Lage</strong></p>
<div>
<osm-map lat="{hotel.latitude}" lng="{hotel.longitude}" :zoom="12"
external-link="{f:uri.typolink(parameter: hotel.externalLink)}"></osm-map>
</div>
</div>
<f:render partial="Region/Webcam" arguments="{region: region}"/>
<f:render partial="RegionMaps" arguments="{region: product.region}"/>
<f:render partial="RegionAward" arguments="{region: product.region}"/>
<f:if condition="{product.banner}">
<div class="ep-sidebar ep-snow">
<f:link.typolink parameter="{product.banner.originalResource.link}"
title="{product.banner.title}">
<f:image src="{product.banner.uid}" treatIdAsReference="1"
class="scale" alt="{product.banner.alternative}"/>
</f:link.typolink>
</div>
</f:if>
<f:render partial="Rating" arguments="{product: product, targetPageUid: settings.ratingPageUid, label: settings.ratingWidgetLabel}"/>
<f:render partial="Snowreport" arguments="{snowreport: product.region.snowreport}"/>
<f:render partial="Officetip" arguments="{officetip: product.officetip}"/>
<f:render partial="Teamer" arguments="{teamer: hotel.teamer}"/>
</div> </div>
</div> </div>
<div id="main" class="col-md-8 col-md-pull-4"> <div class="row">
<!-- Dates --> <div class="col-md-4 col-md-push-8">
<div v-cloak v-show="!loading && datesShow"> <div class="row">
<f:if condition="{isProductWithExcludedConcept}"> <div class="col-xs-12">
<f:if condition="{product.region.season} == 'w'"> <f:if condition="{settings.hotelOnTop}">
<div class="alert alert-info"> <f:then>
<p><strong>Gruppenrabatt bis zu 100€ möglich</strong></p> <button class="button button--light button--sidebar"
<p>Für Gruppen bis 15 Personen könnt ihr <f:link.typolink parameter="31">hier direkt buchen</f:link.typolink>. :class="{ 'button--active': activeSection === 'hotel'}"
Für Gruppen ab 16 Personen erstellen wir euch ein konkretes Angebot zu günstigen @click.prevent="showHtml('hotel')">Unterkunft</button>
Gruppenkonditionen zu dieser Reise. Anfrage via Tel. 0221 - 272 276 18 oder mit einer </f:then>
<f:link.typolink parameter="{settings.defaultContactFormPageUid}">Kurzanfrage</f:link.typolink>!</p>
</div>
</f:if>
</f:if>
<f:if condition="{product.daytrip}">
<f:then>
<date-select
hotel-name="{hotel.name}"
:hotel-uid="{hotel.uid}"
:product-uid="{product.uid}"
:contingent-endpoint-uri="uris.contingents"
:rooms-endpoint-uri="uris.rooms"
:argument-prefix="argumentPrefix"
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
></date-select>
</f:then>
<f:else>
<template v-if="available">
<dates-table :dates-rows="datesRows"
:alt-product-link="altProductLink"
:alt-label="altLabel"
hotel-name="{hotel.name}"
:bookable="bookable"
></dates-table>
</template>
<f:if condition="{isProductWithNoInfoConcept}">
<f:else> <f:else>
<template v-else> <button class="button button--light button--sidebar"
<div class="alert alert-info"> :class="{ 'button--active': activeSection === 'dates'}"
Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei, @click.prevent="loadDatesView()">Termine/Preise</button>
eine andere Reise zu finden. Ruf uns an unter <a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}" title="Service Telefon anrufen" rel="nofollow">{settings.phoneNumber}</a>
oder schreibe an <f:link.email email="[email protected]"/>.
</div>
</template>
</f:else> </f:else>
</f:if> </f:if>
</f:else>
</f:if>
<f:render partial="Product/Details" arguments="{_all}"/>
</div>
<!-- /Dates -->
<!-- Prices -->
<div v-cloak v-show="!loading && priceTableShow">
<price-table :price-table-rows="priceTableRows" :services-included="servicesIncluded"
:date-label="dateLabel" :single-date="singleDate"></price-table>
<f:render partial="Product/Details" arguments="{_all}"/>
</div>
<!-- /Prices -->
<!-- Hotel -->
<div v-show="detailShow && activeSection === 'hotel'">
<f:render partial="Hotel/Details" arguments="{hotel: hotel, bars: 1, showNonBookableHint: 0}"/>
</div>
<!-- /Hotel -->
<!-- City -->
<div v-show="detailShow && activeSection === 'city'">
<f:render partial="City/Details" arguments="{city: product.city, bars: 1}"/>
</div>
<!-- /City -->
<!-- Region -->
<div v-show="detailShow && activeSection === 'region'">
<f:render partial="Region/Details" arguments="{region: product.region, bars: 1}"/>
</div>
<!-- /Region -->
<!-- Journey -->
<div v-show="detailShow && activeSection === 'journey'">
<f:render partial="Journey/Details" arguments="{journey: product.journey, bars: 1}"/>
</div>
<!-- /Journey -->
<!-- Loader -->
<div v-show="loading" class="loader"><p>Loading... <f:image src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/></p></div>
<!-- /Loader -->
<div class="hidden-md hidden-lg">
<f:render section="Calendar" arguments="{_all}"/>
<f:render partial="Facts" arguments="{facts: product.facts}"/>
<f:if condition="{product.video}">
<div class="video">
<iframe class="video__frame" width="560" height="315" frameborder="0" allowfullscreen
src="{ep:uri.video(value: product.video)}"></iframe>
</div>
</f:if>
<div class="ep-sidebar ep-snow">
<p><strong>Lage</strong></p>
<div>
<osm-map lat="{hotel.latitude}" lng="{hotel.longitude}" :zoom="12"
external-link="{f:uri.typolink(parameter: hotel.externalLink)}"></osm-map>
</div> </div>
</div> </div>
<f:render partial="Region/Webcam" arguments="{region: region}"/> <div class="row">
<f:render partial="RegionMaps" arguments="{region: product.region}"/> <div class="col-xs-6 col-md-12">
<f:render partial="RegionAward" arguments="{region: product.region}"/> <f:if condition="{settings.hotelOnTop}">
<f:if condition="{product.banner}"> <f:then>
<div class="ep-sidebar ep-snow"> <button class="button button--light button--sidebar"
<f:link.typolink parameter="{product.banner.originalResource.link}" :class="{ 'button--active': activeSection === 'dates'}"
title="{product.banner.title}"> @click.prevent="loadDatesView()">Termine/Preise</button>
<f:image src="{product.banner.uid}" treatIdAsReference="1" </f:then>
class="scale" alt="{product.banner.alternative}"/> <f:else>
</f:link.typolink> <button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'hotel'}"
@click.prevent="showHtml('hotel')">Unterkunft</button>
</f:else>
</f:if>
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'region'}"
@click.prevent="showHtml('region')">Gebiet</button>
</div> </div>
</f:if> <div class="col-xs-6 col-md-12">
<f:render partial="Rating" arguments="{product: product, targetPageUid: settings.ratingPageUid, label: settings.ratingWidgetLabel}"/> <button class="button button--light button--sidebar"
<f:render partial="Snowreport" arguments="{snowreport: product.region.snowreport}"/> :class="{ 'button--active': activeSection === 'city'}"
<f:render partial="Officetip" arguments="{officetip: product.officetip}"/> @click.prevent="showHtml('city')">Ort</button>
<f:render partial="Teamer" arguments="{teamer: hotel.teamer}"/> <f:if condition="{product.journey}">
<button class="button button--light button--sidebar"
:class="{ 'button--active': activeSection === 'journey'}"
@click.prevent="showHtml('journey')">Anreise</button>
</f:if>
</div>
<div class="col-xs-12">
<p>
<watchlist-toggle :watchlist="watchlist" uid="{hotel.uid}:{product.uid}"
layout="link"></watchlist-toggle>
</p>
</div>
</div>
<div class="hidden-xs">
<f:render section="Calendar" arguments="{_all}"/>
<f:render partial="Facts" arguments="{facts: product.facts}"/>
<f:if condition="{product.video}">
<div class="video">
<iframe class="video__frame" width="560" height="315" frameborder="0" allowfullscreen
src="{ep:uri.video(value: product.video)}"></iframe>
</div>
</f:if>
<div class="ep-sidebar ep-snow">
<p><strong>Lage</strong></p>
<div>
<osm-map lat="{hotel.latitude}" lng="{hotel.longitude}" :zoom="12"
external-link="{f:uri.typolink(parameter: hotel.externalLink)}"></osm-map>
</div>
</div>
<f:render partial="Region/Webcam" arguments="{region: region}"/>
<f:render partial="RegionMaps" arguments="{region: product.region}"/>
<f:render partial="RegionAward" arguments="{region: product.region}"/>
<f:if condition="{product.banner}">
<div class="ep-sidebar ep-snow">
<f:link.typolink parameter="{product.banner.originalResource.link}"
title="{product.banner.title}">
<f:image src="{product.banner.uid}" treatIdAsReference="1"
class="scale" alt="{product.banner.alternative}"/>
</f:link.typolink>
</div>
</f:if>
<f:render partial="Rating" arguments="{product: product, targetPageUid: settings.ratingPageUid, label: settings.ratingWidgetLabel}"/>
<f:render partial="Snowreport" arguments="{snowreport: product.region.snowreport}"/>
<f:render partial="Officetip" arguments="{officetip: product.officetip}"/>
<f:render partial="Teamer" arguments="{teamer: hotel.teamer}"/>
</div>
</div>
<div id="main" class="col-md-8 col-md-pull-4">
<!-- Dates -->
<div v-cloak v-show="!loading && datesShow">
<f:if condition="{isProductWithExcludedConcept}">
<f:if condition="{product.region.season} == 'w'">
<div class="alert alert-info">
<p><strong>Gruppenrabatt bis zu 100€ möglich</strong></p>
<p>Für Gruppen bis 15 Personen könnt ihr <f:link.typolink parameter="31">hier direkt buchen</f:link.typolink>.
Für Gruppen ab 16 Personen erstellen wir euch ein konkretes Angebot zu günstigen
Gruppenkonditionen zu dieser Reise. Anfrage via Tel. 0221 - 272 276 18 oder mit einer
<f:link.typolink parameter="{settings.defaultContactFormPageUid}">Kurzanfrage</f:link.typolink>!</p>
</div>
</f:if>
</f:if>
<f:if condition="{product.daytrip}">
<f:then>
<date-select
hotel-name="{hotel.name}"
:hotel-uid="{hotel.uid}"
:product-uid="{product.uid}"
:contingent-endpoint-uri="uris.contingents"
:rooms-endpoint-uri="uris.rooms"
:argument-prefix="argumentPrefix"
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
></date-select>
</f:then>
<f:else>
<template v-if="available">
<dates-table :dates-rows="datesRows"
:alt-product-link="altProductLink"
:alt-label="altLabel"
hotel-name="{hotel.name}"
:bookable="bookable"
></dates-table>
</template>
<f:if condition="{isProductWithNoInfoConcept}">
<f:else>
<template v-else>
<div class="alert alert-info">
Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei,
eine andere Reise zu finden. Ruf uns an unter <a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}" title="Service Telefon anrufen" rel="nofollow">{settings.phoneNumber}</a>
oder schreibe an <f:link.email email="[email protected]"/>.
</div>
</template>
</f:else>
</f:if>
</f:else>
</f:if>
<f:render partial="Product/Details" arguments="{_all}"/>
</div>
<!-- /Dates -->
<!-- Prices -->
<div v-cloak v-show="!loading && priceTableShow">
<price-table :price-table-rows="priceTableRows" :services-included="servicesIncluded"
:date-label="dateLabel" :single-date="singleDate"></price-table>
<f:render partial="Product/Details" arguments="{_all}"/>
</div>
<!-- /Prices -->
<!-- Hotel -->
<div v-show="detailShow && activeSection === 'hotel'">
<f:render partial="Hotel/Details" arguments="{hotel: hotel, bars: 1, showNonBookableHint: 0}"/>
</div>
<!-- /Hotel -->
<!-- City -->
<div v-show="detailShow && activeSection === 'city'">
<f:render partial="City/Details" arguments="{city: product.city, bars: 1}"/>
</div>
<!-- /City -->
<!-- Region -->
<div v-show="detailShow && activeSection === 'region'">
<f:render partial="Region/Details" arguments="{region: product.region, bars: 1}"/>
</div>
<!-- /Region -->
<!-- Journey -->
<div v-show="detailShow && activeSection === 'journey'">
<f:render partial="Journey/Details" arguments="{journey: product.journey, bars: 1}"/>
</div>
<!-- /Journey -->
<!-- Loader -->
<div v-show="loading" class="loader"><p>Loading... <f:image src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading"/></p></div>
<!-- /Loader -->
<div class="hidden-md hidden-lg">
<f:render section="Calendar" arguments="{_all}"/>
<f:render partial="Facts" arguments="{facts: product.facts}"/>
<f:if condition="{product.video}">
<div class="video">
<iframe class="video__frame" width="560" height="315" frameborder="0" allowfullscreen
src="{ep:uri.video(value: product.video)}"></iframe>
</div>
</f:if>
<div class="ep-sidebar ep-snow">
<p><strong>Lage</strong></p>
<div>
<osm-map lat="{hotel.latitude}" lng="{hotel.longitude}" :zoom="12"
external-link="{f:uri.typolink(parameter: hotel.externalLink)}"></osm-map>
</div>
</div>
<f:render partial="Region/Webcam" arguments="{region: region}"/>
<f:render partial="RegionMaps" arguments="{region: product.region}"/>
<f:render partial="RegionAward" arguments="{region: product.region}"/>
<f:if condition="{product.banner}">
<div class="ep-sidebar ep-snow">
<f:link.typolink parameter="{product.banner.originalResource.link}"
title="{product.banner.title}">
<f:image src="{product.banner.uid}" treatIdAsReference="1"
class="scale" alt="{product.banner.alternative}"/>
</f:link.typolink>
</div>
</f:if>
<f:render partial="Rating" arguments="{product: product, targetPageUid: settings.ratingPageUid, label: settings.ratingWidgetLabel}"/>
<f:render partial="Snowreport" arguments="{snowreport: product.region.snowreport}"/>
<f:render partial="Officetip" arguments="{officetip: product.officetip}"/>
<f:render partial="Teamer" arguments="{teamer: hotel.teamer}"/>
</div>
</div> </div>
</div> </div>
</div> <facebook-pixel :values="{ content_ids: [ fbPixelData.productUid ], content_name: fbPixelData.nameInternal, content_type: 'product' }"></facebook-pixel>
<facebook-pixel :values="{ content_ids: [ fbPixelData.productUid ], content_name: fbPixelData.nameInternal, content_type: 'product' }"></facebook-pixel> </article>
</div> </product-detail>
</product-detail> </div>
</div> </div>
<f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/> <f:render partial="Faq" arguments="{faqs: product.faqs, settings: settings}"/>
</f:section> </f:section>
@@ -5,48 +5,55 @@
<f:layout name="Default"/> <f:layout name="Default"/>
<f:section name="Main"> <f:section name="Main">
<f:render partial="Product/HeaderDetail" arguments="{_all}"/> <hotel-list
<div class="container"> :watchlist="watchlist"
<div class="row"> referring-page-url="{f:uri.typolink(parameter: currentPageUid)}"
<div class="col-md-12"> inline-template>
<f:render partial="Header/Header" arguments="{layout: 8, subheader: product.name, header: product.headline}"/> <article>
<f:render partial="Product/HeaderDetail" arguments="{_all}"/>
<div class="container">
<div class="row">
<div class="col-md-12">
<f:render partial="Header/Header" arguments="{layout: 8, subheader: product.name, header: product.headline}"/>
</div>
</div>
<div class="row">
<div class="col-md-12">
{product.teaserLong -> f:format.html()}
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="ep-headbar">Wähle deine Unterkunft</div>
</div>
</div>
</div> </div>
</div> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-xs-12">
{product.teaserLong -> f:format.html()} <f:if condition="{hotels}">
<f:then>
<f:for each="{hotels}" as="item">
<f:render partial="Hotel/TeaserWide" section="Teaser" arguments="{
item: item,
product: product,
referringPageUrl: referringPageUrl
}"/>
</f:for>
</f:then>
<f:else>
<div class="alert alert-info">
Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei,
eine andere Reise zu finden. Ruf uns an unter <a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}" title="Service Telefon anrufen" rel="nofollow">{settings.phoneNumber}</a>
oder schreibe an <f:link.email email="[email protected]"/>.
</div>
</f:else>
</f:if>
</div>
</div>
</div> </div>
</div> </article>
<div class="row"> </hotel-list>
<div class="col-md-12">
<div class="ep-headbar">Wähle deine Unterkunft</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-xs-12">
<f:if condition="{hotels}">
<f:then>
<f:for each="{hotels}" as="item">
<f:render partial="Hotel/TeaserWide" section="Teaser" arguments="{
item: item,
product: product,
referringPageUid: referringPageUid
}"/>
</f:for>
</f:then>
<f:else>
<div class="alert alert-info">
Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei,
eine andere Reise zu finden. Ruf uns an unter <a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}" title="Service Telefon anrufen" rel="nofollow">{settings.phoneNumber}</a>
oder schreibe an <f:link.email email="[email protected]"/>.
</div>
</f:else>
</f:if>
</div>
</div>
</div>
</f:section> </f:section>
</html> </html>
@@ -10,8 +10,7 @@
<search-result <search-result
results-uri="{ep:uri.ajax(action: 'searchresult', controller: 'AjaxSearch', pageUid: settings.defaultAjaxUid, format: 'json')}" results-uri="{ep:uri.ajax(action: 'searchresult', controller: 'AjaxSearch', pageUid: settings.defaultAjaxUid, format: 'json')}"
:initial-filter-settings='<v:format.json.encode>{filterSettings}</v:format.json.encode>' :initial-filter-settings='<v:format.json.encode>{filterSettings}</v:format.json.encode>'
:initial-filter-options='<v:format.json.encode>{filterOptions}</v:format.json.encode>' referring-page-url="{f:uri.typolink(parameter: referringPageUid)}"
:referring-page-uid="{referringPageUid}"
argument-prefix="{ep:argumentPrefix(pluginName: 'ajax')}" argument-prefix="{ep:argumentPrefix(pluginName: 'ajax')}"
:date-presets='{settings.datePickerPresets -> v:format.json.encode()}' :date-presets='{settings.datePickerPresets -> v:format.json.encode()}'
:watchlist="watchlist" :watchlist="watchlist"
@@ -106,7 +105,8 @@
<div class="panel-heading" role="tab" id="headingPeriod"> <div class="panel-heading" role="tab" id="headingPeriod">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapsePeriod" <a role="button" data-toggle="collapse" href="#collapsePeriod"
aria-expanded="true" aria-controls="collapsePeriod" class="collapsed"> aria-expanded="true" aria-controls="collapsePeriod"
:class="{ collapsed: !filterSettings.dateFrom && !filterSettings.dateTo }">
Reisezeitraum Reisezeitraum
</a> </a>
</h4> </h4>
@@ -150,12 +150,14 @@
<div class="panel-heading" role="tab" id="headingDuration"> <div class="panel-heading" role="tab" id="headingDuration">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapseDuration" <a role="button" data-toggle="collapse" href="#collapseDuration"
aria-expanded="true" aria-controls="collapseDuration" class="collapsed"> aria-expanded="true" aria-controls="collapseDuration"
:class="{ collapsed: filterSettings.priceRange === 0 }">
Preis Preis
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapseDuration" class="panel-collapse collapse" role="tabpanel" <div id="collapseDuration" class="panel-collapse collapse" role="tabpanel"
:class="{ in: filterSettings.priceRange > 0 }"
aria-labelledby="headingDuration"> aria-labelledby="headingDuration">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
@@ -172,12 +174,14 @@
<div class="panel-heading" role="tab" id="headingPax"> <div class="panel-heading" role="tab" id="headingPax">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapsePax" <a role="button" data-toggle="collapse" href="#collapsePax"
aria-expanded="true" aria-controls="collapsePax" class="collapsed"> aria-expanded="true" aria-controls="collapsePax"
:class="{ collapsed: filterSettings.pax === 1 }">
Personen Personen
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapsePax" class="panel-collapse collapse" role="tabpanel" <div id="collapsePax" class="panel-collapse collapse" role="tabpanel"
:class="{ in: filterSettings.pax > 1 }"
aria-labelledby="headingPax"> aria-labelledby="headingPax">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
@@ -194,12 +198,14 @@
<div class="panel-heading" role="tab" id="headingHotel"> <div class="panel-heading" role="tab" id="headingHotel">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapseHotel" <a role="button" data-toggle="collapse" href="#collapseHotel"
aria-expanded="true" aria-controls="collapseHotel" class="collapsed"> aria-expanded="true" aria-controls="collapseHotel"
:class="{ collapsed: !filterSettings.hotelTypes.length }">
Unterkunft Unterkunft
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapseHotel" class="panel-collapse collapse" role="tabpanel" <div id="collapseHotel" class="panel-collapse collapse" role="tabpanel"
:class="{ in: filterSettings.hotelTypes.length }"
aria-labelledby="headingHotel"> aria-labelledby="headingHotel">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
@@ -214,12 +220,14 @@
<div class="panel-heading" role="tab" id="headingBoard"> <div class="panel-heading" role="tab" id="headingBoard">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapseBoard" <a role="button" data-toggle="collapse" href="#collapseBoard"
aria-expanded="true" aria-controls="collapseBoard" class="collapsed"> aria-expanded="true" aria-controls="collapseBoard"
:class="{ collapsed: !filterSettings.boardTypes.length }">
Verpflegung Verpflegung
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapseBoard" class="panel-collapse collapse" role="tabpanel" <div id="collapseBoard" class="panel-collapse collapse" role="tabpanel"
:class="{ in: filterSettings.boardTypes.length }"
aria-labelledby="headingBoard"> aria-labelledby="headingBoard">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
@@ -234,12 +242,14 @@
<div class="panel-heading" role="tab" id="headingRoomtype"> <div class="panel-heading" role="tab" id="headingRoomtype">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapseRoomtype" <a role="button" data-toggle="collapse" href="#collapseRoomtype"
aria-expanded="true" aria-controls="collapseRoomtype" class="collapsed"> aria-expanded="true" aria-controls="collapseRoomtype"
:class="{ collapsed: !filterSettings.roomTypes.length }">
Zimmerarten / Apart. Zimmerarten / Apart.
</a> </a>
</h4> </h4>
</div> </div>
<div id="collapseRoomtype" class="panel-collapse collapse" role="tabpanel" <div id="collapseRoomtype" class="panel-collapse collapse" role="tabpanel"
:class="{ in: filterSettings.roomTypes.length }"
aria-labelledby="headingRoomtype"> aria-labelledby="headingRoomtype">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
@@ -254,7 +264,8 @@
<div class="panel-heading" role="tab" id="headingBus"> <div class="panel-heading" role="tab" id="headingBus">
<h4 class="panel-title"> <h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapseBus" <a role="button" data-toggle="collapse" href="#collapseBus"
aria-expanded="true" aria-controls="collapseBus" class="collapsed"> aria-expanded="true" aria-controls="collapseBus"
:class="{ collapsed: !filterSettings.bus }">
Anreise Anreise
</a> </a>
</h4> </h4>
@@ -292,6 +303,7 @@
:key="item.key" :key="item.key"
:item="item" :item="item"
:section="section" :section="section"
:referring-page-url="referringPageUrl"
:watchlist="watchlist"></search-result-item> :watchlist="watchlist"></search-result-item>
</div> </div>
</template> </template>
+6
View File
@@ -26,6 +26,9 @@ Encore
css: 'css/[name].css', css: 'css/[name].css',
images: 'images/[name].[ext]' images: 'images/[name].[ext]'
}) })
.configureBabel(function (babelConfig) {
babelConfig.plugins = [ 'transform-object-rest-spread' ]
})
; ;
const EpThemeConfig = Encore.getWebpackConfig(); const EpThemeConfig = Encore.getWebpackConfig();
@@ -50,6 +53,9 @@ Encore
css: 'css/[name].css', css: 'css/[name].css',
images: 'images/[name].[ext]' images: 'images/[name].[ext]'
}) })
.configureBabel(function (babelConfig) {
babelConfig.plugins = [ 'transform-object-rest-spread' ]
})
; ;
const EpEventsConfig = Encore.getWebpackConfig(); const EpEventsConfig = Encore.getWebpackConfig();