WIP Migrate searchbar
This commit is contained in:
@@ -25,6 +25,7 @@ import faq from './components/faq'
|
|||||||
import osmMap from './components/osm-map'
|
import osmMap from './components/osm-map'
|
||||||
import productDetail from './components/product-detail'
|
import productDetail from './components/product-detail'
|
||||||
import daytripDateSelect from './components/daytrip-date-select'
|
import daytripDateSelect from './components/daytrip-date-select'
|
||||||
|
import searchBar from './components/search-bar'
|
||||||
|
|
||||||
// Executed on document ready
|
// Executed on document ready
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
@@ -50,6 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
Alpine.data('osmMap', osmMap)
|
Alpine.data('osmMap', osmMap)
|
||||||
Alpine.data('productDetail', productDetail)
|
Alpine.data('productDetail', productDetail)
|
||||||
Alpine.data('daytripDateSelect', daytripDateSelect)
|
Alpine.data('daytripDateSelect', daytripDateSelect)
|
||||||
|
Alpine.data('searchBar', searchBar)
|
||||||
Alpine.start()
|
Alpine.start()
|
||||||
|
|
||||||
Glightbox({
|
Glightbox({
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import flatpickr from 'flatpickr'
|
||||||
|
import { German } from 'flatpickr/dist/l10n/de'
|
||||||
|
|
||||||
|
export default props => ({
|
||||||
|
picker: null,
|
||||||
|
optionsUri: props.optionsUri,
|
||||||
|
resetUri: props.resetUri,
|
||||||
|
searchParams: props.initialSearchParams,
|
||||||
|
filterOptions: props.initialFilterOptions,
|
||||||
|
datePresets: props.datePresets,
|
||||||
|
show: null,
|
||||||
|
destinationName: 'beliebig',
|
||||||
|
conceptSelected: false,
|
||||||
|
destinationSelected: false,
|
||||||
|
dateRangeSelected: { from: null, to: null },
|
||||||
|
dateRangeLabel: null,
|
||||||
|
init() {
|
||||||
|
this.$watch('dateRangeSelected', () => {
|
||||||
|
let dateRangeLabel = ''
|
||||||
|
if (null === this.dateRangeSelected.from && null === this.dateRangeSelected.to) {
|
||||||
|
this.searchParams.dateFrom = null
|
||||||
|
this.searchParams.dateTo = null
|
||||||
|
} else {
|
||||||
|
let dateFrom = flatpickr.formatDate(this.dateRangeSelected.from, 'd.m.Y')
|
||||||
|
let dateTo = flatpickr.formatDate(this.dateRangeSelected.to, 'd.m.Y')
|
||||||
|
dateRangeLabel = `${dateFrom} - ${dateTo}`
|
||||||
|
this.searchParams.dateFrom = dateFrom
|
||||||
|
this.searchParams.dateTo = dateTo
|
||||||
|
}
|
||||||
|
this.dateRangeLabel = dateRangeLabel
|
||||||
|
this.loadOptions()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadOptions() {
|
||||||
|
const data = { 'tx_epproducts_ajax[searchParams]': this.searchParams }
|
||||||
|
axios({
|
||||||
|
url: this.optionsUri,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
}).then(response => {
|
||||||
|
this.applyOptions(response.data)
|
||||||
|
this.show = null
|
||||||
|
})
|
||||||
|
},
|
||||||
|
applyOptions(json) {
|
||||||
|
this.filterOptions.totalResults = json.totalResults
|
||||||
|
const filterOptions = json.filterOptions
|
||||||
|
this.filterOptions.priceRanges = filterOptions.priceRanges
|
||||||
|
this.filterOptions.pax = filterOptions.pax
|
||||||
|
this.filterOptions.destinations = filterOptions.destinations
|
||||||
|
if (filterOptions.dateFrom !== null) {
|
||||||
|
this.filterOptions.selectableRangeFrom = filterOptions.dateFrom
|
||||||
|
}
|
||||||
|
if (filterOptions.dateTo !== null) {
|
||||||
|
this.filterOptions.selectableRangeTo = filterOptions.dateTo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateSearchParam(param, value) {
|
||||||
|
this.searchParams[param] = value
|
||||||
|
this.loadOptions()
|
||||||
|
},
|
||||||
|
updateDestination(destination) {
|
||||||
|
if (destination.type === 'concept') {
|
||||||
|
this.searchParams.conceptUid = destination.uid
|
||||||
|
} else {
|
||||||
|
this.searchParams.destinationUid = destination.uid
|
||||||
|
this.searchParams.destinationType = destination.type
|
||||||
|
}
|
||||||
|
this.loadOptions()
|
||||||
|
},
|
||||||
|
resetDestination() {
|
||||||
|
axios({
|
||||||
|
url: this.resetUri,
|
||||||
|
method: 'post'
|
||||||
|
}).then(response => {
|
||||||
|
this.applyOptions(response.data)
|
||||||
|
this.searchParams = response.data.searchParams
|
||||||
|
this.show = null
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectDateRangePreset(preset) {
|
||||||
|
let dateFrom = new Date(preset.dateFrom)
|
||||||
|
let dateTo = new Date(preset.dateTo)
|
||||||
|
this.dateRangeSelected = { from: dateFrom, to: dateTo }
|
||||||
|
this.picker.setDate([ dateFrom, dateTo ])
|
||||||
|
},
|
||||||
|
resetDateRange () {
|
||||||
|
this.dateRangeSelected = { from: null, to: null }
|
||||||
|
this.picker.clear()
|
||||||
|
this.picker.jumpToDate()
|
||||||
|
},
|
||||||
|
initDatePicker() {
|
||||||
|
this.picker = flatpickr(this.$refs.picker, {
|
||||||
|
mode: 'range',
|
||||||
|
dateFormat: 'd.m.Y',
|
||||||
|
locale: German,
|
||||||
|
inline: true,
|
||||||
|
onChange: selectedDates => {
|
||||||
|
if (selectedDates.length === 2) {
|
||||||
|
this.dateRangeSelected = { from: selectedDates[0], to: selectedDates[1] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (this.filterOptions.minDate && this.filterOptions.maxDate) {
|
||||||
|
this.picker.set('minDate', new Date(this.filterOptions.minDate))
|
||||||
|
this.picker.set('maxDate', new Date(this.filterOptions.maxDate))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
priceRangeLabel() {
|
||||||
|
if (this.searchParams.priceRange === 0) {
|
||||||
|
return 'beliebig'
|
||||||
|
}
|
||||||
|
let range = this.filterOptions.priceRanges[this.searchParams.priceRange]
|
||||||
|
return this.formatPriceRange(range)
|
||||||
|
},
|
||||||
|
formatPriceRange(range) {
|
||||||
|
let min = range[0];
|
||||||
|
let max = range[1];
|
||||||
|
if (min === 1) {
|
||||||
|
return 'bis ' + max + ' €'
|
||||||
|
}
|
||||||
|
if (max === 999) {
|
||||||
|
return 'ab ' + min + ' €'
|
||||||
|
}
|
||||||
|
return min + ' - ' + max + ' €'
|
||||||
|
},
|
||||||
|
selectDestination(uid, name, type) {
|
||||||
|
this.destinationName = name
|
||||||
|
this.destinationSelected = true
|
||||||
|
this.conceptSelected = false
|
||||||
|
this.searchParams.destinationUid = uid
|
||||||
|
this.searchParams.destinationType = type
|
||||||
|
this.loadOptions()
|
||||||
|
},
|
||||||
|
selectConcept(uid, name) {
|
||||||
|
this.destinationName = name
|
||||||
|
this.destinationSelected = false
|
||||||
|
this.conceptSelected = true
|
||||||
|
this.searchParams.destinationUid = uid
|
||||||
|
this.searchParams.destinationType = 'concept'
|
||||||
|
this.loadOptions()
|
||||||
|
},
|
||||||
|
resetDestinationSelect() {
|
||||||
|
this.destinationName = 'beliebig'
|
||||||
|
this.destinationSelected = false
|
||||||
|
this.conceptSelected = false
|
||||||
|
this.resetDestination()
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
.flatpickr-calendar {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
.daterange {
|
.daterange {
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,75 +5,182 @@
|
|||||||
<f:layout name="Default"/>
|
<f:layout name="Default"/>
|
||||||
|
|
||||||
<f:section name="Main">
|
<f:section name="Main">
|
||||||
<div id="searchbar" v-cloak>
|
<div id="searchbar" x-data='searchBar({
|
||||||
<search-bar
|
optionsUri: "<ep:uri.ajax controller="AjaxSearchbar" action="options" pageUid="{settings.defaultAjaxUid}" />",
|
||||||
options-uri='<ep:uri.ajax controller="AjaxSearchbar" action="options" pageUid="{settings.defaultAjaxUid}" />'
|
resetUri: "<ep:uri.ajax controller="AjaxSearchbar" action="reset" pageUid="{settings.defaultAjaxUid}" />",
|
||||||
reset-uri='<ep:uri.ajax controller="AjaxSearchbar" action="reset" pageUid="{settings.defaultAjaxUid}" />'
|
initialSearchParams: {searchParams -> f:format.json() -> f:format.raw()},
|
||||||
argument-prefix='<ep:argumentPrefix pluginName="ajax" />'
|
initialFilterOptions: {filterOptions -> f:format.json() -> f:format.raw()},
|
||||||
:initial-search-params='{searchParams -> f:format.json() -> f:format.raw()}'
|
datePresets: {settings.datePickerPresets -> f:format.json() -> f:format.raw()}
|
||||||
:initial-filter-options='{filterOptions -> f:format.json() -> f:format.raw()}'
|
})'>
|
||||||
:date-presets='{settings.datePickerPresets -> f:format.json() -> f:format.raw()}'
|
<div class="container searchbar{f:if(condition: fixed, then: ' affix')}"{f:if(condition: fixed, else: ' x-data="sticky(650)" x-bind:class="sticky ? \'affix\': \'\'"')}>
|
||||||
inline-template>
|
<div class="container">
|
||||||
<div class="container searchbar{f:if(condition: fixed, then: ' affix')}"{f:if(condition: fixed, else: ' x-data="sticky(650)" x-bind:class="sticky ? \'affix\': \'\'"')}>
|
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
|
||||||
<div class="container">
|
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
|
||||||
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
|
pageUid="{settings.defaultSearchPageUid}" additionalAttributes="{ref: 'form'}">
|
||||||
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
|
<f:form.hidden property="destinationUid" additionalAttributes="{x-model: 'searchParams.destinationUid'}"/>
|
||||||
pageUid="{settings.defaultSearchPageUid}" additionalAttributes="{ref: 'form'}">
|
<f:form.hidden property="destinationType" additionalAttributes="{x-model: 'searchParams.destinationType'}"/>
|
||||||
<f:form.hidden property="destinationUid" additionalAttributes="{v-model: 'searchParams.destinationUid', number: 'number'}"/>
|
<f:form.hidden property="dateFrom" value="" additionalAttributes="{x-model: 'searchParams.dateFrom'}"/>
|
||||||
<f:form.hidden property="destinationType" additionalAttributes="{v-model: 'searchParams.destinationType'}"/>
|
<f:form.hidden property="dateTo" value="" additionalAttributes="{x-model: 'searchParams.dateTo'}"/>
|
||||||
<f:form.hidden property="dateFrom" value="" additionalAttributes="{v-model: 'searchParams.dateFrom'}"/>
|
<f:form.hidden property="nights" additionalAttributes="{x-model: 'searchParams.nights'}"/>
|
||||||
<f:form.hidden property="dateTo" value="" additionalAttributes="{v-model: 'searchParams.dateTo'}"/>
|
<f:form.hidden property="pax" additionalAttributes="{x-model: 'searchParams.pax'}"/>
|
||||||
<f:form.hidden property="nights" additionalAttributes="{v-model: 'searchParams.nights', number: 'number'}"/>
|
<f:form.hidden property="conceptUid" additionalAttributes="{x-model: 'searchParams.conceptUid'}"/>
|
||||||
<f:form.hidden property="pax" additionalAttributes="{v-model: 'searchParams.pax', number: 'number'}"/>
|
<f:form.hidden property="priceRange" additionalAttributes="{x-model: 'searchParams.priceRange'}"/>
|
||||||
<f:form.hidden property="conceptUid" additionalAttributes="{v-model: 'searchParams.conceptUid', number: 'number'}"/>
|
<f:form.hidden property="pageUid" additionalAttributes="{x-model: 'searchParams.pageUid'}"/>
|
||||||
<f:form.hidden property="priceRange" additionalAttributes="{v-model: 'searchParams.priceRange', number: 'number'}"/>
|
<div class="row">
|
||||||
<f:form.hidden property="pageUid" additionalAttributes="{v-model: 'searchParams.pageUid', number: 'number'}"/>
|
<div class="col-sm-3">
|
||||||
<div class="row">
|
<div x-init="initDatePicker()">
|
||||||
<div class="col-sm-3">
|
<label>
|
||||||
<daterange-select
|
Zeitraum
|
||||||
class="form-group"
|
</label>
|
||||||
@selected="updateDateRange"
|
<div class="relative">
|
||||||
:presets="datePresets"
|
<input type="text"
|
||||||
:min-date="filterOptions.dateFrom"
|
class="form-control cursor-pointer"
|
||||||
:max-date="filterOptions.dateTo"
|
placeholder="von... bis"
|
||||||
></daterange-select>
|
x-bind:value="dateRangeLabel"
|
||||||
</div>
|
x-on:click.prevent="show = show === 'dateRange' ? null : 'dateRange'"/>
|
||||||
<div class="col-sm-1">
|
<div class="absolute top-100 left-0 bg-white p-4 shadow-md" x-show="show === 'dateRange'" x-transition x-cloak>
|
||||||
<pax-select
|
<template x-if="datePresets">
|
||||||
@selected="updatePax"
|
<div class="flex items-center justify-between pb-2">
|
||||||
:pax-options="filterOptions.pax"
|
<template x-for="preset in datePresets">
|
||||||
:pax="searchParams.pax"
|
<a class="daterange__preset" href="#"
|
||||||
groups-uri="{f:uri.typolink(parameter: settings.groupsPageUid)}"
|
x-on:click.prevent="selectDateRangePreset(preset)">
|
||||||
></pax-select>
|
<i class="fa fa-calendar"></i> <span x-text="preset.label"></span>
|
||||||
</div>
|
</a>
|
||||||
<div class="col-sm-4">
|
</template>
|
||||||
<destination-select
|
</div>
|
||||||
class="form-group"
|
</template>
|
||||||
@selected="updateDestination"
|
<input x-ref="picker" type="hidden">
|
||||||
@reset="resetDestination"
|
<div class="flex items-center justify-between pt-2">
|
||||||
:destination-options="filterOptions.destinations"
|
<button class="daterange__button focus:outline-none" x-on:click.prevent="show = null">
|
||||||
:concept-options="filterOptions.concepts"
|
<i class="fa fa-times"></i> schließen
|
||||||
></destination-select>
|
</button>
|
||||||
</div>
|
<button class="daterange__button focus:outline-none" x-on:click.prevent="resetDateRange()">
|
||||||
<div class="col-sm-2">
|
<i class="fa fa-trash-o"></i> zurücksetzen
|
||||||
<price-range-select
|
</button>
|
||||||
@selected="updatePriceRange"
|
</div>
|
||||||
:range-options="filterOptions.priceRanges"
|
</div>
|
||||||
:range="searchParams.priceRange"
|
|
||||||
></price-range-select>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-2">
|
|
||||||
<div class="form-group">
|
|
||||||
<label> </label>
|
|
||||||
<button type="submit" class="btn btn-warning">Suchen <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span></button>
|
|
||||||
<button type="submit" class="searchbar__link">Detailsuche</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</f:form>
|
<div class="col-sm-1">
|
||||||
</div>
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
Personen
|
||||||
|
</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
class="form-control cursor-pointer"
|
||||||
|
x-bind:value="searchParams.pax"
|
||||||
|
x-on:click.prevent="show = show === 'pax' ? null : 'pax'"/>
|
||||||
|
<div class="absolute top-100 left-0 bg-white p-4 shadow-md w-full h-48 overflow-y-scroll" x-show="show === 'pax'" x-transition 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>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="destinationselect">
|
||||||
|
<label class="destinationselect__label">
|
||||||
|
Reiseziel
|
||||||
|
</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
class="form-control cursor-pointer"
|
||||||
|
x-bind:value="destinationName"
|
||||||
|
x-on:click.prevent="show = show === 'destination' ? null : 'destination'"/>
|
||||||
|
<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-transition 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">
|
||||||
|
<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">
|
||||||
|
<template x-for="concept in filterOptions.concepts">
|
||||||
|
<li>
|
||||||
|
<a href="#" x-on:click.prevent="selectConcept(concept.uid, concept.name)" x-text="concept.name"></a>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template x-if="destinationSelected || conceptSelected">
|
||||||
|
<button x-on:click.prevent="resetDestinationSelect()">
|
||||||
|
<i class="fa fa-trash-o"></i>
|
||||||
|
zurücksetzen
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
Preis
|
||||||
|
</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
class="form-control cursor-pointer"
|
||||||
|
x-bind:value="priceRangeLabel()"
|
||||||
|
x-on:click.prevent="show = show === 'priceRange' ? null : 'priceRange'"/>
|
||||||
|
<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="resetPriceRange()">beliebig</a>
|
||||||
|
</li>
|
||||||
|
<template x-for="(range, index) in filterOptions.priceRanges">
|
||||||
|
<li>
|
||||||
|
<a href="#"
|
||||||
|
x-on:click.prevent="updateSearchParam('priceRange', index)"
|
||||||
|
x-text="formatPriceRange(range)"></a>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label> </label>
|
||||||
|
<button type="submit" class="btn btn-warning">Suchen <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span></button>
|
||||||
|
<button type="submit" class="searchbar__link">Detailsuche</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f:form>
|
||||||
</div>
|
</div>
|
||||||
</search-bar>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</f:section>
|
</f:section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user