WIP Migrate to stimulus.js and tailwind
This commit is contained in:
@@ -86,6 +86,15 @@ class AjaxSearchController extends ActionController
|
||||
->searchResultService
|
||||
->getResult($filterSettings, $excludedConceptUids);
|
||||
|
||||
// No results? First retry without date constraint if any
|
||||
if ((!empty($filterSettings['dateFrom']) || !empty($filterSettings['dateTo'])) && 0 === $searchResult->getTotal()) {
|
||||
$filterSettings['dateFrom'] = null;
|
||||
$filterSettings['dateTo'] = null;
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
$organic = false;
|
||||
}
|
||||
|
||||
// Still no results? Remove all constraints and search again with defaults
|
||||
if (0 === $searchResult->getTotal()) {
|
||||
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
|
||||
@@ -136,9 +136,20 @@ class SearchController extends ActionController
|
||||
$dateSelected = true;
|
||||
}
|
||||
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
$searchResult = $this
|
||||
->searchResultService
|
||||
->getResult($filterSettings, $excludedConceptUids);
|
||||
|
||||
if ($searchResult->getTotal() === 0) {
|
||||
// No results? First retry without date constraint if any
|
||||
if ((!empty($filterSettings['dateFrom']) || !empty($filterSettings['dateTo'])) && 0 === $searchResult->getTotal()) {
|
||||
$filterSettings['dateFrom'] = null;
|
||||
$filterSettings['dateTo'] = null;
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
$organic = false;
|
||||
}
|
||||
|
||||
// Still no results? Remove all constraints and search again with defaults
|
||||
if (0 === $searchResult->getTotal()) {
|
||||
$filterSettings = $this->filterService->getNormalizedFilterSettings([], $forcedConceptUids);
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
$organic = false;
|
||||
|
||||
+1
-3
@@ -33,7 +33,7 @@ use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
|
||||
class AjaxSearchbarController extends ActionController
|
||||
class SearchbarController extends ActionController
|
||||
{
|
||||
|
||||
use RequestArgumentTypeConversionTrait;
|
||||
@@ -66,7 +66,6 @@ class AjaxSearchbarController extends ActionController
|
||||
|
||||
/**
|
||||
* @param array $searchParams
|
||||
* @return string
|
||||
*/
|
||||
public function indexAction(array $searchParams)
|
||||
{
|
||||
@@ -83,7 +82,6 @@ class AjaxSearchbarController extends ActionController
|
||||
);
|
||||
|
||||
$this->view->assignMultiple([
|
||||
'filterSettings' => $filterSettings,
|
||||
'filterOptions' => $filterOptions,
|
||||
'searchParams' => $searchParams,
|
||||
'fixed' => $fixed,
|
||||
@@ -156,9 +156,6 @@ tx_epproducts_ajax_json {
|
||||
AjaxDate {
|
||||
1 = dates
|
||||
}
|
||||
AjaxSearchbar {
|
||||
1 = index
|
||||
}
|
||||
AjaxTable {
|
||||
1 = datestable
|
||||
2 = pricetable
|
||||
|
||||
@@ -25,12 +25,6 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\T
|
||||
'description' => 'Importiert Schneehöhen',
|
||||
];
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\EP\EpProducts\Task\GroupsSwissTask::class] = [
|
||||
'extension' => $_EXTKEY,
|
||||
'title' => 'EP Groups Swiss Export',
|
||||
'description' => 'Publiziert Verfügbarkeiten nach groups.swiss',
|
||||
];
|
||||
|
||||
$GLOBALS['TYPO3_CONF_VARS']['LOG']['EP']['EpProducts']['Service']['GroupsSwissService']['writerConfiguration'] = [
|
||||
\TYPO3\CMS\Core\Log\LogLevel::INFO => [
|
||||
'TYPO3\\CMS\\Core\\Log\\Writer\\FileWriter' => [
|
||||
@@ -50,10 +44,10 @@ $iconRegistry->registerIcon(
|
||||
'EP.' . $_EXTKEY,
|
||||
'searchbar',
|
||||
[
|
||||
'Search' => 'searchbar',
|
||||
'Searchbar' => 'index',
|
||||
],
|
||||
[
|
||||
'Search' => '',
|
||||
'Searchbar' => 'index',
|
||||
]
|
||||
);
|
||||
|
||||
@@ -371,7 +365,6 @@ $iconRegistry->registerIcon(
|
||||
[
|
||||
'AjaxSearch' => 'searchresult',
|
||||
'AjaxDate' => 'dates',
|
||||
'AjaxSearchbar' => 'index',
|
||||
'AjaxContingent' => 'list,rooms',
|
||||
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||
@@ -381,7 +374,6 @@ $iconRegistry->registerIcon(
|
||||
[
|
||||
'AjaxSearch' => 'searchresult',
|
||||
'AjaxDate' => 'dates',
|
||||
'AjaxSearchbar' => 'index',
|
||||
'AjaxContingent' => 'list,rooms',
|
||||
// 'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable',
|
||||
'AjaxCalendar' => 'range,contingents,availableRooms',
|
||||
|
||||
@@ -4,11 +4,11 @@ lib.searchBar {
|
||||
extensionName = EpProducts
|
||||
pluginName = searchbar
|
||||
vendorName = EP
|
||||
controller = Search
|
||||
action = searchbar
|
||||
controller = Searchbar
|
||||
action = index
|
||||
switchableControllerActions {
|
||||
Search {
|
||||
1 = searchbar
|
||||
Searchbar {
|
||||
1 = index
|
||||
}
|
||||
}
|
||||
settings =< plugin.tx_epproducts.settings
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useWindowResize, useDebounce } from 'stimulus-use'
|
||||
import { useWindowResize, useDebounce, useClickOutside } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
@@ -16,6 +16,7 @@ export default class extends Controller {
|
||||
initialize() {
|
||||
useWindowResize(this)
|
||||
useDebounce(this)
|
||||
useClickOutside(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
|
||||
+36
-14
@@ -5,7 +5,11 @@ import { German } from 'flatpickr/dist/l10n/de'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'field' ]
|
||||
static targets = [
|
||||
'field',
|
||||
'dateFrom',
|
||||
'dateTo',
|
||||
]
|
||||
|
||||
static values = {
|
||||
minDate: String,
|
||||
@@ -23,13 +27,9 @@ 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: dateFrom,
|
||||
to: dateTo,
|
||||
label: `${dateFrom} - ${dateTo}`
|
||||
})
|
||||
this.dateFromValue = flatpickr.formatDate(selectedDates[0], 'Y-m-d')
|
||||
this.dateToValue = flatpickr.formatDate(selectedDates[1], 'Y-m-d')
|
||||
this.update()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -46,20 +46,42 @@ export default class extends Controller {
|
||||
selectPreset(e) {
|
||||
e.preventDefault()
|
||||
const preset = JSON.parse(e.currentTarget.dataset.preset)
|
||||
this.dateFromValue = preset.dateFrom
|
||||
this.dateToValue = 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')}`
|
||||
})
|
||||
this.update()
|
||||
}
|
||||
|
||||
reset(e) {
|
||||
e.preventDefault()
|
||||
this.picker.clear()
|
||||
this.picker.jumpToDate()
|
||||
this.dispatch('range-select', { from: null, to: null, label: null })
|
||||
this.dateFromValue = ''
|
||||
this.dateToValue = ''
|
||||
this.update()
|
||||
}
|
||||
|
||||
update() {
|
||||
// Set values to form fields if applicable
|
||||
if (this.hasDateFromTarget) {
|
||||
this.dateFromTarget.value = this.dateFromValue
|
||||
}
|
||||
if (this.hasDateToTarget) {
|
||||
this.dateToTarget.value = this.dateToValue
|
||||
}
|
||||
// Dispatch event with selected values and formatted range label
|
||||
let label = ''
|
||||
if (this.dateFromValue && this.dateToValue) {
|
||||
let dateFrom = new Date(this.dateFromValue)
|
||||
let dateTo = new Date(this.dateToValue)
|
||||
label = `${flatpickr.formatDate(dateFrom, 'd.m.Y')}-${flatpickr.formatDate(dateTo, 'd.m.Y')}`
|
||||
}
|
||||
this.dispatch('range-select', {
|
||||
from: this.dateFromValue,
|
||||
to: this.dateToValue,
|
||||
label
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+41
-37
@@ -1,56 +1,60 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useFetch } from '../mixins/use_fetch'
|
||||
import { useDispatch } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [
|
||||
'container',
|
||||
'form',
|
||||
'destinationUid',
|
||||
'destinationType',
|
||||
'dateFrom',
|
||||
'dateTo',
|
||||
'nights',
|
||||
'destinationUid',
|
||||
'destinationType',
|
||||
'pax',
|
||||
'priceRange',
|
||||
'dateRangeLabel'
|
||||
'dateRangeLabel',
|
||||
'paxLabel',
|
||||
'destinationLabel',
|
||||
'priceRangeLabel',
|
||||
]
|
||||
|
||||
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
|
||||
})
|
||||
useDispatch(this)
|
||||
}
|
||||
|
||||
setDateRange(e) {
|
||||
this.dateRangeLabelTarget.value = e.detail.label
|
||||
this.dateFromTarget.value = e.detail.from
|
||||
this.dateToTarget.value = e.detail.to
|
||||
this.dateRangeLabelTarget.value = e.detail.label
|
||||
this.load()
|
||||
this.dispatch('input')
|
||||
}
|
||||
|
||||
setPax(e) {
|
||||
e.preventDefault()
|
||||
let pax = e.currentTarget.dataset.option
|
||||
this.paxTarget.value = pax
|
||||
this.paxLabelTarget.value = pax
|
||||
this.dispatch('input')
|
||||
}
|
||||
|
||||
setDestination(e) {
|
||||
e.preventDefault()
|
||||
this.destinationUidTarget.value = e.currentTarget.dataset.optionUid
|
||||
this.destinationTypeTarget.value = e.currentTarget.dataset.optionType
|
||||
this.destinationLabelTarget.value = e.currentTarget.dataset.optionLabel
|
||||
this.dispatch('input')
|
||||
}
|
||||
|
||||
resetDestination(e) {
|
||||
e.preventDefault()
|
||||
this.destinationUidTarget.value = ''
|
||||
this.destinationTypeTarget.value = ''
|
||||
this.destinationLabelTarget.value = ''
|
||||
this.dispatch('input')
|
||||
}
|
||||
|
||||
setPriceRange(e) {
|
||||
e.preventDefault()
|
||||
this.priceRangeTarget.value = e.currentTarget.dataset.optionValue
|
||||
this.priceRangeLabelTarget.value = e.currentTarget.dataset.labelValue
|
||||
this.dispatch('input')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
<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>
|
||||
@@ -1,11 +0,0 @@
|
||||
<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>
|
||||
@@ -1,15 +0,0 @@
|
||||
#<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
|
||||
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<div 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>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,239 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
|
||||
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<div data-controller="searchbar"
|
||||
{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="sticky"')}
|
||||
data-action="datepicker:range-select->searchbar#setDateRange"
|
||||
data-searchbar-uri-value="{ep:uri.ajax(controller: 'Searchbar', action: 'index', pageUid: settings.defaultAjaxUid, format: 'html')}">
|
||||
<div class="searchbar {f:if(condition: fixed, then: ' sticky')}"
|
||||
data-searchbar-target="container">
|
||||
<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="dateFrom" data="{'searchbar-target': 'dateFrom'}"/>
|
||||
<f:form.hidden property="dateTo" data="{'searchbar-target': 'dateTo'}"/>
|
||||
<f:form.hidden property="pax" data="{'searchbar-target': 'pax'}"/>
|
||||
<f:form.hidden property="destinationUid" data="{'searchbar-target': 'destinationUid'}"/>
|
||||
<f:form.hidden property="destinationType" data="{'searchbar-target': 'destinationType'}"/>
|
||||
<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 datepicker"
|
||||
<f:if condition="{searchParams.dateFrom} && {searchParams.dateTo}">
|
||||
data-datepicker-date-from-value="{searchParams.dateFrom}"
|
||||
data-datepicker-date-to-value="{searchParams.dateTo}"
|
||||
</f:if>
|
||||
data-datepicker-min-date-value="{f:format.date(date: 'now', format: 'Y-m-d')}"
|
||||
data-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
|
||||
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"
|
||||
<f:if condition="{searchParams.dateFrom} && {searchParams.dateTo}">
|
||||
value="{ep:dateRange(dateFrom: searchParams.dateFrom, dateTo: searchParams.dateTo, includeLabel: 0)}"
|
||||
</f:if>
|
||||
data-searchbar-target="dateRangeLabel"/>
|
||||
<svg class="w-6 h-6 text-white absolute right-0 top-0 mr-2 mt-1"
|
||||
data-action="click->collapse#toggle">
|
||||
<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">
|
||||
<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-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
data-searchbar-target="paxLabel"
|
||||
value="{searchParams.pax}"
|
||||
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>
|
||||
<button data-action="searchbar#setPax"
|
||||
data-option="{option}">
|
||||
{option}
|
||||
</button>
|
||||
</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-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-b border-r border-white mr-8">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
data-searchbar-target="destinationLabel"
|
||||
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">
|
||||
<button data-action="searchbar#setDestination"
|
||||
data-option-uid="{destination.country.uid}"
|
||||
data-option-type="country"
|
||||
data-option-label="{destination.country.label}">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<ul class="plain-list mb-0">
|
||||
<f:for each="{destination.regions}" as="region">
|
||||
<li>
|
||||
<button data-action="searchbar#setDestination"
|
||||
data-option-uid="{region.uid}"
|
||||
data-option-type="region"
|
||||
data-option-label="{region.label}">
|
||||
{region.label}
|
||||
</button>
|
||||
</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>
|
||||
<button data-action="searchbar#setDestination"
|
||||
data-option-uid="{concept.uid}"
|
||||
data-option-type="concept"
|
||||
data-option-label="{concept.name}">
|
||||
{concept.name}
|
||||
</button>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<button data-action="searchbar#resetDestination">
|
||||
zurücksetzen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-32"
|
||||
data-controller="collapse"
|
||||
data-action="collapse:click:outside->collapse#close searchbar:input@window->collapse#close"
|
||||
data-collapse-collapsed-value="true">
|
||||
<div class="relative border-r border-b border-white">
|
||||
<input type="text"
|
||||
data-action="click->collapse#toggle"
|
||||
data-searchbar-target="priceRangeLabel"
|
||||
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>
|
||||
<button data-action="searchbar#setPriceRange"
|
||||
data-option-value=""
|
||||
data-label-value="beliebig">
|
||||
beliebig
|
||||
</button>
|
||||
</li>
|
||||
<f:for each="{filterOptions.priceRanges}" as="range" iteration="iteration">
|
||||
<li>
|
||||
<button data-action="searchbar#setPriceRange"
|
||||
data-option-value="{range.uid}"
|
||||
data-label-value="{range.formatted}">
|
||||
{range.formatted}
|
||||
</button>
|
||||
</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>
|
||||
</f:form>
|
||||
</div>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user