WIP Migrate mobile search

This commit is contained in:
Björn Fromme
2021-10-22 18:35:02 +02:00
parent c75c86c3ca
commit a5b88ebf47
20 changed files with 654 additions and 506 deletions
@@ -0,0 +1,100 @@
<?php
namespace EP\EpProducts\Controller;
/***************************************************************
*
* Copyright notice
*
* (c) 2021 Björn Fromme <[email protected]>, dreipunktnull
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use EP\EpProducts\Service\FilterService;
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class AjaxFilterPanelController extends ActionController
{
use RequestArgumentTypeConversionTrait;
/**
* @var \EP\EpProducts\Service\FilterService
*/
protected $filterService;
/**
* @param FilterService $filterService
*/
public function __construct(FilterService $filterService)
{
$this->filterService = $filterService;
}
public function initializeAction()
{
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$this->processFilterSettingsArgument($forcedConceptUids);
}
/**
* @param array $filterSettings
*/
public function optionsAction(array $filterSettings)
{
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
$filterOptions = $this->filterService->getFilterOptions(
$filterSettings,
$excludedConceptUids,
$excludedRegionUids,
$forcedConceptUids
);
$this->view->assignMultiple([
'filterSettings' => $filterSettings,
'filterOptions' => $filterOptions,
]);
}
/**
* @param array $filterSettings
*/
public function searchAction(array $filterSettings)
{
$uri = $this->uriBuilder
->reset()
->setCreateAbsoluteUri(true)
->setTargetPageUid($this->settings['defaultSearchPageUid'])
->uriFor(
'searchresult',
['filterSettings' => $filterSettings],
'Search',
'epproducts',
'searchresult'
);
return json_encode($uri, JSON_THROW_ON_ERROR);
}
}
@@ -74,19 +74,6 @@ class SearchController extends ActionController
]);
}
public function searchboxAction()
{
$searchParams = $this->searchResultService->getNormalizedSearchParams();
$filterSettings = $this->filterService->getNormalizedFilterSettings();
$filterOptions = $this->filterService->getFilterOptions($filterSettings);
$this->view->assignMultiple([
'filterSettings' => $filterSettings,
'filterOptions' => $filterOptions,
'searchParams' => $searchParams,
]);
}
public function initializeProcessSearchAction()
{
$this->processSearchParamsArgument();
@@ -148,6 +148,10 @@ tx_epproducts_ajax_json {
1 = pricetable
2 = searchresult
}
AjaxFilterPanel {
1 = options
2 = search
}
AjaxDate {
1 = dates
}
@@ -295,6 +295,7 @@ $iconRegistry->registerIcon(
'ajax',
[
'AjaxSearch' => 'searchresult',
'AjaxFilterPanel' => 'options,search',
'AjaxDate' => 'dates',
'AjaxContingent' => 'list,rooms',
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable,daytripRooms',
@@ -304,6 +305,7 @@ $iconRegistry->registerIcon(
],
[
'AjaxSearch' => 'searchresult',
'AjaxFilterPanel' => 'options,search',
'AjaxDate' => 'dates',
'AjaxContingent' => 'list,rooms',
'AjaxTable' => 'datestable,pricetable,pricetableHtml,eventPricetable,daytripRooms',
@@ -1,18 +0,0 @@
lib.searchBox = USER
lib.searchBox {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = EpProducts
pluginName = searchbox
vendorName = EP
controller = Search
action = searchbox
switchableControllerActions {
Search {
1 = searchbox
}
}
settings =< plugin.tx_epproducts.settings
settings.searchPageUid < plugin.tx_epproducts.settings.defaultSearchPageUid
persistence =< plugin.tx_epproducts.persistence
view =< plugin.tx_epproducts.view
}
@@ -1,12 +0,0 @@
import { Controller } from 'stimulus'
export default class extends Controller {
static targets = [ 'form', 'container' ]
static values = { loading: Boolean }
submit(e) {
}
}
@@ -0,0 +1,89 @@
import { Controller } from 'stimulus'
import { useFetch} from '../mixins/use_fetch'
export default class extends Controller {
static targets = [
'form',
'container',
'loadingIndicator',
'dateFrom',
'dateTo',
]
static values = {
optionsUri: String,
searchUri: String,
loading: Boolean,
show: Boolean,
}
initialize() {
useFetch(this)
}
open(e) {
if (e.detail === 'mobile-search') {
this.showValue = true
this.load()
}
}
close() {
this.showValue = false
this.containerTarget.innerHTML = ''
}
filter() {
this.load({
method: 'POST',
body: new FormData(this.formTarget)
})
}
reset() {
this.load({ method: 'POST' })
}
load(options) {
this.loadingValue = true
fetch(this.optionsUriValue, options)
.then(this.checkStatus)
.then(this.parseHTML)
.then(html => {
this.containerTarget.innerHTML = html
})
.finally(() => {
this.loadingValue = false
})
}
search() {
const options = {
method: 'POST',
body: new FormData(this.formTarget)
}
this.loadingValue = true
fetch(this.searchUriValue, options)
.then(this.checkStatus)
.then(this.parseJSON)
.then(uri => {
window.location = uri
})
}
selectDateRange(e) {
this.dateFromTarget.value = e.detail.from
this.dateToTarget.value = e.detail.to
this.filter()
}
showValueChanged() {
this.element.classList.toggle('hidden', false === this.showValue)
}
loadingValueChanged() {
this.containerTarget.classList.toggle('opacity-25', this.loadingValue === true)
this.loadingIndicatorTarget.classList.toggle('hidden', this.loadingValue === false)
}
}
@@ -49,7 +49,7 @@ export default class extends Controller {
this.busIconTargets.forEach(icon => {
let busPrice = parseInt(icon.dataset.busPrice)
let busIncluded = !!icon.dataset.busIncluded
icon.classList.toggle('hidden', this.hideBusIcon(busPrice, busIncluded))
icon.classList.toggle('hidden', this.isHideBusIcon(busPrice, busIncluded))
})
if (this.hasSurchargeToggleLabelTarget) {
let labelDefault = this.surchargeToggleLabelTarget.dataset.labelDefault
@@ -86,7 +86,7 @@ export default class extends Controller {
return minPrice
}
hideBusIcon(busPrice, busIncluded) {
isHideBusIcon(busPrice, busIncluded) {
return (
busPrice && this.modeValue !== 'surcharge' ||
busIncluded && this.modeValue === 'discount'
@@ -16,7 +16,7 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:cObject typoscriptObjectPath="lib.searchBox"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
<f:render partial="Icons" arguments="{_all}"/>
</html>
@@ -18,6 +18,7 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
<f:render partial="Icons" arguments="{_all}"/>
</html>
@@ -26,6 +26,7 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
<f:render partial="Icons" arguments="{_all}"/>
</html>
@@ -26,6 +26,7 @@
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
<f:render partial="Config" arguments="{_all}" />
<f:render partial="Icons" arguments="{_all}"/>
</html>
@@ -16,19 +16,15 @@
<f:render section="FirstLevel" arguments="{_all}"/>
</div>
<div class="flex lg:hidden items-center space-x-4">
<f:if condition="{data.tx_eptheme_hide_searchbar}">
<f:else>
<button type="button"
class="focus:outline-none text-gray-800"
data-controller="overlay-toggle"
data-overlay-toggle-id-value="search"
data-action="overlay-toggle#invokeToggle">
<svg class="w-8 h-8 text-gray-800">
<use href="#icon-search"></use>
</svg>
</button>
</f:else>
</f:if>
<button type="button"
class="focus:outline-none text-gray-800"
data-controller="overlay-toggle"
data-overlay-toggle-id-value="mobile-search"
data-action="overlay-toggle#invokeToggle">
<svg class="w-8 h-8 text-gray-800">
<use href="#icon-search"></use>
</svg>
</button>
<button type="button"
class="focus:outline-none text-gray-800"
data-controller="overlay-toggle"
@@ -3,118 +3,201 @@
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:form object="{filterSettings}"
name="filterSettings"
pluginName="Ajax"
data="{'search-target': 'form'}">
<div class="mb-4 p-4 bg-ep-primary text-white"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.dateFrom} || {filterSettings.dateTo}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Reisezeitraum
</span>
<svg class="w-6 h-6 text-white transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<f:form.hidden property="dateFrom"
data="{'search-target': 'dateFrom', 'mobile-search-target': 'dateFrom'}"/>
<f:form.hidden property="dateTo"
data="{'search-target': 'dateTo', 'mobile-search-target': 'dateTo'}"/>
<div class="mb-4 p-4 bg-ep-primary text-white"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.dateFrom} || {filterSettings.dateTo}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Reisezeitraum
</span>
<svg class="w-6 h-6 text-white transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<f:form.hidden property="dateFrom"
data="{'search-target': 'dateFrom'}"/>
<f:form.hidden property="dateTo"
data="{'search-target': 'dateTo'}"/>
<div data-controller="collapse datepicker"
data-datepicker-date-from-value="{filterSettings.dateFrom}"
data-datepicker-date-to-value="{filterSettings.dateTo}"
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">
<input type="text"
data-action="click->collapse#toggle"
class="w-full px-0 bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase cursor-pointer placeholder-white"
placeholder="Zeitraum"
value="{ep:dateRange(dateFrom: filterSettings.dateFrom, dateTo: filterSettings.dateTo, includeLabel: 0)}"
data-searchbar-target="dateRangeLabel"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mt-2 cursor-pointer"
data-action="click->collapse#toggle">
<use href="#icon-calendar"></use>
</svg>
<div class="hidden absolute z-20 top-100 mt-2 left-0 min-w-full bg-white p-4 shadow-md rounded"
data-collapse-target="container">
<div class="flex justify-between divide-x divide-gray-200">
<div class="pr-2">
<input type="hidden"
data-datepicker-target="field">
</div>
<div class="flex-1 flex flex-col justify-between pl-2">
<f:if condition="{settings.datePickerPresets}">
<div class="flex flex-col space-y-4">
<f:for each="{settings.datePickerPresets}" as="preset">
<button type="button"
class="button button--small button--light w-full"
data-preset='{preset -> f:format.json() -> f:format.raw()}'
data-action="datepicker#selectPreset">
<span>
{preset.label}
</span>
</button>
</f:for>
</div>
</f:if>
<div data-controller="collapse datepicker"
data-datepicker-date-from-value="{filterSettings.dateFrom}"
data-datepicker-date-to-value="{filterSettings.dateTo}"
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">
<input type="text"
data-action="click->collapse#toggle"
class="w-full px-0 bg-transparent border-0 ring-0 focus:ring-0 text-white uppercase cursor-pointer placeholder-white"
placeholder="Zeitraum"
value="{ep:dateRange(dateFrom: filterSettings.dateFrom, dateTo: filterSettings.dateTo, includeLabel: 0)}"
data-searchbar-target="dateRangeLabel"/>
<svg class="w-6 h-6 text-white absolute right-0 top-0 mt-2 cursor-pointer"
data-action="click->collapse#toggle">
<use href="#icon-calendar"></use>
</svg>
<div class="hidden absolute z-20 top-100 mt-2 left-0 min-w-full bg-white p-4 shadow-md rounded"
data-collapse-target="container">
<div class="flex justify-between divide-x divide-gray-200">
<div class="pr-2">
<input type="hidden"
data-datepicker-target="field">
</div>
<div class="flex-1 flex flex-col justify-between pl-2">
<f:if condition="{settings.datePickerPresets}">
<div class="flex flex-col space-y-4">
<button type="button"
class="button button--small button--light w-full"
data-action="collapse#close">
schließen
</button>
<button type="button"
class="button button--small button--light w-full"
data-action="datepicker#reset">
zurücksetzen
</button>
<f:for each="{settings.datePickerPresets}" as="preset">
<button type="button"
class="button button--small button--light w-full"
data-preset='{preset -> f:format.json() -> f:format.raw()}'
data-action="datepicker#selectPreset">
<span>
{preset.label}
</span>
</button>
</f:for>
</div>
</f:if>
<div class="flex flex-col space-y-4">
<button type="button"
class="button button--small button--light w-full"
data-action="collapse#close">
schließen
</button>
<button type="button"
class="button button--small button--light w-full"
data-action="datepicker#reset">
zurücksetzen
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mb-4 p-4 bg-ep-primary text-white"
</div>
</div>
<div class="mb-4 p-4 bg-ep-primary text-white"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.conceptUids}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Reisekonzepte
</span>
<svg class="w-6 h-6 text-white transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container"
class="pt-2">
<ul class="mb-0 p-0 space-y-2">
<f:for each="{filterOptions.concepts}" as="concept">
<li>
<label>
<f:form.checkbox class="mr-2 rounded-full w-5 h-5 border-white ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="conceptUids"
data="{action: 'search#filter mobile-search#filter'}"
value="{concept.uid}"/>
<span class="inline-block py-1 px-2 bg-concept-{concept.code} text-white">
{concept.name}
</span>
</label>
</li>
</f:for>
</ul>
</div>
</div>
<div class="px-4 divide-y">
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.conceptUids}', then: 'false', else: 'true')}">
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.countryUids} || {filterSettings.regionUids}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Reisekonzepte
Reiseziel
</span>
<svg class="w-6 h-6 text-white transform"
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container"
class="pt-2">
<ul class="mb-0 p-0 space-y-2">
<f:for each="{filterOptions.concepts}" as="concept">
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2">
<f:for each="{filterOptions.destinations}" as="destination">
<li class="relative border-b last:border-0 mb-2">
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="countryUids"
data="{action: 'search#filter mobile-search#filter'}"
value="{destination.country.uid}"/>
<span class="pl-2 leading-none">
<strong>{destination.country.label}</strong>
</span>
<svg class="absolute top-0 right-0 w-8 h-6">
<use href="#icon-flag-{destination.country.code -> f:format.case(mode: 'lower')}"/>
</svg>
</label>
<f:if condition="{destination.regions}">
<ul class="py-2 space-y-1">
<f:for each="{destination.regions}" as="region">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="regionUids"
data="{action: 'search#filter mobile-search#filter'}"
value="{region.uid}"/>
<span class="leading-none pl-2">
{region.label}
</span>
</label>
</li>
</f:for>
</ul>
</f:if>
</li>
</f:for>
</ul>
</div>
</div>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.priceRanges}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Preis
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.priceRanges}" as="range">
<li>
<label>
<f:form.checkbox class="mr-2 rounded-full w-5 h-5 border-white ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="conceptUids"
data="{action: 'search#filter'}"
value="{concept.uid}"/>
<span class="inline-block py-1 px-2 bg-concept-{concept.code} text-white">
{concept.name}
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="priceRanges"
data="{action: 'search#filter mobile-search#filter'}"
value="{range.uid}"/>
<span class="leading-none pl-2">
{ep:priceRange(min: range.min, max: range.max)}
</span>
</label>
</li>
@@ -123,267 +206,177 @@
</div>
</div>
<div class="px-4 divide-y">
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.countryUids} || {filterSettings.regionUids}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Reiseziel
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2">
<f:for each="{filterOptions.destinations}" as="destination">
<li class="relative border-b last:border-0 mb-2">
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="countryUids"
data="{action: 'search#filter'}"
value="{destination.country.uid}"/>
<span class="pl-2 leading-none">
<strong>{destination.country.label}</strong>
</span>
<svg class="absolute top-0 right-0 w-8 h-6">
<use href="#icon-flag-{destination.country.code -> f:format.case(mode: 'lower')}"/>
</svg>
</label>
<f:if condition="{destination.regions}">
<ul class="py-2 space-y-1">
<f:for each="{destination.regions}" as="region">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="regionUids"
data="{action: 'search#filter'}"
value="{region.uid}"/>
<span class="leading-none pl-2">
{region.label}
</span>
</label>
</li>
</f:for>
</ul>
</f:if>
</li>
</f:for>
</ul>
</div>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.pax} > 1', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Personen
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<f:form.select class="w-full border-0 focus:ring-0 bg-right mt-2 p-0"
property="pax"
options="{filterOptions.pax}"
data="{action: 'search#filter mobile-search#filter'}"/>
</div>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.priceRanges}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Preis
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.priceRanges}" as="range">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="priceRanges"
data="{action: 'search#filter'}"
value="{range.uid}"/>
<span class="leading-none pl-2">
{ep:priceRange(min: range.min, max: range.max)}
</span>
</label>
</li>
</f:for>
</ul>
</div>
</div>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.pax} > 1', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Personen
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<f:form.select class="w-full border-0 focus:ring-0 bg-right mt-2 p-0"
property="pax"
options="{filterOptions.pax}"
data="{action: 'search#filter'}"/>
</div>
</div>
<f:variable name="hotelTypes" value="{
1: 'Ferienwohnung',
2: 'Sportclub',
3: 'Appartment',
4: 'Gästehaus',
5: 'Pension'
}"/>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.hotelTypes}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Unterkunft
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.hotelTypes}" as="type">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="hotelTypes"
data="{action: 'search#filter'}"
value="{type}"/>
<span class="leading-none pl-2">
{hotelTypes.{type}}
</span>
</label>
</li>
</f:for>
</ul>
</div>
</div>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.boardTypes}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Verpflegung
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.boardTypes}" as="type">
<f:if condition="{type.label}">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="boardTypes"
data="{action: 'search#filter'}"
value="{type.uid}"/>
<span class="pl-2 leading-none">
{type.label}
</span>
</label>
</li>
</f:if>
</f:for>
</ul>
</div>
</div>
<f:variable name="roomTypes" value="{
1: 'EZ',
2: 'DZ / 2er',
3: '3er',
4: '4er',
5: 'ab 5 Pers.',
6: 'mit anderen'
}"/>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.roomTypes}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Zimmerarten/Apart.
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.roomTypes}" as="type">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="roomTypes"
data="{action: 'search#filter'}"
value="{type}"/>
<span class="leading-none pl-2">
{roomTypes.{type}}
</span>
</label>
</li>
</f:for>
</ul>
</div>
</div>
<f:if condition="{filterOptions.busAvailable}">
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: filterSettings.bus, then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Anreise
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="bus"
data="{action: 'search#filter'}"
value="1"/>
<span class="leading-none pl-2">
Busanreise
</span>
</label>
</li>
</ul>
</div>
</div>
</f:if>
</div>
</f:form>
<f:variable name="hotelTypes" value="{
1: 'Ferienwohnung',
2: 'Sportclub',
3: 'Appartment',
4: 'Gästehaus',
5: 'Pension'
}"/>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.hotelTypes}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Unterkunft
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.hotelTypes}" as="type">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="hotelTypes"
data="{action: 'search#filter mobile-search#filter'}"
value="{type}"/>
<span class="leading-none pl-2">
{hotelTypes.{type}}
</span>
</label>
</li>
</f:for>
</ul>
</div>
</div>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.boardTypes}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Verpflegung
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.boardTypes}" as="type">
<f:if condition="{type.label}">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="boardTypes"
data="{action: 'search#filter mobile-search#filter'}"
value="{type.uid}"/>
<span class="pl-2 leading-none">
{type.label}
</span>
</label>
</li>
</f:if>
</f:for>
</ul>
</div>
</div>
<f:variable name="roomTypes" value="{
1: 'EZ',
2: 'DZ / 2er',
3: '3er',
4: '4er',
5: 'ab 5 Pers.',
6: 'mit anderen'
}"/>
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.roomTypes}', then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Zimmerarten/Apart.
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 p-0 pt-2 space-y-1">
<f:for each="{filterOptions.roomTypes}" as="type">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="roomTypes"
data="{action: 'search#filter mobile-search#filter'}"
value="{type}"/>
<span class="leading-none pl-2">
{roomTypes.{type}}
</span>
</label>
</li>
</f:for>
</ul>
</div>
</div>
<f:if condition="{filterOptions.busAvailable}">
<div class="py-2"
data-controller="collapse"
data-collapse-collapsed-value="{f:if(condition: filterSettings.bus, then: 'false', else: 'true')}">
<button type="button"
data-action="click->collapse#toggle"
class="flex items-center justify-between w-full">
<span class="uppercase font-bold">
Anreise
</span>
<svg class="w-6 h-6 transform"
data-collapse-target="icon">
<use href="#icon-arrow-right"></use>
</svg>
</button>
<div data-collapse-target="container">
<ul class="mb-0 p-0 pt-2 space-y-1">
<li>
<label class="flex items-start">
<f:form.checkbox class="w-5 h-5 border-ep-primary ring-0 bg-transparent focus:ring-0 focus:border-none text-ep-primary"
property="bus"
data="{action: 'search#filter mobile-search#filter'}"
value="1"/>
<span class="leading-none pl-2">
Busanreise
</span>
</label>
</li>
</ul>
</div>
</div>
</f:if>
</div>
</html>
@@ -3,15 +3,17 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-1 relative"
data-controller="collapse"
data-collapse-until-value="1024">
<div class="lg:block bg-white p-4"
data-collapse-target="container">
<div class="col-span-3 lg:col-span-1 relative">
<div class="hidden lg:block bg-white p-4">
<h3 class="headline headline--underlined mb-4">
Filter
</h3>
<f:render partial="Search/FilterPanel" arguments="{_all}"/>
<f:form object="{filterSettings}"
name="filterSettings"
pluginName="Ajax"
data="{'search-target': 'form'}">
<f:render partial="Search/FilterPanel" arguments="{_all}"/>
</f:form>
<div class="mt-4">
<button type="button"
class="button button--secondary w-full"
@@ -20,6 +22,38 @@
</button>
</div>
</div>
<div class="fixed lg:hidden top-0 left-0 inset-0 w-screen h-screen pt-14 pb-10 z-50 bg-gray-700 hidden"
data-controller="mobile-search"
data-mobile-search-options-uri-value="{ep:uri.ajax(controller: 'AjaxFilterPanel', action: 'options', pageUid: settings.defaultAjaxUid, arguments: '{filterSettings: filterSettings}', format: 'html')}"
data-action="overlay-toggle:toggle@window->mobile-search#open">
<div class="fixed top-0 left-0 z-100 inset-x-0 px-4 py-2 flex justify-between bg-white shadow-md">
<a href="{f:uri.typolink(parameter: settings.defaultHomeUid)}"
class="block lg:pb-4"
title="{settings.defaultTitle}">
<img class="block h-10 lg:h-12 w-auto" src="{f:uri.image(src: settings.logoImage)}" alt="{settings.defaultTitle}">
</a>
<button type="button"
class="focus:outline-none" data-action="mobile-search#close">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<div data-mobile-search-target="container"
class="h-full w-full overflow-y-scroll p-4 bg-white">
</div>
<div class="fixed top-0 left-0 z-20 w-full h-full hidden"
data-mobile-search-target="loadingIndicator">
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 flex items-center space-x-4 bg-ep-secondary text-white text-lg p-4 rounded">
<svg class="w-6 h-6">
<use href="#icon-spinner"></use>
</svg>
<span>Suche wird geladen...</span>
</div>
</div>
</div>
</div>
<div class="col-span-3 lg:col-span-2 relative">
<f:render partial="Search/Result" arguments="{_all}"/>
@@ -0,0 +1,37 @@
<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">
<div class="fixed lg:hidden top-0 left-0 inset-0 w-screen h-screen pt-14 pb-10 z-50 bg-gray-700 hidden"
data-controller="mobile-search"
data-mobile-search-options-uri-value="{ep:uri.ajax(controller: 'AjaxFilterPanel', action: 'options', pageUid: settings.defaultAjaxUid, format: 'html')}"
data-mobile-search-search-uri-value="{ep:uri.ajax(controller: 'AjaxFilterPanel', action: 'search', pageUid: settings.defaultAjaxUid, format: 'json')}"
data-action="overlay-toggle:toggle@window->mobile-search#open">
<div class="fixed top-0 left-0 z-100 inset-x-0 px-4 py-2 flex justify-between bg-white shadow-md">
<a href="{f:uri.typolink(parameter: settings.defaultHomeUid)}"
class="block lg:pb-4"
title="{settings.defaultTitle}">
<img class="block h-10 lg:h-12 w-auto" src="{f:uri.image(src: settings.logoImage)}" alt="{settings.defaultTitle}">
</a>
<button type="button"
class="focus:outline-none" data-action="mobile-search#close">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<div data-mobile-search-target="container"
class="h-full w-full overflow-y-scroll p-4 bg-white">
</div>
<div class="fixed top-0 left-0 z-20 w-full h-full hidden"
data-mobile-search-target="loadingIndicator">
<div class="absolute top-1/2 left-1/2 transform -translate-xy-1/2 flex items-center space-x-4 bg-ep-secondary text-white text-lg p-4 rounded">
<svg class="w-6 h-6">
<use href="#icon-spinner"></use>
</svg>
<span>Suche wird geladen...</span>
</div>
</div>
</div>
</html>
@@ -5,6 +5,28 @@
<f:layout name="Default"/>
<f:render partial="Search/FilterPanel" arguments="{_all}"/>
<f:section name="Main">
<f:form object="{filterSettings}"
name="filterSettings"
pluginName="Ajax"
action=""
data="{'mobile-search-target': 'form'}">
<f:render partial="Search/FilterPanel" arguments="{_all}"/>
</f:form>
<div class="mt-4">
<button type="button"
class="button button--primary w-full"
data-action="mobile-search#search">
Ergebnis anzeigen
</button>
</div>
<div class="mt-4">
<button type="button"
class="button button--secondary w-full"
data-action="mobile-search#reset">
Filter zurücksetzen
</button>
</div>
</f:section>
</html>
@@ -1,20 +0,0 @@
<div xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Form/Default"/>
<f:section name="Main">
<div class="relative form form--border"
data-controller="contactform">
<div data-contactform-target="container">
<f:render partial="ContactForm/Simple" arguments="{_all}"/>
</div>
<div class="hidden form__overlay"
data-contactform-target="loadingIndicator">
<img src="{f:uri.resource(path: 'images/ajax-loader-white.gif', extensionName: 'ep_theme')}" alt="Processing...">
</div>
</div>
</f:section>
</div>
@@ -1,69 +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 class="fixed lg:hidden top-0 left-0 inset-0 w-screen h-screen pt-14 pb-10 z-50 bg-gray-700 hidden"
data-controller="overlay-toggle"
data-overlay-toggle-id-value="search"
data-overlay-toggle-target="overlay"
data-action="overlay-toggle:toggle@window->overlay-toggle#toggle">
<div class="fixed top-0 left-0 z-100 inset-x-0 px-4 py-2 flex justify-between bg-white shadow-md">
<a href="{f:uri.typolink(parameter: settings.defaultHomeUid)}"
class="block lg:pb-4"
title="{settings.defaultTitle}">
<img class="block h-10 lg:h-12 w-auto" src="{f:uri.image(src: settings.logoImage)}" alt="{settings.defaultTitle}">
</a>
<button type="button"
class="focus:outline-none" data-action="overlay-toggle#invokeToggle">
<svg class="w-8 h-8">
<use href="#icon-close"></use>
</svg>
</button>
</div>
<div class="h-full w-full overflow-y-scroll p-4">
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
pageUid="{settings.defaultSearchPageUid}">
<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'}"/>
<label>Früheste Anreise</label>
<input
type="date"
class="border-gray-400 focus:border-gray-800">
<label>Späteste Abreise</label>
<input
type="date"
class="border-gray-400 focus:border-gray-800">
<label>Personen</label>
<input
type="number"
min="1"
max="20"
class="border-gray-400 focus:border-gray-800">
<div class="grid grid-cols-2 gap-4">
<button type="submit"
class="button">
Suchen
</button>
<button type="submit"
class="button button--secondary">
Detailsuche
</button>
</div>
</f:form>
</div>
</div>
</f:section>
</html>
@@ -3,7 +3,7 @@
<f:layout name="Default"/>
<f:section name="Main">
<div class="py-8 bg-ep-primary-bg">
<div class="pb-8 md:py-8 bg-ep-primary-bg">
<div class="container"
data-controller="search"
data-search-uri-value="{ep:uri.ajax(controller: 'AjaxSearch', action: 'searchresult', pageUid: settings.defaultAjaxUid, format: 'html')}"